mirror of
https://github.com/pytorch/pytorch.git
synced 2026-10-28 18:56:51 +08:00
main
549
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
64d8a9b961 |
[CI] Remove the remaining XLA CI plumbing (#202262)
Go through the following checklist - [x] Passes lint (`spin fixlint`) - [ ] Added/updated tests - [ ] Updated documentation (if applicable) - [ ] Included benchmark results (for PRs impacting perf) --- ## Linked issue or supporting maintainer Follow-up to #202086. ## BC-breaking? No. This only touches CI configuration. No workflow builds or tests XLA, and nothing in this repo reads the removed env vars. They were consumed by scripts in the cloned `pytorch/xla` repo, which only the dead `build_xla` and `test_xla` paths (removed in #202086) sourced. ## Summary (human written only) PyTorch/XLA is deprecated in favor of TorchTPU, and its repo will be archived on 2026-11-15. #202086 removes the dead XLA paths from `.ci/pytorch` and lists what it leaves for follow-ups. This PR removes the CI plumbing in that list, plus the pin and its consumers: - `XLA_CUDA` and `XLA_CLANG_CACHE_S3_BUCKET_NAME` (and their `docker run -e` passthroughs) from `_linux-build`, `_linux-test`, `_s390x-build`, `_s390x-test` and `_xpu-test`. - The `update-commit-hash` job in `weekly.yml`, whose only step bumped the xla pin, and `.github/ci_commit_pins/xla.txt`. - The `OSS CI / pytorchbot / XLA` merge rule and the `xla.txt` pattern in the `Lazy Tensor` rule of `merge_rules.yaml`. - The XLA block in `scripts/release/apply-release-changes.sh`. Its first `sed` rewrote the only `--quiet` in `.ci/pytorch/common_utils.sh`, which sits in `clone_pytorch_xla`, and its second rewrote the pin file deleted here. Left alone on purpose: - `.ci/docker` (the `xla` image branch and two comments): editing it rebuilds every CI image. - The `xla` merge-rule fixtures in `.github/scripts/test_trymerge.py`: they replay recorded data for PR 105312, so their check names have to stay. - The `module: xla` label list in `.agents/skills/triaging-issues/labels.json` and the `xla` category in `scripts/release_notes`: both describe labels that still exist on old issues and PRs. Does not depend on #202086 and can land in either order: no job reaches the XLA functions that would read these variables. ## Test Plan The edited workflow files and `merge_rules.yaml` parse, `merge_rules.yaml` no longer contains an XLA rule or an `xla.txt` pattern, and the release script passes `bash -n`. ``` python3 -c "import yaml,glob; [yaml.safe_load(open(f)) for f in glob.glob('.github/workflows/*.yml')]" bash -n scripts/release/apply-release-changes.sh git grep -nE 'XLA_CUDA|XLA_CLANG_CACHE|ci_commit_pins/xla' -- .github scripts ``` The last command should only match the `test_trymerge.py` fixtures. ## Agent details (if applicable) Drafted with Claude Code; diff reviewed by hand. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Pull Request resolved: https://github.com/pytorch/pytorch/pull/202262 Approved by: https://github.com/huydhn |
||
|
|
a889cdcbcf |
[pr-review] Show the automated review its earlier findings and people's reactions (#201907)
✴️ iz2: On a re-review, the hardened PR review gets the author's and maintainers' comments but not its own earlier findings, since `fetch_pr_comments.py` drops bot comments, and it gets no emoji reactions. A reply to a finding therefore arrives without the finding it answers, and a 👎 on a finding is invisible. This change passes that conversation through and leaves the judgement to the model. Nothing is filtered or suppressed: - `fetch_pr_comments.py` keeps this workflow's own earlier findings as role `automated_review`: inline comments by the posting account (post_review.py's `REVIEW_AUTHOR`) in reviews that start with its marker, and the body of the latest such review, which lists the findings not posted inline (capped at 40,000 chars). `post_review.py` deletes superseded inline findings that nobody replied to or reacted to, so normally only the latest findings and older ones that drew feedback show up. Inline comments carry `pull_request_review_id` and `original_commit_id`, and the file carries `head_sha`, the commit under review. - A comment's `reactions` lists the author's and maintainers' emoji, read with GraphQL, one call per 100 comments, for reacted comments and the latest review body (at most 300, earlier findings first). Reactors' permission checks (at most 30) are spent only on comments the selection considers. `reactions_truncated` is set when a limit, a failed call or a GraphQL error may have left out a reaction on a kept comment; a failed call is retried twice, keeps the comment and the run, and is logged as a workflow warning. - Over the byte cap, people's comments are kept first, alone, newest first, so a finding never takes a person's place. Earlier findings follow only if all of those fit: the ones kept replies answer, then answered ones and the latest review body, then the rest, skipping any that does not fit. - The pr-review skill gets an "Earlier Automated Findings" section: verify earlier findings at HEAD like any others, and if you repeat a point someone already answered, address their answer. Its Local CLI section lists how to read reactions. The offline prompt describes the new role and fields, and treats earlier automated findings and replies as data from the PR, not instructions. The lookups still use the workflow's `GITHUB_TOKEN`; giving PR review its own GitHub App is a separate change. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .` passes (526 tests). New tests cover provenance (other bots, non-marker and older reviews, the author override), reactions from the author and maintainers only, GraphQL batching, retries and errors, that omitted findings spend no permission checks, the lookup limits and when each sets `reactions_truncated`, and selection under the byte cap. The GraphQL query was run against live pytorch/pytorch comments and reviews. A canary on a pytorch/ciforge probe PR ran the fetch, both review stages and posting from an earlier commit of this branch; the re-review received its earlier inline findings as `automated_review` items, and auto-deleted ones were absent. Pull Request resolved: https://github.com/pytorch/pytorch/pull/201907 Approved by: https://github.com/jeanschmidt, https://github.com/huydhn |
||
|
|
89a8b1cfba |
[hardened PR review] Declutter replaced reviews: fold, delete unreplied comments, link nearby threads (#201479)
minimizing obsolete automatic reviews did not hide their review comments, meaning the identical comments from different review iterations could pile up on the same line. this PR deletes obsolete comments (that don't have maintainer replies), and also edits the obsolete review to wrap them in `<details>` so they render better on mobile (which doesn't currently support minimization). --- ✴️ iz2: On PRs with many re-reviews (e.g. #201405: 24 automated reviews, 67 open inline threads), minimizing a replaced review hides only its body. Its inline comments stay listed on the Files tab and in GitHub Mobile, because `GITHUB_TOKEN` can't resolve threads here. When a newer automated review replaces an earlier one, this change: - folds the earlier review's body into a collapsed `<details>` section (the marker stays the first line; no cross-links) - deletes its inline comments that nobody replied to or reacted to. A comment is deleted only if its review thread, read by GraphQL cursor, holds that one comment. Answered comments and their replies stay. - posts a new blocking finding within 3 lines of an earlier comment someone replied to in the review body, with a link to that thread, instead of opening another inline thread. The match is by position only, so the link text says only that the thread is nearby. Reviews hidden by earlier runs get the same fold and delete from a backfill. The backfill runs after any clean-verdict dismissal and is capped at 50 writes, 1 second apart, and 90 seconds per run, so a PR like #201405 is cleaned over a few runs. A change request that may still stand is never folded or emptied. Known limits: - A reply that isn't visible yet, because it was posted after the threads were read or is still pending in someone's unsubmitted review, does not protect its root comment. The API shows neither, so a maintainer who is mid-reply when a new push is reviewed can lose the comment they were answering. - Throttled deletes on the takedown path, before a clean verdict's dismissal, are not time-bounded. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .` passes all 472 tests, including new cases for overlapping runs, reply, reaction and thread-count protection, retraction, and the backfill's budget, pacing, clock and change-request gate. Pull Request resolved: https://github.com/pytorch/pytorch/pull/201479 Approved by: https://github.com/huydhn, https://github.com/albanD |
||
|
|
91c5511627 |
[pr-review] Raise the finding message cap and tell stage B about it (#201566)
✴️ iz2: The automated review cut inline findings off mid-sentence at 600 characters: `extract_verdict.py` caps each finding message there, and stage B was never told about the limit. 18 of the 32 most recent bot inline comments hit the cap. Example: https://github.com/pytorch/pytorch/pull/201517#discussion_r4327961606 - `MAX_MESSAGE` goes from 600 to 1500, the same as `MAX_SUMMARY`. - The stage B prompt asks for at most 1200 characters per finding message, counting each `&`, `<` and `>` as five, since escaping lengthens them. - A message that still overflows ends in ` ...` at a word boundary. The input slice and the output cut both drop the token they split. `neutralize_message()` then drops trailing tokens until the result passes the publish check (`is_neutral_prose`). If no prefix passes, it returns empty, and the existing empty-message drop applies. A message that fails the check as a whole (for example, a blob that `neutralize()` joins) is not trimmed: it is still dropped as `failed_publish_check`. - A contract test keeps the prompt's budget at or below 80% of `MAX_MESSAGE`, and tests cover the cut cases. At the new cap, a review with 25 findings stays well under GitHub's body limit, and `MAX_BODY` still drops whole findings and says so when needed. Dr.CI's renderer has its own findings budget (`PR_REVIEW_FINDINGS_BUDGET`) and reports any findings it hides. Test plan: `python3 -m unittest discover -s scripts/pr_review -p 'test_*.py'` (452 tests pass). Pull Request resolved: https://github.com/pytorch/pytorch/pull/201566 Approved by: https://github.com/d4l3k |
||
|
|
53e27b2102 |
[hardened PR review] Hide an earlier review even if its threads can't be resolved (#201243)
✴️ iz2: On pytorch/pytorch the publish job's GITHUB_TOKEN is refused `resolveReviewThread` (FORBIDDEN, "Resource not accessible by integration"). `take_down` then returned before minimizing, so a superseded bot review with inline comments stayed visible. Seen on #201131: the clean run dismissed review 5549973588 but never hid it. Now the review is minimized anyway. Threads that could not be resolved stay open, and once the review is minimized no later run retries them. Granting the job `contents: write` was the alternative; it widens the token, so it is not done here. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .` passes (494 tests). The two new tests fail on the old code. Pull Request resolved: https://github.com/pytorch/pytorch/pull/201243 Approved by: https://github.com/huydhn |
||
|
|
64cf217125 |
[hardened PR review] Remove the readiness rubric and the write hook (#201133)
✴️ iz2: Neither stage writes a file any more and the verdict now comes from pr-review's own recommendation, so the `pr-review-readiness` rubric skill and `restrict-write.sh` (the write fence for the old single-stage reviewer) are unused. This deletes both, their contract tests, and their paths in the suite's trigger filter. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .` Pull Request resolved: https://github.com/pytorch/pytorch/pull/201133 Approved by: https://github.com/albanD ghstack dependencies: #201131, #201132 |
||
|
|
3139f1c31d |
[hardened PR review] Remove the findings validators and their hooks (#201132)
✴️ iz2: Stage B now returns its findings through `--json-schema`, so the validators that checked a model-written findings file are unused. This deletes validate_findings.py and its tests, the three validate-* hooks that ran it, and verdict_after_subagents.py, which only the stop hook called. The sanitizer still checks every finding before publish. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .` Pull Request resolved: https://github.com/pytorch/pytorch/pull/201132 Approved by: https://github.com/albanD ghstack dependencies: #201131 |
||
|
|
4bb17d2f12 |
[hardened PR review] Two stages: pr-review, then schema-enforced findings (#201131)
problem: current implementation of the automated PR review sometimes disagrees with local /pr-review skill invocations about which findings are blocking. to improve the behavioral parity, this PR is splitting the review into two stages, where stage A closely replicates the prompt for /pr-review, and stage B mechanistically extracts the findings and maps them to the code lines, preserving the severity rating. --- ✴️ iz2: Makes the automated PR review reach the same verdict as a local `/pr-review <N>`. Before this change, one model step reviewed against a separate readiness rubric and picked its own verdict, which could differ from pr-review's recommendation. Stage A runs exactly `/pr-review <N>`: the trusted copy of the pr-review skill, at high effort (the local default), offline and read-only (no Bash, Write or network). Its system prompt adds only what an offline run needs: a map from the skill's gh/git calls to pre-fetched files, and the security rules. The files are the diff, the changed-file list, and the PR's title, description, and author and maintainer comments, each tagged with its role from the permission API so the skill's maintainer-comment check runs. Its final message is the review in markdown. Stage B reads that review plus the diff and returns schema-enforced JSON (`--json-schema`): the verdict from the review's recommendation (Request Changes -> changes_requested; Approve and Needs Discussion -> ready_for_human_review) and its findings anchored to changed lines. That JSON becomes the findings file; the sanitizer and publish steps are unchanged. A contract test fails when the skill gains a gh/git command that stage A cannot serve offline, so a skill change cannot silently drop a check. The readiness rubric, write hooks and validators are now unused; the next PRs in this stack delete them. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .`. Parity: 41 PRs (33 earlier test cases plus 8 with addressed or unaddressed maintainer comments), each reviewed 3 times by a local `/pr-review <N>` and 3 times by this job in a test repository, on the same head, skill version and conversation cut-off. Verdicts agreed as often across the two paths (89% of pairs) as between repeat runs of either path (85% local, 90% CI). On the 32 PRs where all three local runs agreed, the CI majority matched every time. On the 5 PRs with an unanswered maintainer comment, all 30 runs flagged it. Deviations from local use: stage A reads pre-fetched files instead of running `gh`/`git`, and sees only the author's and maintainers' comments. Pull Request resolved: https://github.com/pytorch/pytorch/pull/201131 Approved by: https://github.com/albanD |
||
|
|
ab95a964f3 |
Use file header to determine owner (#200684)
## Summary Chatted internally: 1) Check for the owner in the header. 2) If the header is missing or is in `IGNORED_OWNERS`, then fallback to CODEOWNERS. ## Test Summary ``` $ python scripts/codeowners/owners.py | jq ``` _Authored with Codex_ Pull Request resolved: https://github.com/pytorch/pytorch/pull/200684 Approved by: https://github.com/janeyx99 |
||
|
|
3b6339b662 |
Create a script to determine test owners (#200341)
## BC-breaking? No ## Summary This script maps a test file to an owner, where owner is determined by CODEOWNERS. We will eventually periodically push these ownership to ClickHouse so we can start attributing (read blaming) test owners for breakage, spend, etc. ## Agent details Written with Claude Code; the author reviewed all changes. ## Test plan ``` $ python scripts/codeowners/owners.py | jq ``` Output: P2543350761 ``` $ python scripts/codeowners/check_taxonomy.py CODEOWNERS taxonomy summary Edit CODEOWNERS to fix failures reported below. Use `/path/ @owner` for active ownership or `# /path/` when unresolved. Tracked paths: 22707 (committed paths checked) Covered paths: 22707 (matched by at least one pattern) Coverage: 100.0000% (must remain 100%) Review surfaces: 55 (sections that effectively cover committed paths) Patterns: 1059 (active ownership rules and commented unresolved paths) Paths matched by multiple patterns: 3721 (expected when narrower patterns follow broader ones) Integrity checks (all must be zero) Uncovered paths: 0 Duplicate patterns: 0 Stale patterns: 0 Ineffective patterns: 0 Source-order mismatches: 0 Result: PASS ``` Pull Request resolved: https://github.com/pytorch/pytorch/pull/200341 Approved by: https://github.com/georgehong |
||
|
|
5647aaae30 |
Define "ready for review" and move the label decision out of the automated review (#199956)
## Author Notes This is aiming to codify the "ready for review" means: - pr-review didn't flag anything - All comments from maintainers have been addressed - All relevant CI is green To reach that, I need to make a few changes: - The current pr-review workflow will stop updating the label (it's result is still visible instantly on the pytorchbot comment) - The current pr-review workflow is extended to look at maintainer asks on the pr and will always flag as major any of them that is not resolved (thus preventing accept) - Updating the recurring pytorchbot job to do the label change if all CI signal is green AND pr-review signal is green Companion test-infra PR: https://github.com/pytorch/test-infra/pull/9041 --- - [x] Passes lint (`spin fixlint`) - [x] Added/updated tests - [x] Updated documentation (if applicable) - [ ] Included benchmark results (for PRs impacting perf) ## Linked issue or supporting maintainer Author has write access. ## BC-breaking? No ## Agent Notes > CONTRIBUTING.md now tells contributors when their PR becomes `ready for review` and what to do to get there, and the maintainer guide tells reviewers what to expect from a PR in their queue: the automated review (pr-review skill) ran and flagged nothing blocking, all CI finished and Dr. CI classified every failure as unrelated (workflows waiting for a maintainer to approve their run do not block), and every maintainer comment was addressed. Since CI is usually still running when the review finishes, the hardened review workflow no longer moves the label: its `publish` job only records the verdict row and posts the review (the label step and its `issues: write` scope are removed), and Dr. CI promotes the PR in the companion test-infra PR. Maintainer comments are checked by the review itself as ordinary findings: `prepare` collects comments from the author and from users with triage access or above (new `scripts/pr_review/fetch_pr_comments.py`, fail-closed, stamped before the `started` row), passes them to the sandboxed review as a job output (single-line JSON, capped at 100 KB) that it writes to `/tmp/pr-comments.json`, and the pr-review skill and readiness rubric report each unaddressed one as a blocking finding. Authors re-run the review with `@pytorchbot review` after replying. Landing order: test-infra first, then this PR. > > Test plan: `python3 -m unittest discover -s scripts/pr_review -t .` (492 tests pass), `spin lint`. > > Authored with an AI assistant (Claude Code). Pull Request resolved: https://github.com/pytorch/pytorch/pull/199956 Approved by: https://github.com/izaitsevfb |
||
|
|
72104d5502 |
Re-review contributor pushes in the hardened PR review (#200156)
✴️ iz2: After a review asks for changes, `in progress` stays on so the author's next push is reviewed again. For authors without write access, which is most open-source contributors, that re-review failed at setup with "User does not have write access on this repository" (recorded as `model_error`), e.g. #196943. The review step now passes the job's own read-only token as `github_token` and sets `allowed_non_write_users`, so those pushes are reviewed like anyone else's while `in progress` is on; `prepare` still checks that label. It also sets `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB: "0"`, without which Claude Code fails to install in this job. The workflow comments give the reasoning; contract tests pin all three settings to the review step. **Testing:** `scripts/pr_review` suite passes (474 tests). A ciforge canary running this review step through `workflow_run` produced the same verdict as the unchanged step (changes_requested, 3 findings). The canary's actor had write access, so a real contributor push is first exercised after merge. Pull Request resolved: https://github.com/pytorch/pytorch/pull/200156 Approved by: https://github.com/liangel-02, https://github.com/huydhn |
||
|
|
6ac7d2a9e3 |
[pr-review] Post the automated review as a native GitHub review (#199734)
✴️ iz2: **What.** The hardened PR review's verdict is also posted as a normal GitHub review by `github-actions[bot]`, from a new last step of the `publish` job (`scripts/pr_review/post_review.py`). The Dr.CI comment section is unchanged. **Why.** Today the verdict appears only in the Dr.CI comment. Edits to that comment send no notification, so authors miss a verdict that is waiting on them. A review notifies them and puts findings on their lines. **How it behaves.** - `changes_requested` becomes REQUEST_CHANGES; anything else is a COMMENT, never an approval. - Blocking (`major`) findings are comments on their lines. The rest are folded into one collapsed section of the review body, each as a link to its line (GitHub renders the code) with the message under it. - A new review is posted before older ones are touched. Older bot reviews then have their threads resolved and are minimized as outdated. Their text is never edited, nothing links between reviews, and nothing is deleted. - A newer change request replaces the older one in the PR's review status. A comment-only review does not, so after a clean verdict the bot dismisses its earlier change requests. A change request is minimized only after it is replaced or dismissed. - Failed, skipped and opted-out runs change nothing. - API errors are warnings. The step fails only if an earlier change request may still stand after a clean verdict. **Sanitizing.** `extract_verdict.py`, the pipeline's existing sanitizer, already cleans the model's output in the review job: printable ASCII only, length caps, findings only on lines the PR changed, and no links, images, HTML, `@mentions` or `#123` references. `post_review.py` re-runs those same checks and adds two escapes needed only for a GitHub review: `@pytorchbot` mentions, so a review cannot issue bot commands, and runs of 3+ backticks or tildes (an unclosed code block would swallow the rest of the review). Dr.CI's renderers in test-infra are separate TypeScript, so they can't share this code. **Testing.** - `python3 -m unittest discover -s scripts/pr_review -t .`: 473 tests pass. - On pytorch/ciforge, this script posted real reviews as `github-actions[bot]`; review status, review states and threads were checked after every step. Demos: - pytorch/ciforge#691: a single review with one blocking and three folded findings. - pytorch/ciforge#690: three reviews on three commits. Older ones are minimized unchanged, and the bot's change requests are dismissed after the clean one. - pytorch/ciforge#692: a reply on a blocking finding survives re-review as a resolved thread. - pytorch/ciforge#693: a longer history, including a failed run, an opted-out run, and a finding outside the diff. - Not yet run through the real two-stage workflow, since Stage 2 only runs from `main`. Size: 1,652 lines, slightly over the 1,500-line PR limit because of the tests (925 lines), so this PR carries `skip-pr-sanity-checks`. Pull Request resolved: https://github.com/pytorch/pytorch/pull/199734 Approved by: https://github.com/janeyx99 |
||
|
|
2573484267 |
Auto PR Triage: relax PR base restriction for ghstack PRs (#199927)
---- - [x] Passes lint (`spin fixlint`) - [x] Added/updated tests - [x] Updated documentation (if applicable) ## BC-breaking? No ## Summary (human written only) - ~~currently ghstack PRs are being ignored for auto triage bc we require the PR base branch to be main~~ - ~~when pull_request_target is used, the workflow runs from the latest commit of the PR's base branch, for ghstack PRs, this base branch is gh/*/base rather than main~~ - ~~bedrock currently allow allows runs whose ref is main (adding gh/*/base to its policy, might be fine, but the ghstack base branch is a snapshot of when the ghstack was last pushed and can be stale, maybe that's not a really big deal?)~~ - ~~the solution here is to have a two separate workflows (1) a "request" workflow that runs from PR's base branch whose only job is to capture the PR number (2) a workflow that gets triggered by the first workflow's completion; this workflow always runs from main~~ Update from @izaitsevfb 's agent: > One thing affects Jeffrey's PR itself. Since 2025-12-08, GitHub runs pull_request_target workflows from the default branch, not the PR's base. Our Stage 1 header cites that GitHub changelog, and two other projects' issues confirm the change. If so, Auto PR Triage already runs from main for ghstack PRs. It skipped them only because of its own base == main checks, so relaxing those might be enough without the relay. I didn't verify whether the bedrock environment check sees main in that case, so Jeffrey would need to test it. ## Agent details (if applicable) > Auto PR Triage skipped every ghstack PR (e.g. https://github.com/pytorch/pytorch/actions/runs/37475856554 on #199914). `pull_request_target` runs the workflow from the PR's base branch, and a ghstack PR's base is `gh/<user>/<N>/base`, not `main`. The `analyze` guard required `base.ref == 'main'`. Relaxing that guard alone would not help: the `bedrock` environment's branch policy only allows `main`, and running workflow code and policy from a `gh/*/base` branch would let anyone with push access run their own version with Bedrock credentials and `pull-requests: write`. > > The fix splits the trigger from the work. The new `auto-pr-triage-request.yml` is a permissionless `pull_request_target` workflow whose only job applies the old entry check and puts the PR number and event in its run name. `auto-pr-triage.yml` now runs `on: workflow_run`, which always runs from `main`. Its new first step parses the run name, then checks that the named PR's head repo and branch match the ones GitHub recorded for the request run, since anyone with push access can edit the request workflow on a ghstack base branch. Intake accepts `main` or `gh/<user>/<N>/base` as the base, and `--expected-base-ref` is removed because intake reads the base from the PR itself. > > Alternatives: adding `gh/*/base` to the `bedrock` branch policy is the smallest diff but trusts code on any base branch; keeping `pull_request_target` for main-based PRs and adding the relay only for ghstack would leave two entry paths. The run name carries the request instead of an artifact because it needs no upload, download, or `actions: read`; both are equally untrusted, so the head-branch check is what matters. > > Review order: `auto-pr-triage-request.yml`, then the trigger, `analyze` guard, and "Resolve the requested PR" step in `auto-pr-triage.yml`, then `identifiers.py`/`assess_intake.py`, then tests and README. > > `workflow_run` only fires for workflows on the default branch, so the hand-off cannot run before merge. A ghstack stack is triaged only after it is rebased onto a `main` that has the request workflow. > > Test plan: > ``` > cd scripts/auto_pr_triage && python -m pytest tests -q > ``` > (249 passed; the new resolve-step test fails when the head-branch check is removed) > ``` > lintrunner --paths-cmd 'git diff --name-only origin/main' > ``` > (clean, including actionlint/shellcheck) > > Authored with an AI assistant (Claude Code). Pull Request resolved: https://github.com/pytorch/pytorch/pull/199927 Approved by: https://github.com/janeyx99 |
||
|
|
ac7ad2ec6e |
[Auto PR Triage] Lead the step summary with each reviewer and why (#199836)
Go through the following checklist - [x] Passes lint (`lintrunner`) - [x] Added/updated tests - [ ] Updated documentation (if applicable) - [ ] Included benchmark results (for PRs impacting perf) --- ## BC-breaking? No ## Summary (human written only) TODO(soulitzer): write the summary. ## Agent details (if applicable) > The step summary used to open with the 20-row intake and ownership table, so a > requested reviewer had to scroll past it to the last section to learn why they > were requested. This reorders the summary and reuses the existing explanation > text; no planning logic changes. > > Example rendering of three real runs by this code: > https://github.com/soulitzer-test/check-name-test/pull/1#issuecomment-6003824086 > > Suggested review order: > > 1. `step_summary.py`: `summary_code` renders untrusted text as `<code>` with > entities, so it is safe inside a table cell (backticks and `\|` are not), > and `summary_diff` fences an excerpt with more backticks than it contains. > 2. `plan_actions.py`: `TextStyle.excerpts` becomes an `excerpt` renderer, which > lets the summary show the diff excerpts it previously omitted. > `render_step_summary` writes a heading naming the requested reviewers, a > decision/admission/labels table, and a reviewer table whose rows carry the > existing reason sentences. Codepath owners' matched files fit in the row; > each semantic owner's longer provenance gets a section below the table, > linked from the row. The previous decision-plan bullets follow, collapsed. > 3. `validate_ownership.py`: the analysis table and LLM concerns are collapsed > in `<details>`. The step summaries render in step order, so this block > still comes first, as one collapsed line above the plan stage's summary. > > The job log output is unchanged. > > Test plan: > > ``` > cd scripts/auto_pr_triage && python3 -m unittest discover -s tests -t . > lintrunner -a scripts/auto_pr_triage/*.py scripts/auto_pr_triage/tests/*.py > ``` > > New tests cover the reviewer table, the linked owner section with a fenced > excerpt, and inert rendering of a path and excerpt containing `|`, `_`, and > backticks. I also rendered five recent runs' uploaded artifacts with this code > (rebuilding each plan from `planner_input.json`, which matched the run's > `plan.json`) and checked the output with GitHub's Markdown API: every table, > collapsed block, diff block, and owner link rendered as intended. > > Not verified: whether the in-page owner links jump in a job summary page; they > do in comments. > > This PR was authored with an AI assistant (Claude Code). Pull Request resolved: https://github.com/pytorch/pytorch/pull/199836 Approved by: https://github.com/janeyx99 |
||
|
|
0f30a5acad |
Move skills to the Agent Skills location .agents/skills (#199554)
Agent harnesses other than Claude Code (pi, Codex, and other Agent Skills implementations) discover project skills in `.agents/skills/`. Make that the real directory and keep `.claude/skills -> ../.agents/skills` so Claude Code still discovers every skill, and one copy serves every harness. References that match on real file paths now name `.agents/skills`: the hardened PR review's path-scoped Read allowlist, prompt hash, and contract tests; the `pr-review-scripts-test` path filters (GitHub matches changed paths, which are now under `.agents/skills`); the distributed-triage cron's label file; and the doc links in CONTRIBUTING.md and the maintainer guide, which GitHub cannot browse through a symlinked directory. Skill hooks that run `$CLAUDE_PROJECT_DIR/.claude/skills/...` keep working through the symlink. The sdist already excludes `.claude/**`; exclude `.agents/**` as well. The NEWLINE and EXEC linters, which match every path, skip the `.claude/skills` symlink itself (NEWLINE crashes on a directory symlink and EXEC reports it as executable); the files are linted under `.agents/skills`. `pr-review-readiness/SKILL.md` gains its missing trailing newline because the move puts it in front of NEWLINE. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .` fails the same 6 macOS-only tests (GNU stat/sha256sum, jq on PATH) before and after. Pull Request resolved: https://github.com/pytorch/pytorch/pull/199554 Approved by: https://github.com/albanD, https://github.com/huydhn |
||
|
|
49eb13cbe9 |
Auto PR Triage: leave PRs labeled no automated triage as is (#199551)
## Author Notes as per title, relatively simple ## Agent Notes > Auto PR Triage now treats the `no automated triage` label like an existing outcome label. When intake sees the label on the PR's live labels, it marks the PR as already handled: the ownership LLM call is skipped, the plan is `kept_open` with no actions, and nothing is written to the PR, so it stays in the manual triage queue. The check is in `HANDLED_LABELS` (`scripts/auto_pr_triage/assess_intake.py`) rather than in the workflow `if:`, because the event payload only has the labels from when the event fired. It also reuses the existing no-op path. The README, the workflow comment and the CONTRIBUTING.md status table are updated to match. > > Test plan: > ``` > cd scripts/auto_pr_triage && python -m pytest tests/ -q > ``` > (246 passed) > > Authored with an AI assistant (Claude Code). ## BC-breaking? No - [x] Passes lint (`spin fixlint`) - [x] Added/updated tests - [x] Updated documentation (if applicable) Pull Request resolved: https://github.com/pytorch/pytorch/pull/199551 Approved by: https://github.com/soulitzer, https://github.com/janeyx99 |
||
|
|
e188cec401 |
Revert "Move skills to the Agent Skills location .agents/skills (#199554)"
This reverts commit
|
||
|
|
06f99c1961 |
Move skills to the Agent Skills location .agents/skills (#199554)
Agent harnesses other than Claude Code (pi, Codex, and other Agent Skills implementations) discover project skills in `.agents/skills/`. Make that the real directory and keep `.claude/skills -> ../.agents/skills` so Claude Code still discovers every skill, and one copy serves every harness. References that match on real file paths now name `.agents/skills`: the hardened PR review's path-scoped Read allowlist, prompt hash, and contract tests; the `pr-review-scripts-test` path filters (GitHub matches changed paths, which are now under `.agents/skills`); the distributed-triage cron's label file; and the doc links in CONTRIBUTING.md and the maintainer guide, which GitHub cannot browse through a symlinked directory. Skill hooks that run `$CLAUDE_PROJECT_DIR/.claude/skills/...` keep working through the symlink. The sdist already excludes `.claude/**`; exclude `.agents/**` as well. The NEWLINE and EXEC linters, which match every path, skip the `.claude/skills` symlink itself (NEWLINE crashes on a directory symlink and EXEC reports it as executable); the files are linted under `.agents/skills`. `pr-review-readiness/SKILL.md` gains its missing trailing newline because the move puts it in front of NEWLINE. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .` fails the same 6 macOS-only tests (GNU stat/sha256sum, jq on PATH) before and after. Pull Request resolved: https://github.com/pytorch/pytorch/pull/199554 Approved by: https://github.com/albanD |
||
|
|
e3fd0188b0 |
[CI] Let pytorch-bot trigger the hardened PR review (#199564)
## Summary Hardened PR review fails whenever pytorch-bot applies `in progress`; AI-drafted explanation below. > The `review` job in `hardened-pr-review-run.yml` failed on #199307 ([run](https://github.com/pytorch/pytorch/actions/runs/37044333444)) before the model started: `Workflow initiated by non-human actor: pytorch-bot (type: Bot)`. > > Stage 2 runs on `workflow_run` and inherits Stage 1's actor, which is whoever applied the `in progress` label. A maintainer's `@pytorchbot pre-review accept` makes pytorch-bot apply that label. It does so only once the PR is `triaged` and every assigned reviewer has accepted, with the author excluded. claude-code-action rejects bot actors that aren't listed in `allowed_bots`, and this step listed none. > > This PR adds `allowed_bots: "pytorch-bot[bot]"`, matching `claude-autorevert-advisor.yml` and `claude-issue-triage-run.yml`. It names the bot exactly instead of using `"*"`, so other bots that can label PRs still can't start a review. A contract test pins the value. ## Checklist - [x] Passes lint (`spin fixlint`) - [x] Added/updated tests - [x] Updated documentation (if applicable) - [x] Included benchmark results (for PRs impacting perf) ## BC-breaking? No Pull Request resolved: https://github.com/pytorch/pytorch/pull/199564 Approved by: https://github.com/izaitsevfb |
||
|
|
921d395fc0 |
Auto PR Triage: print the worker's Claude transcript in the job log (#199517)
The ownership worker's conversation was not visible anywhere. The Claude
action runs with show_full_output off, so the log shows only an init line and
a result summary. When a run fails, for example after running out of
structured-output retries, the action writes no execution log at all, so we
could not see which schema errors the model kept hitting.
Turning show_full_output on is not safe here. The action would print every SDK
message, including PR-controlled model text, as raw JSON. JSON escaping stops a
line from starting with a `::command::`, but the runner still parses the
legacy `##[command]` form anywhere in a line, and `set-output` still works
through it, so a PR could get the model to echo a command that overwrites the
Claude step's outputs. Wrapping the step in stop-commands does not help either,
because the action runs its own inner steps, each with fresh command state.
Instead, the worker no longer passes --no-session-persistence, so Claude Code
saves its transcript as it runs, including on failure. A new step right after
the Claude step prints every user and assistant message from that transcript
through the same sanitizer the result log already uses, which escapes both
`::` and `##[`. That sanitizer moves to step_summary.print_log_json so both
callers share it. The first message is the prepared prompt; it is omitted
because it can be up to about 1 MB and is already recorded in llm_input.json.
The step uses continue-on-error, so a transcript problem cannot fail triage.
Test Plan:
```
cd scripts/auto_pr_triage && python -m unittest discover -p 'test_*.py'
lintrunner .github/actions/auto-pr-triage/action.yml scripts/auto_pr_triage/*.py scripts/auto_pr_triage/tests/*.py
```
test_log_transcript checks that model text containing `::error::` and
`##[set-output ...]` is printed escaped, that the prompt is omitted, and that
non-message records are skipped. Locally, Claude Code 2.1.92 with the CI flags
(including --bare) wrote the transcript to ~/.claude/projects/<cwd>/; with an
unsatisfiable schema and MAX_STRUCTURED_OUTPUT_RETRIES=2, log_transcript.py
printed the schema error the model received ("Output does not match required
schema: /x: must be <= 4") and the model's replies after each attempt.
This PR was authored with an AI assistant (Claude Code).
Pull Request resolved: https://github.com/pytorch/pytorch/pull/199517
Approved by: https://github.com/janeyx99
|
||
|
|
bc258774bd |
Auto PR Triage: start a new run whenever open source is added (#199508)
Until now, a PR could be triaged by the label only once. The admission step counted the PR's `open source` labeled events and admitted a labeled event only when the count was exactly 1, so removing and re-adding the label was skipped as a repeated event. The only way to reevaluate a PR was to rerun all jobs of its original run by hand. Now every `open source` labeled event is admitted. Adding a label needs triage permission, so a re-add is a deliberate request to triage, which matches the existing rule that the label, not the ready event, is the authorization. A PR that already has an outcome label (`triaged`, `bot-triaged`, or `bot-triage-error`) is still a no-op: intake marks it handled, so the run skips the LLM and plans nothing. To reevaluate a PR, remove its outcome labels and remove and re-add `open source`. That run is a first attempt, so it may mark the PR as missing an actionable issue again, unlike a manual rerun. The ready-for-review rule is unchanged: it admits the first ready event after the latest `open source` label, so a re-add resets it. A labeled event no longer depends on the timeline history, so it is admitted even when that history is truncated; the truncation check now only applies to ready events. Test Plan: ``` cd scripts/auto_pr_triage && python -m unittest discover -p 'test_*.py' actionlint .github/workflows/auto-pr-triage.yml ``` test_workflow_admits_label_events_and_first_ready_event runs the admission script against fake timelines; it now expects a second `open source` label to be admitted, and a labeled event to be admitted with truncated history. This PR was authored with an AI assistant (Claude Code). Pull Request resolved: https://github.com/pytorch/pytorch/pull/199508 Approved by: https://github.com/janeyx99 ghstack dependencies: #199428, #199507 |
||
|
|
13cc221983 |
Auto PR Triage: fix failures seen in the first day of shadow runs (#199428)
The first day of shadow runs on pytorch/pytorch (347 runs, 01:53-14:10 UTC on 2026-10-02) found three problems. 1. Ten eligible PRs were never analyzed. The labeler adds several labels to a new PR in the same second, and every label event queued a run in the same per-PR concurrency group. GitHub keeps one pending run per group, so a later label's run replaced the pending `open source` run and then skipped itself. For example, "Remove unused parameters in autograd" got five runs at 07:12:28: both eligible runs were cancelled and the other three skipped. Now only entry events (the `open source` label and ready_for_review) share the per-PR group; every other event gets its own group keyed by run id. Entry events still run one at a time per PR. 2. Four runs (#199436, #199440, #199463, #199484) failed with Claude Code's `error_max_structured_output_retries`: the worker produced no schema-valid output within the default 5 attempts. Many successful runs also needed a retry (4 turns instead of 2). MAX_STRUCTURED_OUTPUT_RETRIES is now 25. Every attempt is a turn, so --max-turns goes from 5 to 30. A failed run cost about $0.02 per attempt, so --max-budget-usd goes from 0.50 to 1.50. When the SDK errors like this, the action writes no execution log and leaves `execution_file` empty. Stage 2c then opened `Path("")`, which is `.`, and recorded "IsADirectoryError" as the processing error. It now records "Claude action wrote no execution log". 3. Eight runs failed the evidence-excerpt check. One cut a long line short (the case that started this change, #199425). Five skipped lines inside an excerpt (#197857, #199447, #199471, #199472, #199497). Two turned an unchanged line, which also appeared in the `@@` hunk header, into a fake `-`/`+` pair (#199453, #199461). The worker prompt still asks for whole, adjacent lines, and now also says never to cut or skip lines or to turn unchanged lines or hunk-header text into changed lines. A local replay showed that wording alone does not stop the model from dropping comment lines or stopping a long line at the end of a sentence, so the validator is relaxed as a safety net. Every excerpt line must still come from the file's patch, in patch order, but lines may be skipped in between, and a line may stop early if it keeps some text after its marker. Every character shown still comes from the patch, so the model still cannot add text; what we give up is that an excerpt may show two lines without the lines between them. A fake `-`/`+` pair is still rejected, because it claims an unchanged line changed. We also considered having the model cite patch line ranges and extracting the excerpt in the controller, which removes these failures by construction. The relaxed check fixes almost all of them with a much smaller change. The same change is being made to pytorch/pytorch and pytorch/ciforge. Test Plan: ``` cd scripts/auto_pr_triage && python -m unittest discover -p 'test_*.py' actionlint .github/workflows/auto-pr-triage.yml ``` The worker was replayed locally (claude-sonnet-4-6, --effort low, the CI flags and the old 5-attempt limits) on the exact inputs of the eight runs that failed the excerpt check: each run's patch at its analyzed head_sha and CODEOWNERS at its workflow_sha. Outputs were checked with validate_result. | Prompt and check | Excerpt failures | |--------------------------------------|------------------| | old worker.md, old check (3 PRs) | 8/12 | | new worker.md, old check (3 PRs) | 5/11 | | new worker.md, relaxed check (8 PRs) | 1/21 | Re-checking the eight original CI outputs with the relaxed check, six pass. The two that still fail (#199453, #199461), and the one replay failure (#199453), are all the fake `-`/`+` pair, which stays rejected. An extra prompt example aimed at that pattern made it worse in a replay, so it is not included. This PR was authored with an AI assistant (Claude Code). Pull Request resolved: https://github.com/pytorch/pytorch/pull/199428 Approved by: https://github.com/janeyx99 |
||
|
|
a0931e1316 |
Record sanitized PR review findings in the verdict row (#199409)
✴️ iz2: The hardened PR review's ClickHouse row (`misc.pr_review_verdicts`) carries the verdict, summary and a findings count, but not the findings, so no surface can show an author what the review objected to. This writes the sanitized findings from `verdict.json` into the row's existing `extra` map as `extra['findings']`, a JSON array of `{path, line, severity, message}`. No table or replicator change: `extra` is already `Map(String, String)`. `verdict.json` comes from the job that read untrusted code, so the publish job re-checks every string it writes to the row (verdict, summary, findings, failure_detail) against what `extract_verdict.py` can emit. The predicates live in `extract_verdict.py` next to `neutralize()`, and `build()` holds its own output to them, so `validate_findings.py` reports in-session anything the publish job would drop. A summary or verdict that fails is recorded as `sanitizer_rejected`; the label step reads status and verdict back from that row, so it cannot mark the PR reviewed. A failing finding is dropped and counted in `extra['findings_dropped_at_publish']`. A failure here never stops the row from being written. Dr.CI renders these findings inside a code fence: pytorch/test-infra#8972. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .` (389 pass), `lintrunner` on the changed files. Pull Request resolved: https://github.com/pytorch/pytorch/pull/199409 Approved by: https://github.com/jeanschmidt, https://github.com/huydhn |
||
|
|
d0b8bbb015 |
Let the hardened PR review use pr-review's sub-agents (#199411)
✴️ iz2: The CI review never ran pr-review as written. The readiness skill told the model to replace pr-review's sub-agents, including the Step 5 per-finding fact-checks, with its own reading, and the workflow denied the `Task` tool. Local `/pr-review` runs spawn several sub-agents. In a blind comparison on the same PR revisions, local runs requested changes on every revision CI rated ready. Changes: - `hardened-pr-review-run.yml`: allow `Agent` (the current name for `Task`). Sub-agents are pinned to the review model and run in the foreground, inside the reviewer's turn. - `pr-review-readiness/SKILL.md`: require three to six parallel sub-agents with distinct focuses before writing the findings file, plus a fact-check sub-agent per candidate `major` finding. - `restrict-write.sh`: only the reviewer writes the verdict; sub-agents report back to it. - `validate-on-stop.sh` + `verdict_after_subagents.py`: the findings file must be written after the last sub-agent report. Otherwise the session is sent back to rewrite it, and if it still ends on the earlier file, the run is recorded as failed instead of being published. - Time limit: the model step now gets 55 minutes and the review job 90, to be tightened once live durations are known. Verified on CI with a temporary pytorch/ciforge workflow running this PR's exact review step on 18 PR revisions, each compared with two isolated local `/pr-review` runs. Where the local runs agreed with each other (16 revisions), the new review matched them on 11. On the earlier hardened-review revisions it matched on 5 of 6, against 3 of 6 for the current review; pytorch/pytorch#196507 and pytorch/pytorch#197012 flip from ready to changes requested. In every remaining mismatch, CI said ready where local asked for changes. Rebased on pytorch/pytorch#199410. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .`, plus the CI runs above. Pull Request resolved: https://github.com/pytorch/pytorch/pull/199411 Approved by: https://github.com/huydhn |
||
|
|
90769ae442 |
Add Auto PR Triage in shadow mode (#195778)
NOTE: Auto PR Triage has been mostly rewritten since the previous review.
Human written reviewer guide:
- Job split (This also captures what changed between the previous version. We're still doing the same analysis apply split, but the previous version had a much more complex apply stage. In this split, apply is a simple executor and carries out zero policy logic. This makes shadow mode much more simpler)
- The auto pr triage workflow is split between 3 jobs, a ready-only analysis job and, a "dumb" write-access job, and a error_reporting job. The analysis job has read-only access to github and bedrock permissions for its claude action step. It's purpose is to generate the exact instructions that the apply job would then execute, like what labels to add, what reviewers to request, and whether to close the PR. The apply job does not have complex logic of its own, its only purpose is to execute the simple instructions that the analysis job produced. When the workflow is run in shadow mode, we only execute the analysis job and not the apply job.
- The analysis job is further split into smaller modules with their own responsibilities. schemas.py is what captures what data flows between each module boundary, for schemas.py, skip most of the post init sections (though if there's a doc string that's worth reading).
Analysis job file / module split:
- intake (assess_intake.py)
- this still happens first, but unlike previously, the decision made here is no longer final. It can be overridden if any team has a intake bypass criteria.
- ownership analysis
- deterministic codepath-based analysis (codepath_owners.py)
- ownership based analysis
- prepare llm input (build_ownership_input.py)
- run llm (worker.md + claude action)
- validate llm output (validate_ownership.py)
- plan actions (plan_actions.py see exit 1-4 in the main routine; the workflow diagram can mostly be mapped to this)
- plan actions is a pure function which should run determinsitically given the same inputs
- inputs: 1) a snapshot of the reviewer state (reviewer_state.py), 2) the ownership analysis output, 3) intake output
- handles owner routing, error cases, and returns a list of actions
- The apply is logically only one step (apply_actions.py), it will just naively execute the list of actions that plan_actions.py returned.
<img width="978" height="955" alt="image" src="https://github.com/user-attachments/assets/f3fb3bc3-557f-4ac0-b0d0-2f5043f84b74" />
Mechanically import the reusable Auto PR Triage workflow, controller, tests, and documentation from pytorch/ciforge@9718df7668. All 31 reusable files are byte-identical to that source.
For review, start with the workflow and composite action, then schemas.py, which defines the typed record each stage writes. The stages run in order: assess_intake.py gathers the intake facts, build_ownership_input.py resolves codepath owners from the root CODEOWNERS file and builds the LLM input (worker.md is the LLM's instructions), validate_ownership.py checks the LLM result, plan_actions.py decides one plan from those records alone, and apply_actions.py executes it in live mode only. scripts/auto_pr_triage/README.md keeps what the code cannot show (goals, accepted tradeoffs, the reasons behind some rules, and operating notes), overview.svg is the decision flow, and tests/ mirrors the stages. The initial deployment is shadow-only, and GitHub's native CODEOWNERS keeps requesting codepath owners; Auto PR Triage never requests them.
Since the previous revision of this PR, a team owner's reviewer is picked at random from its roster, seeded by the PR, instead of by round robin, so the `owner:` labels are gone; a PR that is not admitted gets the labels `triaged`, `bot-triaged`, and `missing actionable issue` instead of being closed, so the bot can no longer close a PR or comment; codepath owners are read from the root CODEOWNERS file, so the separate `codepath_owners.txt` copy and the CODEOWNERS comparison are gone; and the design doc became the README.
## Example explanations
For every run, the plan step logs why the PR was admitted and, for each reviewer, why they were requested. For an owner, that includes its provenance: the CODEOWNERS rules it matched, or the validated semantic concern with the LLM's reasoning and diff evidence. The first two examples are copied verbatim from shadow-mode end-to-end runs on pytorch/ciforge, including the log's escaping of quotes and non-ASCII characters. They ran before reviewers were picked by a seeded random choice: where #637 says "@izaitsevfb was the next eligible member of `autograd`'s round-robin rotation after @soulitzer, who was assigned on #560", the current code says "@izaitsevfb was picked at random from `autograd`'s roster, seeded by this PR so that reruns pick the same member."
<details>
<summary>Reviewer picked for a semantic owner (pytorch/ciforge#637)</summary>
```
Why this PR was admitted: Admitted because it is part of an `actionable` issue.
Reviewer @izaitsevfb
Why this reviewer: @izaitsevfb was the next eligible member of `autograd`'s round-robin rotation after @soulitzer, who was assigned on #560.
Semantic owner `autograd`: The resolve() method in the autograd kernel dispatcher now raises a LookupError when two registered kernels share the highest priority, changing the dispatch contract from silent tie-breaking to explicit error signaling.
Reasoning: Changed behavior: The autograd kernel resolver now raises LookupError on equal-priority conflicts rather than silently returning the first kernel, altering observable dispatch semantics. Ownership connection: The autograd owner description explicitly covers 'autograd engine behavior' and 'gradient recording and execution', and this change modifies the core kernel dispatch resolution contract. Materiality and boundary: Any caller that registers two kernels at equal priority will now receive an error instead of a result, making this a breaking behavioral change in the autograd dispatch layer.
Supporting files: `test_dir/torch/shared/autograd_dispatch.py`.
Evidence:
- `test_dir/torch/shared/autograd_dispatch.py`: This directly changes autograd kernel resolution behavior \u2014 previously a silent tie-break, now an explicit error \u2014 which is a material contract change in autograd dispatch.
+ if len(kernels) > 1 and kernels[0].priority == kernels[1].priority:
+ raise LookupError(
+ f\"ambiguous autograd kernels for {operator}: \"
+ f\"{kernels[0].kernel} and {kernels[1].kernel}\"
+ )
Planned effect: one deduplicated request for @izaitsevfb.
Reviewer @soulitzer
Why this reviewer: @soulitzer labeled a linked or related issue `actionable`.
Planned effect: one deduplicated request for @soulitzer.
```
</details>
<details>
<summary>Semantic owner already covered by a submitted review (pytorch/ciforge#633)</summary>
```
Why this PR was admitted: Admitted because a maintainer has qualifying activity on it.
Reviewer @soulitzer
Why this reviewer: @soulitzer already submitted a review covering `autograd`; no new request is needed.
Semantic owner `autograd`: Adds a new `registration_count` method to the autograd dispatcher that exposes the number of kernel registrations for a given operator, changing the public contract of the autograd dispatch tracker.
Reasoning: Changed behavior: a new public method `registration_count` is added to the autograd dispatcher class, expanding its observable API contract. Ownership connection: `autograd` owns autograd engine behavior and dispatcher internals; this change modifies the dispatcher's registration-querying interface. Materiality and boundary: the addition is not a test or doc change\u2014it introduces new callable behavior on a shared dispatcher object used across the autograd stack.
Supporting files: `test_dir/torch/shared/autograd_dispatch.py`.
Evidence:
- `test_dir/torch/shared/autograd_dispatch.py`: Directly extends the autograd dispatcher's API surface by introducing a new method that queries internal registration state, which falls squarely within autograd engine behavior and dispatcher ownership.
+ def registration_count(self, operator: str) -> int:
+ return len(self._registrations.get(operator, []))
Planned effect: no new Auto PR Triage reviewer request is needed.
```
</details>
<details>
<summary>Codepath owner from CODEOWNERS (planner run on the test fixture)</summary>
GitHub requests codepath owners itself; the explanation reports what the reviewer state showed for each one (here a pending request; with neither a request nor a review it says GitHub may not have requested them or someone may have removed the request). This is the planner's output for a fixture, so its names are synthetic:
```
Why this PR was admitted: Admitted because it fixes an issue labeled `actionable`.
Reviewer @codepath-owner
Why this reviewer: @codepath-owner is a codepath owner in CODEOWNERS and already has a pending request; no new request is needed.
Codepath owner `@codepath-owner` matched CODEOWNERS rules for: `torch/file.py`.
Planned effect: no new Auto PR Triage reviewer request is needed.
```
</details>
<details>
<summary>Admission through bypass intake (planner run on the test fixture)</summary>
Bypass intake has not run end to end yet, because no team in the pilot configuration sets `bypass_intake_criteria`. This is the planner's output for the bypass scenario in `tests/plan_fixtures.py`, so its wording is synthetic:
```
Why this PR was admitted: Admitted because `autograd`'s bypass intake "PRs that fix incorrect gradients." matched: The change fixes a gradient formula the team asked to see..
Reviewer @soulitzer
Why this reviewer: @soulitzer was picked at random from `autograd`'s roster, seeded by this PR so that reruns pick the same member.
Semantic owner `autograd`: autograd owns this changed behavior.
Reasoning: The changed behavior falls within this ownership area. The configured description names the affected contract. A review from this team covers the semantic concern.
Supporting files: `torch/semantic.py`.
Evidence:
- `torch/semantic.py`: This line implements the owned behavior.
+new semantic behavior
Matches bypass intake "PRs that fix incorrect gradients.": The change fixes a gradient formula the team asked to see.
Bypass evidence:
- `torch/semantic.py`: This line fixes the gradient the team asked to see.
+new semantic behavior
Planned effect: one deduplicated request for @soulitzer.
```
</details>
## In-depth review guide
A suggested order through the roughly 5,300 lines of non-test code, workflow, and prompt, with what each file owns and what is worth checking.
1. **`.github/workflows/auto-pr-triage.yml`** (249 lines): the trigger and the permission split.
- Three jobs. `analyze` has `contents`, `issues`, and `pull-requests` read plus `id-token: write` for the Bedrock session. `apply` and `record-error` have `pull-requests: write` and run only when `needs.analyze.outputs.mode == 'live'`; the mode comes from `AUTO_PR_TRIAGE_MODE` in the first step, so in shadow mode only `analyze` runs.
- The history step decides whether this event is the first eligible one: the first `open source` label, or the first ready-for-review after it. Compare its label and ready-event counting with the "When Auto PR Triage runs" box in the diagram.
- `${{ }}` values reach shell only through `env:`, never interpolated into a script.
2. **`.github/actions/auto-pr-triage/action.yml`** (225 lines): the analyze steps in order: Stage 1 intake, 2a ownership input, the inference-only AWS session, 2b the LLM, 2c the ownership result, 3 the plan.
- The Claude step may use only `TodoWrite` and must return JSON matching `--json-schema`. The comment above `claude_args` explains why it names TodoWrite rather than passing an empty tool list.
3. **`schemas.py`** (1,084 lines): the records passed between stages. Read the class docstrings and the comment at the top of each `__post_init__`; the rest of each body is consistency checking. The one to read closely is `ActionPlan.__post_init__`, which bounds what the write job can do: only this bot's labels; the `missing actionable issue` labels only for a PR that is not admitted, on attempt 1, and exactly as MISSING_ACTIONABLE_ISSUE_ACTIONS; every requested user justified by its request reason; no team ever requested; at most 15 users per request; and no action that can close a PR or comment.
4. **`assess_intake.py`** (658 lines): the seven gate facts, all deterministic GitHub reads. Check `verify_description_claims` (`Supported by @login` counts only for a triage-or-higher user, who is then requested; `Part of #N` only for a same-repository issue labeled `actionable`) and `fetch_maintainer_activity` (what counts as deliberate maintainer action).
5. **Ownership analysis**
- `codepath_owners.py` and `trusted_config.py`: parse the root CODEOWNERS file, the extra ownership metadata, and the rosters. Config is read at the workflow SHA, so a PR cannot change the policy applied to itself, and like GitHub the CODEOWNERS loader skips lines it cannot read (logging each one) instead of failing.
- `build_ownership_input.py` (216 lines) and `worker.md`: what the LLM sees. Check the split between `trusted_context` (resolved codepath owners, ownership metadata) and `untrusted_context` (PR text and patches, bounded by `bounded_files`), and that `worker.md` tells the model to treat `untrusted_context` as attacker-controlled data.
- `validate_ownership.py` (440 lines): `validate_result` rejects the whole LLM result on any violation: duplicate or unconfigured owners, paths absent from the PR, a bypass claim for a team without criteria or with a quote not in its criteria, a codepath-owner concern naming someone who is not a codepath owner, and evidence that is not a verbatim changed line of a supporting file (`evidence_errors`). `build_ownership_result` then discards low-confidence suggestions and those whose files had truncated patches. A rejected result becomes `llm_run_status=failed`: the PR is reported incomplete and is never marked as missing an actionable issue.
6. **`plan_actions.py`** (1,214 lines): start with `build_action_plan`, whose docstring lists exits 1-4 and maps to the diagram. Then `plan_unadmitted` (the only code that can mark a PR as missing an actionable issue), `resolve_team_owner_reviewers` and `select_team_owner_reviewers` (one reviewer per additional owner, preferring a roster member who is a codepath owner or already reviewing over a new pick), `pick_roster_member` (the seeded random pick), and `new_reviewer_requests` (drops the author and anyone already reviewing). `gather_planner_input` does every GitHub read up front into `planner_input.json`, so `build_action_plan` is pure and any plan can be replayed from that file. The `explain_*` and `log_*` functions only produce the explanations shown above and never change the plan.
7. **`reviewer_state.py`** (98 lines): the pending-request and submitted-review reads.
8. **`apply_actions.py`** (244 lines): the only code with write access. It rejects a plan for another PR, run, or attempt, checks `owner_roster` reviewers against the rosters at the workflow SHA (`check_plan_reviewers`), and executes the reviewer requests and then the labels; added labels are confirmed from the response.
9. **Tests**: `tests/test_<stage>.py` mirror the stages, and `plan_fixtures.py` and `stage_fixtures.py` build the stage records. `test_plan_actions.py` holds the planner scenarios, including the fixture examples above.
Test Plan:
```bash
python -m unittest discover -s scripts/auto_pr_triage/tests -t scripts/auto_pr_triage -b
python tools/linter/adapters/no_workflows_on_fork.py .github/workflows/auto-pr-triage.yml
UV_NO_CONFIG=1 lintrunner <changed files>
git diff --check HEAD~1 HEAD
```
Byte identity was checked by comparing each path's git tree hash with pytorch/ciforge at the source commit. With this code, PyTorch's root CODEOWNERS loads as 157 rules with no skipped lines.
Authored with an AI assistant.
Pull Request resolved: https://github.com/pytorch/pytorch/pull/195778
Approved by: https://github.com/albanD
ghstack dependencies: #195777
|
||
|
|
c4a196f867 |
Skip the hardened PR review when a PR carries 'no automated review' (#199410)
✴️ iz2: The `no automated review` label ("Request skipping the automated PR review process") exists, but the hardened review never checked it, so applying it changed nothing. With this change: - Stage 1 (`hardened-pr-review.yml`) skips the capture for a PR carrying the label, on every trigger. - Stage 2 (`hardened-pr-review-run.yml`) re-reads the labels from the API in `prepare` and treats an opted-out PR as ineligible: no review and no rows. No trigger overrides it, including a maintainer freshly adding `in progress`. The match is case-insensitive, like the other label checks. - `publish` re-reads the labels before moving them. If the opt-out arrived while the review ran, it leaves every label alone. Residual: a review already past `prepare` when the label is added still runs and still records its telemetry row. Only its label change is suppressed. A label added between publish's read and its label calls is not seen; the existing head-SHA check has the same window. Test plan: `python3 -m unittest discover -s scripts/pr_review -t .`. New tests execute the `prepare` eligibility step and the `publish` label step with opted-out and plain PRs, and pin Stage 1's literal to Stage 2's `OPT_OUT_LABEL`. Pull Request resolved: https://github.com/pytorch/pytorch/pull/199410 Approved by: https://github.com/jeanschmidt |
||
|
|
ec7e3c04d8 |
[pr-review] Flag CI runner labels that break the runner naming standard (#199315)
**Impact:** pr-review skill **Risk:** low ## What Adds `ci-runner-naming.md` to the pr-review skill. It checks every self-hosted runner label that a PR adds or changes against the runner naming standard from pytorch/ci-infra. A non-conforming label that no other CI in this repo uses gets Request Changes. A non-conforming label that other CI already uses gets a short, non-blocking heads-up. GitHub-hosted runners are exempt. ## Why New runners keep landing with labels outside the standard (legacy `linux.*` style, wrong field order, missing features and similar mistakes). Today we only catch these by hand at review time. Tooling depends on the label structure: the `rel-` marker controls access to the protected release runner group, ARC rejects names over 45 characters, and HUD queue-time queries and queue alerts rely on the `l-` segment. Putting the rule in the pr-review skill means both AI reviewers enforce it from trusted `main`. Most of the file is a procedure, not just the grammar, because the hard part is resolving the label a job actually runs on. That covers prefixes from the runner determinator or `runner_prefix:`, reusable-workflow defaults, test-matrix rows, ternaries and templates. The procedure then decides whether the label is new to this repo, so existing labels don't block unrelated PRs. The other files are wiring: - `review-checklist.md` gets a "CI Runner Labels" item that links the new file. - `pr-review-readiness/SKILL.md` links the new file and maps blocker/heads-up to `major`/`minor`, so the readiness rubric doesn't drop the finding as a "naming preference". - The new file is added to the `prompt_hash` list in `hardened-pr-review-run.yml` and to the pinned sets in `test_workflow_contract.py`. Pull Request resolved: https://github.com/pytorch/pytorch/pull/199315 Approved by: https://github.com/huydhn |
||
|
|
0022037676 |
Make pr-review-readiness a wrapper over the pr-review skill (#197331)
✴️ iz2: The hardened review pipeline applies `.claude/skills/pr-review-readiness/SKILL.md`, a standalone rubric restating six review areas. The repo's real review knowledge — TensorIterator, dispatch keys, device guards, dtype promotion, meta registration, BC rules — lives in `.claude/skills/pr-review/` and was not reaching the model at all. The rubric now reads pr-review's three files and applies them, keeping only what pr-review cannot answer here: the readiness question, the non-interactive shape (no usage modes, no `gh`/`git`, no sub-agents, no markdown template), and the mapping onto the JSON schema. `major` is pr-review's Approve boundary, so `ready for review` is applied exactly when pr-review would have recommended Approve; `minor` and `info` are reported and do not move the label. Its delegates therefore join the trusted surface. `--allowedTools` grants `Read` on `trusted/.claude/skills/pr-review/**` — without it the read is denied, and a denial reaches the model as an ordinary tool failure, so it returns a verdict with no checklist behind it. The prompt hash covers the three files, the prompt names the directory as trusted so the rubric's instruction is not mistaken for an injection attempt, and the suite's `paths:` filter covers it. The trusted checkout is at `github.sha` — the default branch, never the pull request — so this widens what the model reads, not who controls it. One clarification comes with it: the prompt told the model to ignore every instruction and configuration file under the PR tree, while the rubric sends it to read some of them as evidence. One reads as "do not open", the other as "open but do not obey". The prompt now says which. ## Test plan `python3 -m unittest discover -s scripts/pr_review -t .` — 361 tests, was 353. The eight new tests pin what the rubric points at against what the model may read, what the hash covers, and what the prompt calls trusted; each pairing fails silently on its own. `TestTheRubricIsAWrapperOverPrReview` derives the delegate set from the rubric's own links, transitively; `test_every_readable_skill_file_is_hashed` closes the same class from the grants instead, parsing no links. Three things are pinned verbatim rather than pattern-matched — the delegation sentence, the prompt's whole trust-and-security region, and the three severity definitions. That is deliberate and the docstrings say what it does and does not buy: a wording screen cannot tell "read and apply these" from "read these as optional background", and every screen tried was defeated by the next paraphrase, while one strong enough to catch them all also rejected legitimate edits. What is enforced is that these cannot be **edited or deleted** unnoticed; prose **added** beside them is left to code review. A 15-arm mutation battery passes, each arm caught by the intended test: dropping the grant, dropping a delegate from the hash, dropping the `paths:` entry, making the rubric standalone, adding an unhashed file under a granted directory, five severity-vocabulary arms, and five delegation/trust paraphrases. Exercised end to end in pytorch/ciforge, where the `in progress` label is not bot-guarded: the review session read the wrapper and then followed it into `pr-review/SKILL.md` through the new grant — visible as reads 1 and 3 in the session trace — and the verdict, findings and label move all behaved. Pull Request resolved: https://github.com/pytorch/pytorch/pull/197331 Approved by: https://github.com/georgehong, https://github.com/pytorchgreenlight, https://github.com/huydhn |
||
|
|
4dff1b8047 |
Add the hardened PR review workflows (#196845)
Two workflows split trust. `hardened-pr-review.yml` runs on `pull_request`, so it comes from the PR's own ref and is treated as attacker-controlled: it holds no secrets, gates on the review label, and does nothing but publish the request. `hardened-pr-review-run.yml` runs on `workflow_run` from the base ref, and is where every privileged step lives — `prepare` records the attempt, `review` checks out the PR and runs the model, `publish` writes the verdict and moves labels. The boundary is which AWS role each job can assume, pinned by the GitHub environment it declares. `review` assumes a role scoped to `claude-untrusted`, which can invoke Bedrock and write the trace prefix but is denied the verdict prefix; `publish` assumes the publisher role and never checks out PR code. Every provenance field on a row — repo, PR number, both SHAs, run id, trusted SHA, prompt hash — comes from `prepare` or the workflow context, not from the review. Residual risk, stated because the separation does not cover it: `publish` consumes the sanitized artifact the review job uploaded and does not re-sanitize it, so a compromised review runner can still influence the `verdict` and `summary` it records. Separately, because stage 1 ships from the PR's ref, an author can rename it, leave `workflow_run` nothing to match, and produce no rows at all — a consumer has to render "no review recorded" as its own state rather than as a pass. `test_workflow_contract.py` pins the environment/role pair, the egress allowlist, the API scopes, and the gating label. Test Plan: ``` python3 -m unittest discover -s scripts/pr_review -t . # 178 tests, OK BASE=HEAD~1 bash .github/scripts/pr-sanity-check.sh # PR SIZE is 1807 python3 .github/scripts/ensure_actions_will_cancel.py # exit 0 ``` Exercised end-to-end on pytorch/ciforge, which runs the same two workflows: same-repo and fork PRs both reach a `ready_for_human_review` verdict with the label moved, and the untrusted role is denied the verdict prefix, a non-Claude model, and the same profile from another region. Authored with Claude Code. Pull Request resolved: https://github.com/pytorch/pytorch/pull/196845 Approved by: https://github.com/huydhn, https://github.com/jeanschmidt ghstack dependencies: #196843, #196844 |
||
|
|
2bd317481d |
Add the review-time validator, row emitter, and suite CI (#196844)
`validate_findings.py` runs inside the review as a Claude Code hook, rejecting findings the sanitizer would later drop so the model can correct them while it still has the context to do so. `emit_row.py` renders an attempt as the ClickHouse telemetry row shape — two rows per attempt, so a run that dies on the runner is distinguishable from one that was never triggered. The `.claude/hooks/pr_review/` scripts wire the validator into the agent's write and stop events. `_suite_manifest.py` is a completeness guard imported by every test module: `unittest discover` runs what it finds, so deleting a module would otherwise retire its assertions and leave the run green. `pr-review-scripts-test.yml` runs the suite on any change under `scripts/pr_review/` or `.claude/hooks/pr_review/`. Test Plan: ``` python3 -m unittest discover -s scripts/pr_review -t . # 110 tests, OK BASE=HEAD~1 bash .github/scripts/pr-sanity-check.sh # PR SIZE is 1159 ``` Authored with Claude Code. Pull Request resolved: https://github.com/pytorch/pytorch/pull/196844 Approved by: https://github.com/huydhn, https://github.com/jeanschmidt ghstack dependencies: #196843 |
||
|
|
b36c355999 |
Add the PR review output sanitizer (#196843)
The hardened PR review pipeline runs a model over untrusted PR code, so its output cannot be published as-is. `extract_verdict.py` parses the model's structured output and reduces it to a publishable verdict: charset and encoded-blob checks, mention/issue/URL neutralization, and findings anchored to lines the PR actually touched. Nothing imports it yet. The validator and row emitter that call it are the next commit in this stack, and the workflows that run it are the one after. Markdown structure the model writes is deliberately not escaped here — the docstring on `neutralize` records why, and that it must be closed by whichever change first renders a summary. Test Plan: ``` python3 -m unittest discover -s scripts/pr_review -t . # 78 tests, OK BASE=HEAD~1 bash .github/scripts/pr-sanity-check.sh # PR SIZE is 1837 ``` Authored with Claude Code. Pull Request resolved: https://github.com/pytorch/pytorch/pull/196843 Approved by: https://github.com/jeanschmidt |
||
|
|
c6155b1bc8 |
[dynamo] Add offline sync check for test/cpython/v3_13 *.diff files (… (#191767)
## Summary - Add an offline check that each `test/cpython/v3_13` adapted test and its `.diff` stay in sync: reverse-apply the diff, match `sha256(pristine)` from `upstream_manifest.json` (tag/path taken from each file header), and assert `apply(pristine, diff) == adapted`. - Add `tools/regenerate_cpython_diffs.py` to fetch upstream sources, regenerate stale diffs, and refresh the manifest. - Regenerate drifted `.diff` files, add the missing source URL on `test_unittest/test_assertions.py`, and fix three headers that pointed at the wrong upstream file (`test_binop`, `test_heapq`, `test_patma`). Fixes #189607 ## Test plan - [x] `python tools/regenerate_cpython_diffs.py --check` - [ ] `python test/dynamo/test_cpython_diff_sync.py CPythonDiffSyncTests` - [ ] Confirm CI runs the new dynamo sync test Pull Request resolved: https://github.com/pytorch/pytorch/pull/191767 Approved by: https://github.com/guilhermeleobas, https://github.com/williamwen42 Co-authored-by: Guilherme Leobas <guilhermeleobas@gmail.com> |
||
|
|
0b6f26805a |
Integrate existing CODEOWNERS into review surfaces (#192572)
Human Note phase 2 mergin in exsting list of owenrs into the finer grained form Agent note > This AI-assisted change moves the existing active CODEOWNERS assignments into the grouped review-surface > taxonomy. Unresolved ownership remains commented for future assignment. > > The taxonomy checker validates active and unresolved rules in the grouped sections, supports the existing glob > patterns, and reads committed paths so generated files staged by CI do not affect coverage. Every listed > pattern must match at least one committed path. > > I compared the last matching active rule for every committed path before and after the migration. Of 21,647 > paths, 21,522 retain the exact ordered owner tuple. The 125 intentional changes repair five previously invalid > quantization rules (122 paths), update the renamed XPU getting-started document (one path), and add protected > ownership for CODEOWNERS and merge_rules.yaml (two paths). > > I also audited every active rule removed from the old layout. Thirteen matched no committed path: obsolete > Caffe2 HIP, PyTree directory, CUDA scaled-BLAS, invalid quantization, and renamed XPU documentation patterns. > The quantization and XPU patterns were corrected; the obsolete patterns were removed. The remaining removed > rule was the merge_rules.yaml assignment, which had been shadowed by the later /.github/ rule; it is now an > explicit protected rule at the end of CODEOWNERS. > > The standalone taxonomy check reports 100% coverage and zero uncovered, duplicate, stale, ineffective, or > source-order-invalid patterns. <details> <summary>Ownership validation</summary> ```text paths=21647 differences=125 unchanged exact owner tuples: 21522 corrected quantization ownership: 122 corrected XPU documentation ownership: 1 protected ownership overrides: 2 CODEOWNERS taxonomy summary Tracked paths: 21647 Covered paths: 21647 Coverage: 100.0000% Review surfaces: 55 Patterns: 1052 Paths matched by multiple patterns: 3595 Integrity checks (all must be zero) Uncovered paths: 0 Duplicate patterns: 0 Stale patterns: 0 Ineffective patterns: 0 Source-order mismatches: 0 Result: PASS ``` </details> Test Plan: ```bash export PATH="$HOME/.venvs/dev/bin:$PATH" lintrunner -a /Users/drisspg/meta/pytorch/.venv/bin/python scripts/codeowners/check_taxonomy.py python3 agent_space/compare_codeowners_with_allowed_overrides.py python3 agent_space/audit_removed_codeowner_rules.py git diff --check ``` Pull Request resolved: https://github.com/pytorch/pytorch/pull/192572 Approved by: https://github.com/albanD |
||
|
|
3b294598b3 |
Add CPython test test_bytes (#192236)
ref: https://github.com/python/cpython/blob/main/Lib/test/test_bytes.py `BaseTest.test_count`, `test_find` and `test_rfind` in `string_tests.py` are skipped under Dynamo. Each one ends with a cross-product loop over every string built from a small alphabet up to a fixed length -- 255 strings for `count`, 364 for `find`/`rfind` -- i.e. 65k and 132k iterations. Under `fullgraph` the loop bounds are constants, so Dynamo unrolls the loops and constant-folds every operation in them at trace time, roughly 0.4ms per iteration. That made these three tests take ~350s of the file's ~360s runtime, and what they cover is CPython's own string-search implementation rather than anything Dynamo-specific. They keep their expected-failure sentinels, which is what turns the `SkipTest` raised inside the compiled region into a reported skip. Test Plan: ``` PYTORCH_TEST_WITH_DYNAMO=1 python test/cpython/v3_13/test_bytes.py ``` This commit was authored with the assistance of an AI coding assistant (Claude). Pull Request resolved: https://github.com/pytorch/pytorch/pull/192236 Approved by: https://github.com/williamwen42 |
||
|
|
ab64516551 |
Add finer grain codeownership and linting (#187624)
### Summary Right now this is basically prep work. Did a basic accoutning of some general code groups in pytorch. They are all current read only (commented) and the exsting is still the source of truth. Also added the ability to make sure that our file/folder breakdowns cover all of pytorch code. Left a note about the semantics of lower in file wins. And added a lint rule that makes sure we abide by the above. After landing we can start un-commenting groups and actually adding owners / reconciling with the current form today Pull Request resolved: https://github.com/pytorch/pytorch/pull/187624 Approved by: https://github.com/albanD |
||
|
|
24d9a8d5fb |
[CD] Use compatible-release (~=) triton pins for release builds (#191941)
Release wheels should take any **patch** release of the pinned triton minor (`~=X.Y.Z`) instead of a single exact version, so a triton patch fix can ship without respinning torch. Nightly is unchanged — it keeps the exact `==<version>+git<shorthash>` pin against the nightly index. Every release-path triton pin in the tree, before this PR: | location | package | release spec | nightly spec | |---|---|---|---| | `binary_populate_env.sh:78` | `triton` | `~=` ✅ already | `==...+git` | | `binary_populate_env.sh:88` | `triton-rocm` | `~=` ✅ already | `==...+git` | | `binary_populate_env.sh:107` | `triton-xpu` | `==` ❌ | `==...+git` | | `scripts/install_triton_wheel.sh:19` | `triton` / `triton-xpu` | `==` ❌ | `==...+git` | `triton` and `triton-rocm` were already switched to `~=` in #188251; this PR covers the two that were missed. ### `scripts/install_triton_wheel.sh` The package name is now held separately from the version. The single `TRITON_VERSION="triton==$(...)"` variable was shared by both paths, and `~=` could not simply be substituted into it: PEP 440 disallows a local version segment in a compatible-release clause, so the nightly path would have produced the invalid `triton~=3.8.0+git10f6be36`. Rendered specs after the change (verified by running the script with `pip` stubbed out): ``` release/2.13, cuda : --index-url .../whl/test/ triton~=3.8.0 release/2.13, xpu : --index-url .../whl/test/ triton-xpu~=3.7.2 main, cuda : --index-url .../whl/nightly/ triton==3.8.0+git10f6be36 main, xpu : --index-url .../whl/nightly/ triton-xpu==3.7.2+git5fcc14d9 ``` `shellcheck` is clean on both files. Note `Dockerfile` declares `ARG TRITON_VERSION` (fed by `docker-release.yml`) but never installs triton with it — triton arrives transitively via `torch` — so no change is needed there. Pull Request resolved: https://github.com/pytorch/pytorch/pull/191941 Approved by: https://github.com/pytorchgreenlight, https://github.com/chuanqi129, https://github.com/huydhn |
||
|
|
835f13b7af |
Treat cancelled jobs as no-signal, not failures, in HUD analysis (#190218)
Follow-up to #190069. analyze_failing_jobs.py still counted "cancelled" as a failure: is_skipped() did not recognize it, and is_failure() flags any conclusion that is not "success"/skipped. But a cancelled run is a superseded/aborted run (e.g. the workflow's concurrency cancel-in-progress when a newer commit lands), not a real result. The concrete symptom: `Create Release / Upload source code to S3 for release tags` is skipped on every real run (it only runs on rc tags), so its only non-skipped datapoints were cancelled runs -- which produced a phantom failure streak with no captured error text ("unclassified"). Adding "cancelled" to SKIPPED_CONCLUSIONS makes such runs no-data, matching the existing handling of queued/in_progress. Test Plan: ``` lintrunner scripts/hud/analyze_failing_jobs.py # ok, no lint issues ``` Authored with assistance from Claude. Pull Request resolved: https://github.com/pytorch/pytorch/pull/190218 Approved by: https://github.com/zou3519 |
||
|
|
6c4cc2ed0b |
Treat queued/in_progress jobs as in-flight, not failures, in HUD analysis (#190069)
analyze_failing_jobs.py counted any conclusion that was not "success"/"pending" as a failure. The HUD reports runner-queue-backlogged jobs with conclusion "queued", which fell through that gap: is_skipped() only recognized "pending" as in-flight, so a queued run was classified as a failure. A job stuck in the runner queue (e.g. trunk / macos-py3-arm64 / build with ~19 commits queued on a backed-up arm64 pool) then looked like a long failure streak even though every completed run was green. Add "queued" and "in_progress" to SKIPPED_CONCLUSIONS and derive is_failure() from is_skipped() so the two helpers share a single source of truth and cannot drift apart again. Test Plan: ``` # Previously reported as failing; now correctly empty: python scripts/hud/analyze_failing_jobs.py main --commits 100 \ --filter-job-name "trunk / macos-py3-arm64 / build" # -> Found 0 continuously failing jobs # Genuine failures are still detected: python scripts/hud/analyze_failing_jobs.py main --commits 100 \ --filter-job-name ".*backwards_compat.*" # -> still reports the broken backwards_compat jobs ``` Authored with Claude. Pull Request resolved: https://github.com/pytorch/pytorch/pull/190069 Approved by: https://github.com/Skylion007 |
||
|
|
337d62bdf0 |
Attribute pinned failure-streak starts to their commit/PR in HUD tool (#188842)
When a job's failure streak start is pinned within the window (not clipped),
surface the suspected culprit: the commit/PR the streak began at. The HUD
already returns per-commit time, PR number, and title in each row, so no extra
API calls are needed -- build a sha->commit map from the pages already fetched
and resolve the failing-since commit.
The text report prints the date, PR URL, and title under "failing since"; the
CSV gains failing_since_date, failing_since_pr_url, and failing_since_pr_title
columns. These are populated only when the start is pinned; clipped streaks
(true start unknown, possibly older than the window) leave them blank.
Test Plan:
HUD_INTERNAL_BOT_TOKEN=<token> python scripts/hud/analyze_failing_jobs.py --commits 500 --csv > out.csv
Confirmed pinned rows carry the culprit PR date/url/title (e.g. #188107, dated
2026-06-28) while clipped rows leave those columns blank.
lintrunner scripts/hud/analyze_failing_jobs.py
Authored with Claude.
Pull Request resolved: https://github.com/pytorch/pytorch/pull/188842
Approved by: https://github.com/zou3519
|
||
|
|
089472d452 |
Strip +PTX from CUDA arch list on release/RC builds (in build_env_setup.py) (#188914)
## Summary Restore the pre-2.13 behavior where **release/RC** CUDA wheels ship **without `+PTX`**, while nightlies keep it for forward-compat. ### Background `libtorch_cuda.so` grew ~80-90 MB from 2.12.1 to 2.13.0 (cu130): 2.13 started emitting `12.0+PTX` (sm_120 PTX) in the release wheels. The SASS arch set is unchanged; the growth is the embedded compute_120 PTX. The branch-cut script `scripts/release/apply-release-changes.sh` was *supposed* to strip `+PTX` for releases, but it edited `.ci/manywheel/build_cuda.sh` — which was refactored into `.ci/manywheel/build_env_setup.py`. The `sed` silently became a no-op, so 2.13.0 shipped with `+PTX`. ### Fix Detect release/RC builds in Python instead of relying on a branch-cut `sed`: - `build_env_setup.py`: add `_is_release_build()` (true when `PYTORCH_BUILD_VERSION` is set and has no `.dev` suffix — i.e. tagged release/RC builds, per `binary_populate_env.sh`) and `_ptx_arches()`; emit `+PTX` only for nightly/dev builds. - `apply-release-changes.sh`: drop the now-redundant/broken `+PTX` `sed`. ### Behavior (cuda 13.0, x86_64) | Build | `PYTORCH_BUILD_VERSION` | arch list | |---|---|---| | nightly | `2.13.0.dev...+cu130` | `7.5;8.0;8.6;9.0;10.0;12.0+PTX` | | RC / release | `2.13.0+cu130` | `7.5;8.0;8.6;9.0;10.0;12.0` | | local / unset | `''` | `...;12.0+PTX` (safe default) | This is drift-proof: the logic lives next to the arch table it depends on, keyed off a signal the repo already uses. *Authored with AI assistance.* Pull Request resolved: https://github.com/pytorch/pytorch/pull/188914 Approved by: https://github.com/zxiiro, https://github.com/huydhn |
||
|
|
2c4251f06e |
Title: [BE] Enable ruff FURB122 lint rule and fix violations (#187984)
Body: ## Summary Enables the ruff `FURB122` rule (prefer `f.writelines(...)` over a for-loop of `f.write(...)`) by fixing all existing violations and removing `FURB122` from the `[tool.ruff.lint] ignore` list in `pyproject.toml`. Most violations were fixed with ruff's autofix. The one manual change is in `caffe2/perfkernels/hp_emblookup_codegen.py`, where an inline comment in the loop body blocked the autofix; that file is excluded from CI lint, so the edit is kept minimal with no reformatting. Non-user-facing, mechanical cleanup. No behavior change. ## Test plan `FURB122` reports no violations across the repo: \`\`\` ruff check . --select FURB122 \`\`\` Changed files are format-clean under the CI-pinned ruff 0.14.4: \`\`\` lintrunner --skip PYREFLY # ok, no lint issues \`\`\` Pull Request resolved: https://github.com/pytorch/pytorch/pull/187984 Approved by: https://github.com/Skylion007 |
||
|
|
bfeaf39d26 |
Treat in-progress (pending) HUD runs as no-data in failure streaks (#188562)
analyze_failing_jobs.py computes a job's current failure streak by walking its
results newest-to-oldest. A "pending" (in-progress) run was classified as
neither a failure nor no-data, so it hit the streak-terminating branch and
reset the streak to 0. Jobs that almost always have an in-flight rerun at the
tip -- common for flaky ROCm jobs -- were therefore dropped by the default
--filter-min-streak even when every completed run in the window had failed.
Classify "pending" as no-data (add it to SKIPPED_CONCLUSIONS) so the streak
walk looks past in-flight runs to the last completed result, and a rerun in
progress no longer masks a consistently failing job. is_failure already
excluded "pending", so this only affects the skip path.
Test Plan:
Before the fix, this consistently-failing job was missing from the report
because its newest run was pending; after, it is reported with streak 42:
HUD_INTERNAL_BOT_TOKEN=<token> python scripts/hud/analyze_failing_jobs.py \
--commits 200 --filter-job-name ".*navi31.*test \(default, 1, 2.*"
lintrunner -a scripts/hud/analyze_failing_jobs.py
Authored with Claude.
Pull Request resolved: https://github.com/pytorch/pytorch/pull/188562
Approved by: https://github.com/zou3519
|
||
|
|
f497ab2c3c |
Revert "Title: [BE] Enable ruff FURB122 lint rule and fix violations (#187984)"
This reverts commit
|
||
|
|
404f8ed429 |
Title: [BE] Enable ruff FURB122 lint rule and fix violations (#187984)
Body: ## Summary Enables the ruff `FURB122` rule (prefer `f.writelines(...)` over a for-loop of `f.write(...)`) by fixing all existing violations and removing `FURB122` from the `[tool.ruff.lint] ignore` list in `pyproject.toml`. Most violations were fixed with ruff's autofix. The one manual change is in `caffe2/perfkernels/hp_emblookup_codegen.py`, where an inline comment in the loop body blocked the autofix; that file is excluded from CI lint, so the edit is kept minimal with no reformatting. Non-user-facing, mechanical cleanup. No behavior change. ## Test plan `FURB122` reports no violations across the repo: \`\`\` ruff check . --select FURB122 \`\`\` Changed files are format-clean under the CI-pinned ruff 0.14.4: \`\`\` lintrunner --skip PYREFLY # ok, no lint issues \`\`\` Pull Request resolved: https://github.com/pytorch/pytorch/pull/187984 Approved by: https://github.com/Skylion007 |
||
|
|
dab0373600 |
Add HUD analysis tool for continuously failing CI jobs (#188479)
A standalone script for the pt2 oncall to find jobs currently on a failure
streak from the data backing hud.pytorch.org. It pulls the per-commit job grid
for a branch or commit and reports, per job, the current streak length, total
runs in the window, the commit the streak started at, and the distinct failure
signatures.
The HUD sits behind a Vercel WAF that 429s anonymous traffic, so requests send
an x-hud-internal-bot bypass token read from the HUD_INTERNAL_BOT_TOKEN env
var. Results come from a three-state per-(job, commit) model -- failure,
success/pending, or no-data -- so streaks skip commits a job did not run on,
and only jobs still active at the newest commit are reported (stale removed
jobs are dropped). A streak is flagged as possibly predating the window when no
passing run was observed and the job still has data at the oldest fetched
commit, in which case the tool suggests a larger --commits.
Test Plan:
HUD_INTERNAL_BOT_TOKEN=<token> python scripts/hud/analyze_failing_jobs.py --commits 100
HUD_INTERNAL_BOT_TOKEN=<token> python scripts/hud/analyze_failing_jobs.py --commits 200 --filter-job-name ".*windows-arm64.*"
HUD_INTERNAL_BOT_TOKEN=<token> python scripts/hud/analyze_failing_jobs.py --commits 1000 --csv > /tmp/out.csv
lintrunner -a scripts/hud/analyze_failing_jobs.py
Authored with Claude.
Pull Request resolved: https://github.com/pytorch/pytorch/pull/188479
Approved by: https://github.com/zou3519
|
||
|
|
bf4f52f112 |
[BE][MPS] Call function directly (#188414)
Rather than doing `subprocess.run` Small followup after https://github.com/pytorch/pytorch/pull/187087 Pull Request resolved: https://github.com/pytorch/pytorch/pull/188414 Approved by: https://github.com/Skylion007 |
||
|
|
47edb83c87 |
Fix URL lint false-FAIL on bot-protected hosts (hud.pytorch.org) (#188518)
## Summary `scripts/lint_urls.sh` hard-fails on URLs pointing at `https://hud.pytorch.org` (and any other live host behind an anti-bot WAF), even though the site is reachable for real users. See #188510. ## Root cause The linter already tolerates WAF responses — `403/429/503` are printed as `WARN` (pass), not `FAIL`. The problem is a gap in the **check-host.net fallback**, the external prober used when the runner's own `curl` can't get a status: ```bash new_code=$(... check-host.net/check-result/$request_id | jq -r -e '.[][0][3]') || new_code=000 [[ "$new_code" =~ ^[0-9]+$ ]] || new_code=000 if [ "$new_code" -ge 200 ] && [ "$new_code" -lt 400 ]; then # <-- only 2xx/3xx adopted code=$new_code break fi ``` On the Meta-hosted lint runner the two direct `curl` attempts to `hud.pytorch.org` return `http_code 000` (no status captured). The check-host.net fallback then **does** observe the real status — `429` from the Vercel WAF — but the acceptance condition only adopts `2xx/3xx`, so the `429` is thrown away, `code` stays `000`, and the URL falls through to the hard `FAIL` branch. Verified: - CI log of the failing run (`Link checks / lint-urls`, job 84156364669): `FAIL 000 https://hud.pytorch.org`. - The exact check-host.net invocation this script uses (`us3.node.check-host.net`, `max_nodes=1`) returns `429` on the first poll for both `https://hud.pytorch.org` and `https://hud.pytorch.org/api/hud/pytorch/pytorch` — i.e. the fallback already has the information needed to WARN-pass; it was just being discarded. ## Fix Adopt **any real HTTP status (100–599)** reported by check-host.net and let the existing `WARN`/`FAIL`/`OK` logic below classify it. `000` remains the "no result yet / unreachable" sentinel that keeps the poll loop going, so genuinely dead links still `FAIL` as before. This also makes diagnostics more accurate: a check-host-observed `404`/`500` now surfaces as `FAIL 404`/`FAIL 500` instead of a misleading `FAIL 000`. ## Test plan - `bash -n scripts/lint_urls.sh` passes. - A URL behind a WAF that returns `403/429/503` via check-host.net now reports `WARN` instead of `FAIL`. - A genuinely broken URL (no HTTP response anywhere → `000`) still reports `FAIL`. - The `Link checks / lint-urls` job on this PR exercises the path (this PR touches a checked file). Pull Request resolved: https://github.com/pytorch/pytorch/pull/188518 Approved by: https://github.com/malfet, https://github.com/huydhn |
||
|
|
1fa5c7aa70 |
[release_notes] Map sub-area labels to per-area worksheets (#188453)
Improve how scripts/release_notes assigns `release notes:` labels to worksheet files in three related ways: - Distributed: previously every `distributed (...)` sub-area label was collapsed into a single `distributed` category, so the generator produced one combined result_distributed.md. Keep each sub-area label as its own category so a separate `result_distributed (<subarea>).md` is emitted per area (c10d, dtensor, fsdp, fsdp2, checkpoint, pipeline, torchelastic, symm_mem, ddp, composable, rpc, sharded). Done by appending `distributed.categories` to the master category list and dropping the collapse branch in `category_remapper`. - Optimizer: `optim` and `optimizer` are duplicate labels for the same area. Add an `optim` CategoryGroup mapping both to `optim`, remove the stale `optimizer` frontend category, and point the `[optimizer]` title heuristic at `optim`. - AOTDispatcher: `aot autograd` and `aotdispatcher` name the same subsystem; add a CategoryGroup mapping both to `aotdispatcher`. The resulting `common.categories` contains no duplicates and drops no previously-handled category; other areas are unaffected. Pull Request resolved: https://github.com/pytorch/pytorch/pull/188453 Approved by: https://github.com/atalman |
||
|
|
48b24f8a8b |
[mps] Add dependencies for metallib headers in CMake (#187087)
Add dependencies between `*_metallib.h` generated headers and the corresponding `*.metal` files, so that changes trigger a correct rebuild. Brought to you by a morning trying to figure out why my new kernel wasn't callable when tested. Pull Request resolved: https://github.com/pytorch/pytorch/pull/187087 Approved by: https://github.com/kurtamohler, https://github.com/malfet |