Skip to content

feat(skills): add analyze-upstream-pr to judge an upstream PR for an AMD deployment - #1851

Merged
chaojhou merged 40 commits into
AMD-AGI:mainfrom
okorzh-amd:okorzh/skill-analyze-upstream-pr
Oct 22, 2026
Merged

chaojhou merged 40 commits into
AMD-AGI:mainfrom
okorzh-amd:okorzh/skill-analyze-upstream-pr

Conversation

@okorzh-amd

@okorzh-amd okorzh-amd commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor
  • Description: what and why

    Someone who finds an upstream PR (a framework or kernel-library change) has three questions before taking it up for an AMD deployment: does my deployment run the code it changes on my GPU architecture and dtype; is the change already in the tree I measure, through this PR or another one; and should I take it now, carry it as a patch, wait, or drop it. Nothing in this repo answers them. review-pr is built for this repo's own PRs, and check-prior-art answers a different question ("is my planned change already upstream?"). An upstream PR is reviewed and measured on its author's platform, so a reader tends to take its benchmark, its green CI and "merged" as facts about their own deployment.

    What this PR changes: three files, all Markdown.

    • .claude/skills/analyze-upstream-pr/SKILL.md (new, 280 lines): the procedure.
    • .claude/skills/analyze-upstream-pr/references/dimensions.md (new, 91 lines): the six risk lenses.
    • AGENTS.md: one row in the "Where things are documented" table.

    It adds no script and no test, and it changes none of check-prior-art's files. The scripts this skill runs (read.sh, its lib.sh, and the inclusion probe) belong to feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream #1850 and feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847 and keep the output they have there.

    Like review-pr and check-prior-art, the skill is written for an agent to run top to bottom. Every step writes an artifact into a work directory. A step that cannot run is recorded as SKIPPED and carried into the report, and every claim is refuted once before the report is written. The skill reads GitHub and never posts to it, and it never starts a server, a benchmark or a GPU job. It reuses check-prior-art for collection, for the inclusion probe and for the search for other PRs, and it cites that skill's gotchas (G3-G7).

    Inputs. target.txt holds:

    • the PR's repository and number;
    • the target GPU architecture (for example gfx942 or gfx950), dtypes and quantization method;
    • the deployment: model, parallelism, concurrency, sequence lengths, server flags and environment;
    • a local tree of the PR's repository (or a fork) and the commit the deployment runs;
    • selection: a second tree and commit for the code that selects the changed code when the PR's tree does not hold it, such as the framework that calls a kernel library's op;
    • the local patches on top.

    Step 0 asks the user for whatever is missing before collecting: a deployment with no model or flags, a tree, and, for a kernel-library PR, the selection tree. Without a tree, the on-path, inclusion and apply checks are SKIPPED.

    Collection (SKILL.md lines 89-94) is check-prior-art's read.sh plus two gh commands, and a third when GitHub refuses the diff:

    • read.sh writes four files:
      • pr.json: number, title, state, isDraft, mergedAt, mergeCommit (null until the PR merges), changedFiles, body, comments, reviews and url;
      • files.txt: every changed path with its status and line counts;
      • diff.txt, or a SKIPPED: diff line when GitHub will not render the diff;
      • xrefs.tsv: every PR that links to this one, with its state and title.
    • gh pr view --json headRefOid,baseRefName,statusCheckRollup writes checks.json: the head sha, the base branch and every check at the head.
    • gh api --paginate .../pulls/<N>/comments --jq ... writes inline.jsonl, one JSON object per inline review comment, with its author association. gh pr view has no field for inline review comments, so pr.json lacks them.
    • Only when diff.txt is a SKIPPED: diff line, gh api --paginate .../pulls/<N>/files --jq ... writes hunks.txt, each file's hunks. GitHub refuses a diff past 300 files, but its files endpoint still lists up to 3,000 files with each one's patch (none for a binary file). So hunks.txt stands in wherever the diff is read, though not where it is applied.

    Comments stay JSON. An untrusted-input section treats every title, body, comment, diff and hunk as data. It reads a comment's authorAssociation as whose word the comment is, not as proof that it holds.

    Steps, one artifact each:

    1. mechanism.txt: two sentences from the diff, naming path:symbol. Then the step reads xrefs.tsv and the comments:
      • A merged PR titled as a revert of this one is a revert.
      • A PR that either PR's title, body or comments call this one's replacement or reland is a successor. check-prior-art's SUPERSEDED class likewise rests on a named replacement.
      • A PR stacked on this one or following it up is not a successor.
      • An open or merged successor is recorded as SUCCESSOR: <ref> <state>. The agent asks the user about it, and it goes to Step 9.
    2. operating_point.txt: hardware, model, dtype, batch, lengths, parallelism, flags and reported results. Each is a verbatim quote with its source, or NOT STATED. Then one GAP line for each dimension that differs from the target.
    3. on_path.txt: each changed source path starts UNKNOWN. It moves to ON-PATH or OFF-PATH only on a cited selection site at the target commit: platform, arch, capability and dtype checks, backend registries, environment reads, tuning tables. The walk uses the selection tree when that tree holds the site.
    4. inclusion.txt: check-prior-art's Step 5 snippet runs once for the PR and once for each merged revert, with the merge commit from pr.json.
      • The probe exits 0 for included and 3 for unknown.
      • Exit 1 (a crash) or 2 (a usage error) is no answer. It becomes SKIPPED: inclusion with the probe's error.
      • unknown means nothing in local git showed the PR. It never means the PR is absent.
    5. effect.txt: the metric, the direction at the target and why. It is always labelled predicted. The author's numbers reach the report as quoted.
    6. conflicts.txt:
      • Other PRs. check-prior-art runs through its verdict (its Steps 0 to 7) in its own work directory, $WORK/prior-art, with this PR's mechanism, tree, commit and changed paths. Its search and path scan also find the PR under analysis, and any PR stacked on it. One sentence tells the agent to keep those out of the shortlist and classes it writes. The verdict line goes into conflicts.txt. When the verdict says -- incomplete, check-prior-art's Coverage goes into the report.
      • The diff at the target. git apply --check of diff.txt runs every time, in a scratch worktree at the target commit.
        • The result is APPLIES, or FAILS with the first failing hunk.
        • After a failure, a diff that applies with --reverse means the tree already has the change. That settles an unknown inclusion as included.
        • Each local patch is then applied in the worktree and the diff is checked again. A failure that only appears after a patch goes through the same --reverse check. If the diff applies backwards, the line is local:<patch> REVERSE: the patch already carries the PR, and inclusion becomes included. A failure in both directions is a CONFLICT, as for a patch that carries an older head of the PR. Any other failure is drift.
        • GitHub abbreviates a binary file's index, so a Binary files ... differ stanza never applies. Every check therefore passes $BIN, an --exclude for each binary stanza's old and new path, and records each such path as SKIPPED: apply -- <path>: binary.
        • The worktree is removed afterwards. Nothing is applied in the user's tree.
    7. risks.txt: one line per lens in references/dimensions.md.
    8. measure.md (optional): a plan the user runs, never the skill. It has:
      • two arms that differ by the PR alone;
      • the control arm run twice, for the noise floor;
      • accuracy checked before timing when numerics change;
      • the operating point;
      • a decision rule written before the first run;
      • proof that the changed code ran;
      • results labelled measured.
    9. refutations.txt: one FINDING / ATTEMPT / OUTCOME block for each of these: risk, on-path line, gap, unknown inclusion, effect, check-prior-art verdict, conflict, merged revert and successor.
      • An unknown inclusion is attacked in the tree's own history, as check-prior-art's Step 6 does.
      • A successor is attacked in both PRs' bodies, because a stacked or follow-up PR does not replace this one.
    10. report.md: the recommendation, the mechanism, the evidence rows, Coverage and the next step, plus the head sha and base branch from checks.json. Each evidence row is labelled verified, quoted, predicted, measured or inferred.

    Recommendation (SKILL.md lines 25-39). One table with ten rows. The first row that applies wins, the last row takes every case left, and each row carries its next step:

    1. NOT-APPLICABLE -- reverted by <ref>: a merged revert survived refutation.
      • It adds , relanded in <ref> when there is a merged reland, and the next step is to analyze that reland.
      • When the tree carries the PR and not its revert, the next step says so: the tree runs a change that upstream backed out.
    2. NOT-APPLICABLE -- already in tree: the probe or the reverse apply found the PR. It reads -- already in tree via <patch> when the reverse apply after a local patch found it. It reads -- already in tree via <ref> when check-prior-art's verdict is EXISTS-MERGED -- in your tree or EXISTS-OPEN -- in your tree, because then the tree already runs the change through another PR.
    3. NOT-APPLICABLE -- merged as <ref>: the PR is not merged, and check-prior-art's verdict is EXISTS-MERGED -- inclusion unchecked. The next step is to analyze <ref> instead.
    4. NOT-APPLICABLE -- superseded by <ref>: the PR is not merged and either:
      • an open or merged successor survived refutation; or
      • the PR closed unmerged and check-prior-art's verdict is EXISTS-OPEN -- inclusion unchecked.
    5. NOT-APPLICABLE -- off path: <condition>: every changed path is OFF-PATH.
    6. NEEDS-MEASUREMENT: an on-path line the effect rests on stayed UNKNOWN, or a blocking risk that only a run can settle survived.
    7. WAIT-FOR-MERGE -- <risk>: the PR is open and upstream is settling a surviving risk. That risk is changes requested, an open design question, a check failing at the head, or a competing open PR (check-prior-art's EXISTS-OPEN -- inclusion unchecked).
    8. ADOPT-NOW: merged, on path, no blocking risk survived. It adds -- inclusion unchecked when the probe answered unknown or was SKIPPED.
    9. CARRY-AS-PATCH: open, or closed unmerged with no surviving successor; on path; no blocking risk survived. It adds -- conflicts with <patch> from Step 6.
    10. NOT-APPLICABLE -- <lens>: <condition>: everything else, such as a confirmed blocking risk.

    ADOPT-NOW and CARRY-AS-PATCH also add -- does not apply at <commit> when the apply check failed. Their next step then starts by resolving the hunk it names.

    Risk lenses. references/dimensions.md holds six lenses. Each is a way a change that is right on its author's platform turns out wrong, slower or inert on a ROCm deployment. What the change does, which paths reach it and whether it is in the tree are covered by Steps 1 to 4. Each lens says where to look, what makes it a risk and when it stays clear. Like review-pr's rules, each cites the public upstream PR it was learned from:

    Dependency. This PR depends on feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream #1850 ("feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream"). This skill uses that PR's read.sh, its Step 5 snippet, its procedure through the verdict, and its gotchas. feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream #1850 in turn depends on feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847, the inclusion probe. The kernel-names refactor that feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847 used to stack on has landed in main as refactor(kb_store): move derive_kernel_names into kb_store.identity.kernel_names #2028. This PR no longer carries that refactor.

    Every PR in the stack targets main. Until feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847 and feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream #1850 merge, this diff also carries their commits: 10 from feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847, 16 from feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream #1850 and 14 from this PR. Against main that is 17 files, +1967/-20. feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream #1850's head 75d77c943 is an ancestor of this head, and git diff 75d77c943 6cc661136 touches only the three files above (+372/-0).

  • Linked issue(s): none.

  • Tests: added/updated? commands run? (what to test; if a test was replaced, where its assertions live now):

    No test is added, because this PR adds no script and no Python: its three files are Markdown. The collection it runs is check-prior-art's read.sh, which scripts/tests/test_check_prior_art.py in feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream #1850 pins, plus three gh commands and one awk line written out in SKILL.md. read.sh keeps comments as the JSON that gh pr view returns, and inline.jsonl is JSON too, so there is no rendered comment format for a test to pin.

    scripts/check_cli_references.py checks the python -m modules and src/ script paths in every SKILL.md. This one names neither, so nothing checks its read.sh path or its gh and awk commands automatically. They were checked by running them, below.

    Commands run at head 6cc661136 (main 7ac8c1229):

    • pytest scripts/tests/test_check_prior_art.py scripts/tests/test_check_cli_references.py src/kernelforge/tests/knowledge/pr_knowledge/test_inclusion.py: 47 passed (10 + 4 + 33).
    • pytest -n 16 scripts/tests: 742 tests, 738 passed. Main has 732, 728 passed. The 10 extra tests are test_check_prior_art.py from feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream #1850. The same 4 test_code_metrics.py fork-comment tests fail on both, from the host: a package.json with "type": "module" above the pytest temp directory makes Node.js load their harness.js as an ES module (ReferenceError: require is not defined).
    • pre-commit run --files on the three files passed these hooks: trailing whitespace, end of file, merge conflicts, large files, private key, line endings, reuse, gitleaks, codespell and check-cli-references.
    • python scripts/check_cli_references.py: exit 0.
    • lint-imports: 10 kept, 0 broken.
    • python scripts/code_metrics.py:
    • ruff check, ruff format --check and scripts/check_broad_suppress.py on the 7 Python files the stacked PRs change: clean. This PR changes no Python file.

    Live, read-only run against public vLLM PRs, at 88bd0be46; the last commit changes only how Step 6 reads a failure after a local patch. The tree was a local vLLM fork at a9b39d6dcb, a commit from 2026-08-05, and the probe ran from this source tree.

    • Step 0 on [Bugfix][ROCm] Alias SparseAttnIndexerKpool.forward_cuda to forward_native (GLM-5.3-Flash boot crash) vllm-project/vllm#57425:
      • It wrote pr.json, files.txt, diff.txt and xrefs.tsv.
      • It wrote checks.json with the head sha, base main and 87 checks.
      • It wrote inline.jsonl with the 3 inline review comments, which pr.json does not carry.
    • gh pr view --json statusCheckRollup returned the same number of check contexts as GraphQL's totalCount for six PRs. They had 10 to 254 contexts each; #50243's 254 is more than one GraphQL page of 100.
    • Step 0 on [Misc] Add SPDX-License-Identifier headers to python source files vllm-project/vllm#12628, which changes 1012 files: read.sh wrote SKIPPED: diff (GitHub's HTTP 406 past 300 files), and the hunks.txt command wrote hunks for all 1012 paths, exit 0. On #50243, whose diff GitHub renders, the command wrote nothing and exited 0.
    • Step 4's snippet:
      • #51131, which the tree carries: included, method subject, exit 0.
      • #46730 and #57425, which the tree does not carry: unknown, method none, exit 3.
    • Step 6's apply check, run as written in SKILL.md:
      • #51131: FAILS forward and applies with --reverse, so the tree has it.
      • #46730: FAILS both forward and with --reverse, because the tree's copy of fused_indexer_q.py differs from the PR's base. The qualifier is -- does not apply at a9b39d6dcb.
      • #57425: FAILS, because its path is absent at that commit.
      • #50243, which changes a PNG. Without $BIN, the diff fails at the PR's parent, on the binary stanza alone ("cannot apply binary patch ... without full index line"). With $BIN, it applies at the parent, and applies with --reverse at the merge commit.
      • A scratch repository with binary files added, deleted, modified, and renamed with a change: with $BIN, the diff applies forward at the parent and with --reverse at the child. Excluding only the new name fails the reverse check on the rename.
      • A scratch repository whose local patch is the PR's own diff, at 6cc661136: the diff applies at the commit and the patch is OVERLAP. After the patch, the forward check fails with "patch failed: m.py:1" and the --reverse check applies, so the line is local:<patch> REVERSE, not CONFLICT. With a patch that changes the same line differently, both directions fail: CONFLICT.
      • Afterwards the scratch worktree was removed, and the tree's worktree list no longer named it.
    • check-prior-art's search.sh, run with two terms for #57425's mechanism (SparseAttnIndexerKpool forward_native and forward_cuda forward_native ROCm), listed #57425 itself among its 18 candidates. That is the case the Step 6 exclusion sentence covers.
  • Size/complexity triggers crossed: split, or why the shape is right: none.

  • If this simplifies or refactors: which mechanism was removed, which contract was preserved, and the tests that show it: not a refactor; a new skill.

  • Observable effect: what an operator will now see, and what the old behaviour cost them (if none, why the change is unobservable):

    • The runtime, the CLI and the shipped package do not change. check-prior-art's scripts and their artifacts do not change either.
    • A contributor working in this repository with Claude Code now has an analyze-upstream-pr skill, listed in AGENTS.md. Given an upstream PR and a target deployment, it writes a work directory that ends in report.md, with one of the ten recommendations and its next step.
    • These are the effects the recommendation rows decide:
      • A PR whose change upstream merged through another PR is reported NOT-APPLICABLE -- merged as <ref>, with "analyze <ref> instead". It is not reported CARRY-AS-PATCH.
      • A PR with a surviving replacement or reland, or one that closed unmerged while an equivalent PR is open, is reported NOT-APPLICABLE -- superseded by <ref>.
      • A tree that already runs another PR doing the same thing gets NOT-APPLICABLE -- already in tree via <ref>.
      • A deployment that already carries the PR as a local patch gets NOT-APPLICABLE -- already in tree via <patch>. It is not told the PR conflicts with its own patch and to apply diff.txt a second time. CARRY-AS-PATCH creates such a patch, so a rerun after it reaches this case.
      • A PR whose every changed path is excluded at the target by a cited selection site is reported NOT-APPLICABLE -- off path: <condition>.
      • An open PR competing with another open one waits (WAIT-FOR-MERGE).
      • When the PR's diff does not apply at the target commit, ADOPT-NOW and CARRY-AS-PATCH say so (-- does not apply at <commit>). They no longer tell the user to apply a diff that fails.
    • A PR that changes a binary file is judged by its text hunks, and each binary path is listed as SKIPPED: apply. Without the exclusion, git apply would read every such PR as -- does not apply, and the reverse check could not show that the tree already has it.
    • A PR past GitHub's 300-file diff limit still has its mechanism, effect and risks read from each file's hunks. The apply check is then SKIPPED, which the report's Coverage shows.
    • An open PR is not reported superseded by a PR stacked on it or following it up (Step 1). A PR stacked on it does not hold it at WAIT-FOR-MERGE either, because Step 6 keeps that PR out of check-prior-art's shortlist and classes. An example is [ROCm][Perf][Qwen3.8] Mono decode layer for Qwen3.8 on MI355X vllm-project/vllm#60833 and #61271, a draft stacked on it.
    • A probe that crashes or is misused is reported as SKIPPED: inclusion with its error. It is not read as an answer.
    • Step 0 asks for the tree, and for a kernel-library PR the framework tree, before collecting. Without that, the report would end with every on-path line UNKNOWN.
    • Costs:
      • Step 6 runs check-prior-art's full keyword search and open-PR path scan, which takes minutes on a large upstream repository.
      • Step 6 registers a temporary worktree in the user's tree (git worktree add, then git worktree remove).
      • The skill needs jq and gh, and gh must be authenticated because check-prior-art's path scan uses GraphQL.
    • Before this change, the analysis was manual and its evidence went unrecorded. A green upstream CI, an author's benchmark or "merged" could be taken as facts about an AMD deployment that never ran the changed code, or that already had the change.
  • Breaking changes: no. The PR only adds files and one AGENTS.md row. check-prior-art's read.sh, lib.sh and their outputs are unchanged.

  • PR addresses single concern: yes. The skill is one capability. The other commits in the diff belong to the stacked PRs beneath it, feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847 and feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream #1850.

  • Root cause is upstream (Magpie/TraceLens/GEAK/IntelliKit/AgentKernelArena), ticket filed: no; not applicable.

@okorzh-amd
okorzh-amd requested a review from a team as a code owner October 13, 2026 06:03
@rpoornac
rpoornac requested a review from chaojhou October 13, 2026 19:05
@okorzh-amd
okorzh-amd force-pushed the okorzh/skill-analyze-upstream-pr branch from 0383dc7 to d74f164 Compare October 13, 2026 19:38

@chaojhou chaojhou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks @okorzh-amd. The scripts are sound:

  • References parsed from comments are validated before any gh api call.
  • The header-forgery normalisation matches the description.
  • The probe gets its arguments as an array.

One must-fix, in the procedure:

Step 6 never excludes the PR under analysis from the check-prior-art run.

  • analyze-upstream-pr/SKILL.md:208-210 sends the open-PR question to check-prior-art, with files.txt as the planned paths, and carries back its DUPLICATE and OVERLAPPING classes.
  • overlap.sh has no exclusion, and check-prior-art defines DUPLICATE as "does what the mechanism says, in the same code path".
  • For an open PR, the PR itself is an overlap.tsv row that matches every planned path. Followed literally, it comes back as its own DUPLICATE, and the WAIT-FOR-MERGE row ("a competing open PR") can fire instead of CARRY-AS-PATCH.
  • review-pr's fetch.sh drops the PR itself ($1 == self), and that is the check this step replaces.
  • Please say in Step 6 to leave <owner/name>#<N> out of the shortlist and the classes, or give overlap.sh an exclusion.

This PR also contains #1847, whose must-fix applies here too.

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Gate job conclusion: success (run) on 6cc661136dcc50d35990983c06ef2247c4f2b5c6, as reported by GitHub.

The report below was uploaded by that run; the conclusion above is the result.


Code metrics gate: PASSED

Judged: the 17 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.

Dimension Limit New Worse Baseline out of date Baseline entries vs base
Cyclomatic complexity fails > 20 0 0 0 59 +0
Cognitive complexity fails > 30 0 0 0 245 +0
Function length (lines) fails > 80 0 0 0 390 +0
Nested block depth fails > 5 0 0 0 24 +0
Parameter count fails > 5 0 0 0 95 +0
Module length (lines) warns > 800, fails > 1200 0 0 0 50 +0
Duplicated lines per file fails > 0 0 0 0 96 +0
Dead code (vulture) fails > 0 0 0 0 2 +0
Check Findings Effect
Comments 0
English only 0
Production code imports test code 0
Repeated literals 0
A1 Private name of another package 0
A8 Flag argument 0
A9 Tuple return 0
A10 Raw vocabulary comparison 0
A11 Hidden global write 0
A12 Pure forwarder 0
A12 Single-implementation abstraction 0
116 findings in files this change does not touch

Outside this change (informational): 116

Unit Dimension Baseline Now
src/hyperloom/orchestrator/state/shared_state.py resolve_graded_comparison Cognitive complexity absent 34
src/hyperloom_kb/collect/mapping.py _experience Cognitive complexity absent 31
src/hyperloom_kb/schema.py FieldDeclaration.__post_init__ Cognitive complexity absent 32
src/kernelforge/cli/forge_loop_command.py _prepare_measurement_driver Cognitive complexity absent 36
src/kernelforge/cli/forge_loop_command.py _read_kb_warm_start Cognitive complexity absent 40
src/kernelforge/cli/forge_loop_command.py forge_loop Cognitive complexity absent 31
src/kernelforge/loop/runner.py IterationLoop._collect_session_candidate Cognitive complexity absent 44
src/kernelforge/loop/runner.py IterationLoop._rule_on_attempt Cognitive complexity absent 38
src/hyperloom/inference_optimizer/cli/__init__.py _reexport_session_environment Function length (lines) absent 111
src/hyperloom/inference_optimizer/cli/__init__.py _resolve_agent_lineup Function length (lines) absent 89
src/hyperloom/inference_optimizer/cli/__init__.py _resolve_run_topology Function length (lines) absent 92
src/hyperloom/inference_optimizer/cli/__init__.py _resume_session Function length (lines) absent 170
src/hyperloom/inference_optimizer/cli/__init__.py _seed_fresh_session Function length (lines) absent 170
src/hyperloom/inference_optimizer/multi_node/cli.py _restart_multinode Function length (lines) absent 107
src/hyperloom/inference_optimizer/multi_node/scripts/launch_multinode.py _build_parser Function length (lines) absent 84
src/hyperloom/inference_optimizer/multi_node/scripts/launch_multinode.py _spawn_rank_actors Function length (lines) absent 84
src/hyperloom_kb/sync.py GlobalSync.pull Function length (lines) absent 84
src/kernelforge/cli/forge_loop_command.py _RunReporter.build_result Function length (lines) absent 92
src/kernelforge/cli/forge_loop_command.py _prepare_measurement_driver Function length (lines) absent 136
src/kernelforge/cli/forge_loop_command.py _read_kb_warm_start Function length (lines) absent 149
src/kernelforge/loop/runner.py IterationLoop._close_iteration Function length (lines) absent 117
src/kernelforge/loop/runner.py IterationLoop._collect_session_candidate Function length (lines) absent 185
src/kernelforge/loop/runner.py IterationLoop._implementer_history Function length (lines) absent 112
src/kernelforge/loop/runner.py IterationLoop._rule_on_attempt Function length (lines) absent 102
src/kernelforge/loop/runner.py IterationLoop._source_attempt Function length (lines) absent 175

...and 91 more; the job log lists every row.

Verdict computed by the gate scripts in /home/runner/work/_temp/code-metrics-gate, not by this tree's copy.

Thresholds and sources: [tool.hyperloom.code_metrics] in pyproject.toml. Tools: complexipy 8.0.1, jscpd 5.4.1, ruff 0.16.2, vulture 2.16.

Comment thread src/kernelforge/tests/knowledge/pr_knowledge/test_inclusion.py Fixed
@okorzh-amd
okorzh-amd force-pushed the okorzh/skill-analyze-upstream-pr branch from d74f164 to 9e3cf82 Compare October 14, 2026 03:32
@okorzh-amd
okorzh-amd requested a review from chaojhou October 14, 2026 18:48

@chaojhou chaojhou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

PR #1851 -- feat(skills): add analyze-upstream-pr to judge an upstream PR for an AMD deployment

What it does: Adds a contributor skill (.claude/skills/analyze-upstream-pr/) that takes an upstream framework or kernel-library PR plus a target AMD deployment and writes a report on whether the deployment runs the changed code, whether the local tree already has it, and one of five recommendations. It collects upstream-only data with fetch_upstream.sh, reuses check-prior-art for duplicate PRs (drop_self.sh keeps the PR out of its own check), and moves the shared comment and PR-state jq into check-prior-art/lib.sh. The GitHub diff also carries the stacked #1850 (check-prior-art) and #1847 (inclusion probe).

Blocking issues: 1

  1. [X2] Description is stale after the last commit: "14 tests" and no drop_self.sh [verified]
    Problem: the body says test_analyze_upstream_pr.py has 14 tests and never mentions drop_self.sh, but head commit 9e3cf82 adds .claude/skills/analyze-upstream-pr/drop_self.sh and five more tests, 19 in total. scripts/tests/test_analyze_upstream_pr.py:400
    Impact: the description no longer matches the diff at head: a reader misses a new script that rewrites check-prior-art's candidates.tsv and overlap.tsv, and the stated test count is wrong.
    Action: update the body: add drop_self.sh to the collection/Step 6 description, list the two drop_self tests it pins, and change "14 tests" to 19.

SKIPPED: V4 -- PARTIAL overlap for #1032, #1154, #1276, #1675 (each cut at its first 100 files), overlap there unchecked

Checked: analyze-upstream-pr/{SKILL.md Step 6 and recommendation table, drop_self.sh, fetch_upstream.sh}, check-prior-art/{lib.sh PR_JQ/render_comments/replace_file, search.sh, overlap.sh, SKILL.md Steps 2-4}, AGENTS.md row, .importlinter, test_analyze_upstream_pr.py at head and head~1, body.txt, comments.txt, openprs.txt, ci.txt; earlier self-duplicate hypothesis rechecked and fixed at head | Ran: hl_fetch.py 1851; git show of head files; python ..\pr_tools\c901.py on the 7 changed src/kernelforge units (max head complexity 10); shell scripts reviewed statically, nothing run; gh run list --commit 9e3cf82 after approving the fork runs (9 workflows success, the 2 label-gated E2E skipped) | Base: 1a444f6 | Head: 9e3cf82

@chaojhou chaojhou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

PR #1851 -- feat(skills): add analyze-upstream-pr to judge an upstream PR for an AMD deployment

What it does: Adds .claude/skills/analyze-upstream-pr/, a procedure that takes an upstream framework or kernel-library PR plus a target AMD deployment and writes a report: does the deployment run the changed code, is the change already in the tree, and one of five recommendations. It collects with review-pr's fetch.sh plus a new fetch_upstream.sh, reuses check-prior-art for duplicate PRs (with drop_self.sh to keep the PR out of its own check), and moves the shared comment and PR-state jq into check-prior-art's lib.sh. The diff also carries the stacked #1850 and #1847.

Blocking issues: 2

  1. [free:merged-duplicate] A PR whose change already landed through another PR comes out CARRY-AS-PATCH [verified]
    Problem: Step 6 carries check-prior-art's DUPLICATE and OVERLAPPING classes into conflicts.txt (SKILL.md:206-211), but the recommendation table reads another PR only as "a competing open PR" under WAIT-FOR-MERGE (SKILL.md:29); no row handles a merged DUPLICATE, and Step 4 checks the inclusion of this PR only. An open PR that is ON-PATH with no surviving blocking risk therefore falls to CARRY-AS-PATCH. .claude/skills/analyze-upstream-pr/SKILL.md:31
    Impact: the user is told to apply the PR's diff as a local patch (SKILL.md:54) although upstream already merged the change through another PR, which the tree may already run or can take with ADOPT-NOW.
    Action: run check-prior-art through its verdict and map it into the table: EXISTS-MERGED-INCLUDED -> NOT-APPLICABLE -- already in tree via <ref>, EXISTS-MERGED-NOT-IN-TREE -> analyze <ref> instead, EXISTS-OPEN -> WAIT-FOR-MERGE.

  2. [X2] The description is stale after the last commit [verified]
    Problem: unchanged since the last round at this head: it says test_analyze_upstream_pr.py has "14 tests" and never mentions drop_self.sh; head 9e3cf82 adds drop_self.sh and five more test cases, 19 in total. scripts/tests/test_analyze_upstream_pr.py:400
    Impact: the description does not match the diff at the head.
    Action: update the body to the final shape (if drop_self.sh goes as suggested below, describe that instead).


Design review (requested by the maintainer; beyond the blocking bar)

The aim of this section is a smaller PR, not more guards: each point says what to delete or merge.

Purpose, route, files. Skill files: SKILL.md (292 lines), references/dimensions.md (lenses L1-L11), references/measurement.md; scripts fetch_upstream.sh and drop_self.sh; tests scripts/tests/test_analyze_upstream_pr.py (19 cases); check-prior-art's lib.sh gains the shared helpers and read.sh / search.sh use them; one AGENTS.md row.

1. Completeness. Item 1. Step 6 notices when the PR's diff no longer applies to the target commit (SKILL.md:216-218), but no row or qualifier reads that, and CARRY-AS-PATCH then tells the user to apply that same diff; with no local patches the check does not run at all (:219) -- always run git apply --check against the target commit. For a kernel-library PR the code that selects the kernel lives in the framework, but target.txt holds one tree, so on-path and inclusion cannot both be checked. When GitHub refuses the diff, fetch.sh exits before writing files.txt, and Steps 1 and 6 lose their input with only SKIPPED: fetch in the report. Without a tree every on-path line stays UNKNOWN; ask for a tree at Step 0.

2. Simpler / smaller.

  • Collection: check-prior-art's read.sh already writes the state lines, comments, cross-references, per-file counts and a diff with its own SKIPPED line, through the same lib.sh functions; only CI at the head commit is missing (two gh api lines). Using it removes fetch_upstream.sh's comment, state and timeline parts and the fetch.sh-then-read.sh fallback.
  • linked.txt: only reverted-by and superseded-by are read (SKILL.md:26-27); closes (a separate GraphQL call), supersedes and reverts are only described, and Step 9 re-proves a revert from its diff anyway. Reading the titles in xrefs.tsv and the comments is enough.
  • drop_self.sh: delete it. The sentence at SKILL.md:210 ("keep <owner/name>#<N> out of the shortlist and classes") is sufficient because the agent writes the shortlist, and the script both misses the third source (pathlog.txt) and rewrites another skill's tables. My earlier must-fix offered "a sentence, or an exclusion in overlap.sh"; the sentence is the right one.
  • Lenses: L2 repeats Steps 1-2, L3, L4 and L10 repeat Step 3's caller walk, L11 overlaps WAIT-FOR-MERGE. Keep the AMD-specific L5-L9 plus a dependency-pin lens, each citing the PR it was learned from, as review-pr requires of its rules; fold measurement.md into Step 8.

3. Redundancy and silent failure. Comments are fetched twice (fetch.sh and fetch_upstream.sh), open PRs are scanned twice (openprs.txt and check-prior-art), the merge commit is read three ways, the probe is called two ways, and the PR is excluded in three places. A failed reference lookup, rate limit included, becomes an unresolved row with no retry and is not listed in coverage, so a failed "Reverted by #N" lookup cannot trigger the reverted row; a probe crash (exit 1 or 2) is recorded and the script still exits 0, and SKILL.md never names that outcome.

4. Docs and comments. Item 2; fetch_upstream.sh:18-19 says anything that cannot be collected exits non-zero except an unresolvable reference, but :149-152 record a failed probe and carry on; SKILL.md:109 says read.sh's files.txt "still lists every path", but read.sh writes it under prs/<owner>/<name>/<N>/ with extra columns, not the $WORK/files.txt that Steps 3 and 6 read.

5. Tests. Keep two and move them into test_check_prior_art.py, run against read.sh: the forged-header test (test_analyze_upstream_pr.py:189) and the ordering-and-roles test (:132). Both pin render_comments, which read.sh now depends on and test_check_prior_art.py checks only by its first line. The rest pin incidental detail or duplicate other tests: the exact 14-row table (:227), the repeated "#41" case (:297), the merge-commit rule already pinned in test_check_prior_art.py (:319), a literal string (:341), the unused GraphQL call (:349), usage messages (:356, :416), printf padding (:409), and the drop_self.sh tests if the script goes.

Starting from zero. A skill with no scripts of its own: read.sh plus one CI command for collection, one probe snippet for the PR and for each merged revert, Steps 1-5 and 9-10, and about 50 lines of AMD-specific lenses. It is worth a separate skill: the on-path step, the operating-point gaps and the recommendation table do not fit check-prior-art's search-driven flow.

Change list, in order of impact:

  1. Item 1: run check-prior-art through its verdict and map it into the table.
  2. Delete drop_self.sh and its tests; keep the one-sentence exclusion.
  3. Collect with read.sh plus one CI command; delete fetch_upstream.sh's comment, state and timeline parts and stop using fetch.sh (SKILL.md:64-65, 93-110, 204-205).
  4. Replace linked.txt with xrefs.tsv plus one sentence; drop closes, supersedes, reverts and the GraphQL call (or, if a helper stays, list its unresolved rows under coverage).
  5. One probe snippet; treat exit 1 and 2 as SKIPPED.
  6. Close the gaps in point 1: always git apply --check against the target commit and qualify the recommendation; an input for the tree that holds the selection code; ask for a tree at Step 0.
  7. Cut dimensions.md to the AMD-specific lenses with their source PRs; fold measurement.md into Step 8.
  8. Move the two comment tests; delete the others; revert lib.sh:5.
  9. Rewrite the description (item 2).

Checked: SKILL.md (recommendation and next-step tables, Step 6), drop_self.sh, fetch_upstream.sh, references/dimensions.md and measurement.md, check-prior-art's lib.sh / read.sh / SKILL.md Steps 4-7, test_analyze_upstream_pr.py, the description against the head | Ran: static review only (no bash or jq on this host); no test suite | Base: 1a444f6 | Head: 9e3cf82

@okorzh-amd
okorzh-amd force-pushed the okorzh/skill-analyze-upstream-pr branch 3 times, most recently from 5b729b2 to f8bc23d Compare October 19, 2026 17:34

@chaojhou chaojhou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

PR #1851 -- feat(skills): add analyze-upstream-pr to judge an upstream PR for an AMD deployment

What it does: PR #1851 adds a contributor skill, .claude/skills/analyze-upstream-pr/ (SKILL.md plus references/dimensions.md, and one row in the AGENTS.md table). The skill judges an upstream framework or kernel-library PR against an AMD deployment and ends in one recommendation. It collects the PR with check-prior-art's read.sh plus two gh calls, finds the selection site that decides whether the deployment runs the changed code, probes the local tree with the pr_knowledge inclusion probe, runs check-prior-art for competing PRs, runs git apply --check on the diff at the target commit, checks six AMD-specific risk lenses, refutes each claim, and maps the result to one row of the recommendation table. It is cleanly stacked on #1850: origin/pr-1850 (56702fb) is an ancestor of the head, and git diff origin/pr-1850 origin/pr-1851 touches only those three files, so the check-prior-art files, the inclusion probe and test_check_prior_art.py are identical to #1850 and belong to that PR's review.

Blocking issues: 1

  1. [desync-body-describes-removed-design] The PR description still describes the earlier design: fetch_upstream.sh, a 14-test file, measurement.md, the lib.sh lift and a new comments.txt shape, none of which exist at head [verified]
    Problem: The live body (checked with gh at head f8bc23d, updated 2026-10-20) describes a design that later commits replaced. Commits 100feb6 ('collect an upstream PR with check-prior-art's read.sh and one probe snippet') and d6b3c78 ('keep only the AMD-specific risk lenses') did the replacing.

What the body says:

  • Collection uses review-pr's fetch.sh plus a new fetch_upstream.sh, which writes comments_untrusted.txt, linked.txt and inclusion.txt.
  • It adds scripts/tests/test_analyze_upstream_pr.py with 14 tests.
  • It adds references/measurement.md.
  • It lifts the comment collection and the PR-state jq into check-prior-art's lib.sh.
  • check-prior-art's read.sh comments.txt changes shape (the 'Observable effect' section).
  • There are 'eleven risk lenses' and SKILL.md is 292 lines.
  • It gives a five-row recommendation list.

What the head has:

  • Collection is read.sh plus a gh pr view call for checks.json and a gh api call for inline.jsonl (SKILL.md:89-94).
  • None of those scripts, tests or files exist (git ls-tree origin/pr-1851 lists only SKILL.md and dimensions.md under analyze-upstream-pr).
  • lib.sh and read.sh are byte-identical to #1850. read.sh writes no comments.txt at all.
  • dimensions.md has six lenses (L1-L6).
  • SKILL.md is 280 lines.
  • The recommendation table (SKILL.md:25-39) adds four NOT-APPLICABLE rows the body omits: already in tree, merged as, superseded by, and off path. It also adds the -- does not apply at <commit> qualifier. .claude/skills/analyze-upstream-pr/SKILL.md:89
    Impact: The 'Observable effect' and 'Breaking changes' sections claim that check-prior-art's per-PR comments.txt changes shape, which this PR does not do. The 'Tests' section claims 14 tests that the PR does not add. A reviewer or the release notes would describe scripts, artifacts and a refactor that do not ship, and would miss the recommendation rows that actually decide the outcome.
    Action: Rewrite the body for the head:
  • Name the three files.
  • Describe collection as read.sh plus checks.json and inline.jsonl.
  • Say the PR adds no scripts or tests, and why (only markdown procedure; scripts are #1850's).
  • Give the six lenses.
  • Give the ten-row recommendation table and its qualifiers.
  • Drop the lib.sh, comments.txt and fetch_upstream.sh paragraphs.
  • Name #1850 explicitly as the dependency.

Checked: I read the following:

  • At head: the full analyze-upstream-pr/SKILL.md and references/dimensions.md, and the AGENTS.md diff.
  • check-prior-art read.sh, lib.sh, SKILL.md (Steps 4-7 and the verdict table) and the gotchas G1-G8 headings.
  • inclusion.py at head against main's kernelforge.llm.git git/git_bytes signatures.
  • The live PR body via gh, plus body.txt, files.txt, numstat.txt, commits.txt, comments.txt (earlier review cards) and ci.txt (all green; label-gated E2E skipped).

I ran the following:

  • git merge-base --is-ancestor origin/pr-1850 origin/pr-1851: stacked.
  • git diff --stat origin/pr-1850 origin/pr-1851: 3 files.
  • git merge-tree origin/main origin/pr-1851: clean.
  • The inclusion probe on an archive of the merged tree: exit 3, works.
  • The Step 6 binary-exclusion awk plus git apply --check in a scratch repo: the binary stanza fails without $BIN and passes with it.

Not run: pytest (Windows) and the shell procedure end to end against a real upstream PR.

Base b7004d1, head f8bc23d, main 1cdc6ff. | Base: b7004d1 | Head: f8bc23d

okorzh-amd and others added 16 commits October 20, 2026 12:39
Nothing answered "is upstream PR N already in this tree or commit?", so an
agent could take up a PR that its framework tree already carries.

probe_inclusion reads local git only. Revisions resolve through rev-list
with a --missing action, so a sha that a partial clone lacks is not
fetched. It checks ancestry of the PR's merge commit when that commit is
present, then looks for a reachable commit whose subject ends in a (#N)
suffix run, which is how squash merges and fork cherry-picks record the
PR. It answers not_included only when a complete history gives a definite
not-ancestor answer and no subject matches. Every other case, including a
git call that fails, cannot start or times out, is unknown, with the
reason in the evidence. Each git call is bounded by a timeout.

The verdict is available as python -m kernelforge.knowledge.pr_knowledge.inclusion
(JSON on stdout; exit 0, 4 or 3 for included, not included or unknown, 2 on
a usage error; 1 stays the status of an uncaught error) and as the
pr_check_inclusion tool of the PR stdio MCP server, whose commit defaults
to the tree's HEAD.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The stub that fails one git command was a lambda wrapping a call, which
CodeQL reports as an unnecessary lambda. A nested function does the same
job without the wrapper.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ncluded

probe_inclusion answered not_included whenever the merge commit was
present but not an ancestor and no subject ended in (#N). A PR the tree
does carry read as absent when a fork cherry-picked the squash commit
under a rewritten subject, or, under an upstream that merges with merge
commits, cherry-picked the PR's own commits.

Before answering not_included, the probe now compares the non-merge
commits the merge commit brought in (merge^1..merge: the squash commit
itself, or the PR's commits under a merge commit) with the commits
reachable from the checked commit, using git rev-list --cherry. A commit
counts as carried when the tree has it by sha or by patch-id. All carried
is included, through the new method patch_id; none is not_included; some
is unknown, and so is a call that fails or times out.

Both calls run as rev-list with --missing=print, which turns lazy fetching
off, so a partial clone that lacks a blob a patch-id needs answers unknown
and fetches nothing. A backport without the suffix whose diff was edited
still reads as not_included; the module docstring and the MCP tool
description now say so, and a test pins it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… is carried

Under an upstream that merges with merge commits, the patch-id step
compares each of the PR's commits on its own. A fork that carries a
multi-commit PR folded into one commit, with git cherry-pick -m 1 of the
merge or by squashing the PR's commits, matches none of their patch-ids,
and the default cherry-pick -m 1 subject "Merge pull request #N from ..."
has no (#N) suffix. Such a tree read as not_included, exit 4.

When the merge brought in several commits and none is carried one by
one, the probe now answers unknown, and the evidence names the fold. A
squash or rebase merge brings in one commit and still answers
not_included when that commit has no patch-id match.

A patch-id of the merge's first-parent diff would match the fold, but
computing it needs git diff-tree -p or git log -p. Neither takes
--missing, and only git 2.44 and later can stop them lazily fetching
blobs in a partial clone (GIT_NO_LAZY_FETCH), while the probe must
fetch nothing.

Tests: the none-carried case of the two-commit merge test is now
unknown, and git cherry-pick -m 1 of the merge is included for a
one-commit PR and unknown for a two-commit PR.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The module docstring said the patch-id step finds a cherry-pick under
any subject, and the pr_check_inclusion description named only an
edited backport as a miss. A patch-id hashes the diff with its context
lines and compares one commit at a time, so a backport with no (#N)
suffix that git applied cleanly still reads as not_included when the
fork had changed a line next to a hunk, and so does one that carries a
squash-merged PR as the PR's own commits picked one by one.

The docstring now says a patch-id matches only a commit whose diff,
whitespace and line numbers aside, is identical to the commit the merge
brought in, and names the misses: an edit or a resolved conflict, a
change landed next to fork changes, a squash-merged PR carried as its
commits, a fold into a larger commit, and a fork that squashed a
rebase-merged PR. The MCP tool description, which is what an agent
reads, says the same in short. rev-list --cherry cannot compare with
less context: it rejects -U0 and ignores diff.context.

A new test pins the clean backport next to a fork change as
not_included.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Inclusion and InclusionMethod used a StrEnum with a Python 3.10 shim
copied from the quantization driver, but nothing formats them with
str(): to_record reads .value, and the PR-state enum next to them is a
plain (str, Enum). They are now the same, and the shim is gone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Local history can show that a PR is in a tree, never that it is absent:
a backport that was edited, had a conflict resolved or was folded into
a larger commit looks the same as no carry. The probe still answered
not_included (exit 4) for such trees, so a caller could take up a PR the
tree already carries.

The probe now answers included, with the git evidence, or unknown, with
the reason. not_included, exit status 4, the shallow-history check and
the one-commit and several-commit branches are gone.

With a merge commit sha, one call,

    git rev-list --missing=print --no-merges --cherry-pick --right-only \
        <head>...<merge> ^<merge>^1 --

replaces the ancestry check, the commit count and the --cherry pass. It
lists the non-merge commits the merge brought in that head has neither
by sha nor by patch-id, so an empty list is included: for a merge
commit that is an ancestor, and for a cherry-pick under another
subject. A sha the tree lacks fails the call, and nothing is fetched.
Subjects are now searched first, because they need no sha.

Every git call goes through the shared wrapper with its exit status
checked. A non-zero exit, a timeout or a git that cannot start is caught
once in probe_inclusion and answered unknown with the error as the
evidence (git's own message for a non-zero exit), so a timed-out
resolve no longer reads "is not in the tree". At most three
calls run, each bounded at 15 s, inside the 60 s the Forge agent gives a
pr_monitor tool call. The revision to check reaches git after
--end-of-options: rev-list accepts --output=<file> and creates that
file, so a leading-dash value is now a bad revision, not an option.

The methods are now subject, merge_commit and none. The module
docstring, the pr_check_inclusion description and cli.md state the rule,
and cli.md names the check-prior-art contributor skill as the consumer
that passes the merge commit sha on the command line.

Tests keep the subject hits and misses, non-UTF-8 output, the
merge-commit cases, the partial-clone no-fetch cases, one hang, one
exit 128, a bad and a leading-dash revision, a directory outside git,
the command line and the MCP tool. The not_included cases, the
ten-case failure matrix and the schema restatement test are gone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An upstream that lands PRs with merge commits records the PR as
"Merge pull request #N from <branch>", and so does a fork's
git cherry-pick -m 1 of that merge. The subject search only knew the
trailing (#N) of a squash merge, so without a merge commit sha such a
tree answered unknown.

The search now also greps for that subject and counts a commit whose
subject starts with it for exactly N; #N followed by another digit, or
the phrase inside a revert subject, does not count. Two cases pin it:
the subject for AMD-AGI#12 is included, and the one for AMD-AGI#123 is not taken for
AMD-AGI#12.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…gent cannot supply

pr_check_inclusion took merge_commit_sha and passed it through, but no
tool the Forge Implementer can call returns one: pr_get_reference
carries the PR's state and merge time, not its merge commit. The
argument only ever arrived from tests, so the merge-commit check never
ran for the one consumer of the tool.

The tool now answers from commit subjects alone, and its schema and
description say so. The command line keeps --merge-commit-sha for the
check-prior-art contributor skill, which reads the sha from the GitHub
API.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…he probe

The module docstring said the check-prior-art skill passes the sha it
"reads from the GitHub API". The skill reads the PR's mergeCommit with
gh pr view, and avoids REST's merge_commit_sha, which names a test merge
for an open PR. Say which field it passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lanned change upstream

Before building an optimization, nothing checked whether the same change
already sat in an open or merged upstream PR, or was already in the tree
being measured.

.claude/skills/check-prior-art runs top to bottom like review-pr: every step
writes an artifact into a work directory, every DUPLICATE or OVERLAPPING
class and every not_included answer is refuted once, and a step that cannot
run is recorded as SKIPPED. search.sh sends one GitHub search per repository,
state (open, merged) and phrase, pacing itself by the search rate-limit
headers, and records every query with what it listed out of what matched.
overlap.sh pages GraphQL over a repository's open PRs, newest first, up to
10000, and names every cut (page cap, a stopped scan, a PR whose file list
GitHub truncated) as PARTIAL. read.sh collects one PR for a deep read and
records merge_commit_sha only once the PR merged. Merged candidates are
checked against the tree with python -m
kernelforge.knowledge.pr_knowledge.inclusion. The report is one of five
verdicts with its evidence, coverage and next step; nothing is posted to
GitHub. references/gotchas.md holds the generic traps, and AGENTS.md links
the skill.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…misses

The probe now compares patch-ids before answering not_included, so a
cherry-pick with the same diff is found under any subject. G5 still said a
cherry-pick that rewrote its subject was invisible to the probe; it now names
the backports that still read as not_included.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… PRs too

The inclusion probe now answers only included or unknown, so check-prior-art no
longer reads a not_included answer, exit 4 or a subject_suffix method. Step 5
probes every DUPLICATE, open or merged, and no longer probes OVERLAPPING PRs,
whose answer fed no verdict. Step 6 searches the tree's own log for every
DUPLICATE whose answer is not included, so a fork that carries an open PR under
its own subject is reported in the tree instead of being told to apply it.

EXISTS-MERGED and EXISTS-OPEN now share one qualifier: "-- in your tree" when
Step 5 or 6 found the PR there, otherwise "-- inclusion unchecked", because
local git cannot show that a PR is absent. G5 says what the probe's subject and
patch-id checks miss and that Step 6 covers them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Step 4 classed a PR that carries part of the mechanism in another file as
OVERLAPPING, while Step 6 dropped every OVERLAPPING PR to UNRELATED unless it
changed a planned path or function. An upstream PR doing the same trick in a
sibling backend's file therefore ended UNRELATED, and the verdict could be
NOT-FOUND with "proceed".

Step 4's table is now the only definition, read top to bottom with the first
row that applies winning; SUPERSEDED moves to the top and names a reverted PR,
so a reverted duplicate is SUPERSEDED in both steps. Step 6 no longer restates
the classes: a refuted PR takes the first row of that table it still meets.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s in Coverage

Step 4 reads at most 12 candidates and keeps the rest as "# cut:" lines, but
neither Coverage nor the "-- incomplete" qualifier looked at them, so a bare
NOT-FOUND could rest on 12 of 76 candidates chosen by title. Coverage now says
"read N of M shortlisted" and names the cut PRs, any "# cut:" line makes
PARTIAL-OVERLAP and NOT-FOUND "-- incomplete", and the shortlist takes every
candidates.tsv row so that M counts them all.

Coverage also states what no check searches: closed-unmerged PRs, issues and
RFCs, repositories outside repos.txt, and a planned path absent upstream, which
matches nothing while the scan says COMPLETE.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s, not its wording

The overlap tests compared scan.txt line for line, so rewording a status line
or appending the lines in another order failed them with no change in what
the scan covered. A helper now reads each line as its repository or PR, its
status and the numbers it states, and the tests compare those. The refused-diff
and stray-path tests check the SKIPPED status and the offending paths.txt line
instead of the sentence around them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
okorzh-amd and others added 20 commits October 20, 2026 12:39
The test fed search.sh an empty repos.txt and a URL in place of owner/name and
checked the error text. Both are one-line input checks at the script's entry;
the overlap test with a planned path for a repository outside repos.txt
already pins that a bad input line stops a script with a non-zero exit that
names the line.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
SKILL.md listed the six verdicts in one table and their next steps in
another, and described Coverage both in the output contract and in Step 7's
template. The verdict table now carries each verdict line with when it applies
and its next step, and Step 7's template is the one description of Coverage.

The script headers restated the artifact layout SKILL.md gives, a second copy
that had already drifted once (read.sh's header said only a refused diff was
SKIPPED). Each header now names the step of SKILL.md that describes its
output and keeps only the rule a maintainer needs: queries.tsv is the
coverage, an empty overlap.tsv means nothing outside COMPLETE, and any diff
failure is SKIPPED while every other failure exits non-zero.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
G4 recommended a --filter=blob:none clone for an installed package and said
the probe reports the objects it lacks. The probe answers by subject and by
merge-commit ancestry from such a clone, but a cherry-pick matched by patch-id
needs the blobs, and without them the probe answers unknown rather than fetch
them. G4 now says so, so a contributor who needs that match clones in full.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s on the planned paths

Step 3's path log read a merged PR only from a squash commit's "(#N)"
suffix. On an upstream that lands PRs as merge commits, a path-limited
git log drops the merge commit, which is TREESAME to the PR side, and
lists the PR's own commits, which name no PR. Merged PRs on the planned
paths then never reached the shortlist, and nothing in Coverage said so.
In this repository, the path log from 5b4ac6f~1 to a56028d for
pr_knowledge/references.py prints only 5b4ac6f; with --first-parent it
prints the "Merge pull request AMD-AGI#1846 from ..." commit.

Write the path log with --first-parent and read both a "(#N)" suffix and
a "Merge pull request #N from" subject as a merged PR.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…erge commit's body

Step 5 and G1 called every subject match whose subject is not the PR's
title a collision, and so unknown. GitHub writes a merge commit as
"Merge pull request #N from <branch>" with the title in its body, so
that rule turned every correct merge-subject match into unknown, although
the probe accepts that subject and G5 says it finds the carry. On this
repository, the probe answers included/subject for AMD-AGI#1846 at a56028d
with the merge commit fc25a6d, whose body's first line is the PR's
title.

The same rule also missed a squash of a single commit, which keeps that
commit's subject: AMD-AGI#1763 here is titled "Re-Order Tasks by TraceLens
Sorting Order" and landed as "Reorder tasks by TL order (AMD-AGI#1763)", with
the PR's four files.

Compare the title with the subject of a "(#N)" match and with the body
of a "Merge pull request #N from" match. G1 says where GitHub writes the
title, and accepts a "(#N)" commit with another title when it changes
the PR's files.txt paths. Any other mismatch stays a collision.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…robe cannot run

Step 5 writes "SKIPPED: inclusion" both when there is no tree and when
the probe cannot run (exit 1 or 2), and Step 6 let every such line stand
as having "no tree to search". With a tree whose probe failed, for
example because the package cannot be imported, the log search that
attacks an unknown answer never ran, so a fork that carries the PR got
"-- inclusion unchecked".

A SKIPPED line with no tree or no commit (G4's version that names none)
still stands; a probe that could not run on a tree and commit is
attacked like unknown.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…n fields read.sh asks for

The stub gh ignored --json and printed the whole fixture, so the deep-read
test's "mergeCommit is None" restated the fixture: dropping mergeCommit
from read.sh's --json list left all 10 tests passing, although Step 5
reads mergeCommit.oid from pr.json and would then probe a merged PR by
its subject alone. The SKIPPED: diff count read changedFiles the same
way.

Print only the fields asked for, as gh does. Dropping mergeCommit or
changedFiles from read.sh's --json now fails the deep-read test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…AMD deployment

Nothing turned an upstream pull request into a grounded answer to three
questions: does the ROCm deployment run the code it changes, is it already
in the tree being measured, and what should be done with it.

.claude/skills/analyze-upstream-pr collects the PR with review-pr's
fetch.sh, and fetch_upstream.sh adds what fetch.sh leaves out upstream:
every comment under an untrusted-data header (fetch.sh's write-access
filter keeps only the author there), the closing, supersede, revert and
mention links, and the inclusion probe's answer for a given tree and
commit. Each step writes one artifact: the mechanism, the operating point
quoted from the body and comments, the selection site that puts the
changed code on or off the target's path (UNKNOWN until cited), inclusion,
a predicted effect, conflicts with open PRs (through check-prior-art) and
local patches, eleven risk lenses in references/dimensions.md, an optional
control-versus-change plan the user runs, and one refutation per claim.
The report is one of ADOPT-NOW, CARRY-AS-PATCH, WAIT-FOR-MERGE,
NOT-APPLICABLE or NEEDS-MEASUREMENT with its evidence; nothing is posted
to GitHub and nothing is benchmarked.

fetch_upstream.sh sources check-prior-art's lib.sh, which gains the
comment collection and rendering, the untrusted-data header and the
PR-state jq rules it uses. AGENTS.md links the skill.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
analyze-upstream-pr's Step 6 sends the open-PR question to check-prior-art
with files.txt as the planned paths, and nothing left the PR under
analysis out of that run. An open PR is an overlap.tsv row on every
planned path, and its title words make it a candidates.tsv row too, so
followed literally it came back as its own DUPLICATE, and the
WAIT-FOR-MERGE row ("a competing open PR") could fire instead of
CARRY-AS-PATCH. review-pr's fetch.sh, whose open-PR check this step
replaces, drops the PR itself.

The new drop_self.sh removes OWNER/NAME#NUMBER from check-prior-art's
candidates.tsv and overlap.tsv, matching the repository without regard
to case and the number exactly, and changes neither table unless both
exist. Step 6 runs it before check-prior-art's Step 4 and keeps the PR
out of the shortlist and the classes, which covers pathlog.txt, where a
merged PR the tree lacks shows up.

SKILL.md stays at its 292-line budget: Step 3's selection-site
paragraph is reflowed, and Step 0's note on G4 is shortened.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ck by one sentence

drop_self.sh rewrote check-prior-art's candidates.tsv and overlap.tsv to
remove the PR under analysis, and still missed the third source of the
shortlist, pathlog.txt. The agent writes check-prior-art's shortlist and
classes itself, so the sentence Step 6 already carried ("keep
<owner/name>#<N> out of its shortlist and classes") is the whole
exclusion. drop_self.sh and its two tests go; no script edits another
skill's tables any more.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…sh and one probe snippet

analyze-upstream-pr collected a PR three ways: review-pr's fetch.sh, a
new fetch_upstream.sh, and check-prior-art's read.sh as the fallback when
GitHub refused the diff. Comments were fetched twice, open PRs scanned
twice, the merge commit read three ways and the probe called two ways.

Step 0 now runs read.sh, which writes pr.json (state, draft flag,
mergeCommit, body, comments, reviews), files.txt, diff.txt or its
SKIPPED line, and xrefs.tsv, plus two gh commands: the head sha, base
branch and checks at the head into checks.json, and the inline review
comments, which gh pr view does not return, into inline.jsonl. Step 1
reads reverts and successors from xrefs.tsv and the comments in one
sentence instead of linked.txt's six relation kinds and GraphQL call.
Step 4 runs check-prior-art's Step 5 snippet for the PR and each merged
revert, and names exit 1 and 2 as SKIPPED: inclusion; the probe answers
included or unknown, and Step 9 attacks unknown in the tree's history.

fetch_upstream.sh and its test file go. lib.sh returns to check-prior-art's
own helpers: the comment rendering it carried for fetch_upstream.sh has no
user, since read.sh keeps comments as gh pr view's JSON, where a comment
cannot forge a header and needs no ordering test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… recommendation

Step 6 carried check-prior-art's DUPLICATE and OVERLAPPING classes into
conflicts.txt, but the recommendation table read another PR only as "a
competing open PR" under WAIT-FOR-MERGE. No row handled a merged
duplicate, and Step 4 probed this PR alone, so an open PR whose change
upstream had already merged through another PR fell through to
CARRY-AS-PATCH: apply this diff as a local patch.

Step 6 now runs check-prior-art through its verdict and writes the
verdict line into conflicts.txt, which the table reads:
- EXISTS-MERGED or EXISTS-OPEN -- in your tree: NOT-APPLICABLE --
  already in tree via <ref>, since the tree already runs the change;
- EXISTS-MERGED -- inclusion unchecked, for a PR that is not merged:
  NOT-APPLICABLE -- merged as <ref>, with "analyze <ref> instead";
- EXISTS-OPEN -- inclusion unchecked: WAIT-FOR-MERGE for an open PR,
  and NOT-APPLICABLE -- superseded by <ref> for one closed unmerged.
Its incomplete Coverage goes into the report's Coverage, and Step 9
refutes the verdict like any other claim.

Each row of the table now carries its own next step, as check-prior-art's
verdict table does, instead of a second table keyed by the first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…k for the trees it needs

Step 6 noticed a diff that no longer applied at the target commit only
while checking local patches: with no patches the check did not run, no
row or qualifier read its result, and CARRY-AS-PATCH then told the user
to apply that same diff. target.txt also held one tree, so for a
kernel-library PR, whose selection code lives in the framework, on-path
and inclusion could not both be checked, and a missing tree left every
on-path line UNKNOWN without asking for one.

- Step 6 always runs git apply --check of diff.txt in a scratch worktree
  at the target commit. FAILS qualifies ADOPT-NOW and CARRY-AS-PATCH
  with "-- does not apply at <commit>"; a diff that applies only in
  reverse means the tree already has the change and settles a Step 4
  unknown as included. Local patches are then applied one by one and
  checked again, as before.
- target.txt gains selection: <work tree>@<commit> for the code that
  selects the changed code when the PR's tree does not hold it; Step 3
  walks the callers there.
- Step 0 asks the user for what is missing before collecting: a
  deployment, a tree, and for a kernel-library PR the selection tree.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…he upstream PR it came from

Of the eleven lenses, L2 (premise) repeated Steps 1 and 2; L1, L3, L4
and L10 (semantics, gate or default, other paths, blast radius)
repeated Step 3's caller walk; and L11 (upstream fit) overlapped
WAIT-FOR-MERGE. None of them is specific to an AMD deployment.

references/dimensions.md keeps the lenses the author's platform hides,
renumbered L1-L6: parity with the CUDA path, weights and quantization,
numeric formats and bounds, collectives and parallelism, tests and
whether ROCm CI ran them, and a new dependency-pin lens. Each cites,
like review-pr's rules, the public upstream PR it was learned from
(vllm-project/vllm #34543, #57425, #56359, #46730, #39953, #16010,
#48654, #48155, #47072). 139 lines become 91.

references/measurement.md (50 lines) folds into Step 8 as one
paragraph: two arms that differ by the PR alone, the noise floor,
correctness before speed, the operating point, a decision rule written
first, proof the changed code ran, and results labeled measured.
Step 5 drops its label line, which only restated that the effect is
always predicted. SKILL.md is 278 lines against a budget of 280.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…upstream-pr's Step 6

check-prior-art's Step 0 sets $WORK, so an agent that runs it "in
$WORK/prior-art" by setting WORK there writes this skill's conflicts.txt
and every later artifact into the wrong directory. Step 6 now says
$WORK/prior-art is check-prior-art's work directory and $WORK stays
this skill's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…and apply steps meet

A GAP line is check-prior-art's G6 (a validated operating point may
differ from yours), and a diff that does not apply at the target commit
can be a path the tree lays out elsewhere (G7), not only drift from the
PR's base. Step 2 and Step 6 now say so.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… once

Step 0 restated the field list of check-prior-art's Step 4 collection,
and report parts 4 and 5 restated the Coverage and Next step lines of
Step 10's template. Both now point at the text that states them; Step 0
keeps only that mergeCommit is null until the PR merges, which Step 4's
probe depends on. Two sentences lose a trailing orphan line.

SKILL.md goes from 280 to 275 lines, so the next two fixes fit the
280-line budget.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…R's diff

read.sh writes diff.txt as a "SKIPPED: diff" line when gh pr diff
fails, as it does past 300 files (HTTP 406). Step 1 then had nothing to
read the mechanism from, and Steps 5 and 7 nothing to cite. The files
endpoint still returns each file's patch: on vllm-project/vllm#12628
(1012 files) the diff is refused, and the endpoint returns hunks for all
1012 paths.

Step 0 now writes hunks.txt from that endpoint only when diff.txt is
skipped, and it stands in wherever the diff is read. The apply check
still records "SKIPPED: apply", since hunks without file headers do not
apply. hunks.txt joins the untrusted inputs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
GitHub writes a binary file as a "Binary files a/x and b/x differ"
stanza with an abbreviated index line, which git apply never applies
("cannot apply binary patch ... without full index line"). Any PR that
touches a binary file therefore read FAILS in both directions, so it
got "-- does not apply at <commit>" and could not settle an unknown
inclusion by the reverse check.

Step 6 now builds $BIN, an --exclude for each binary stanza's old and
new path, passes it to every check, and records each such path as
"SKIPPED: apply -- <path>: binary". On vllm-project/vllm#50243, which
changes a PNG: without $BIN the diff fails at the PR's parent; with it,
it applies at the parent and applies with --reverse at the merge
commit. Both names are excluded because a reversed rename is matched by
its old name.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…uccessor

Step 1 took any PR a title or comment called this one's "replacement,
follow-up or reland" as a successor, and the superseded row fired for
any unmerged PR with one, with no Step 9 block behind it. A follow-up
builds on a PR rather than ending it: on vllm-project/vllm#60833, open,
xrefs.tsv lists #61271 "Mono decode follow-up", a draft stacked on
#60833's commits, and the skill read literally answered
"NOT-APPLICABLE -- superseded by #61271" for a live PR.

A successor is now a PR that either PR's title, body or comments call
this one's replacement or reland, the test check-prior-art's SUPERSEDED
class uses; a stacked or follow-up PR is not one. Each successor gets a
successor:<ref> block in Step 9, attacked in both PRs' bodies, and the
superseded row, like the revert row, needs one that survived. The file
stays at its 280-line budget.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@okorzh-amd
okorzh-amd force-pushed the okorzh/skill-analyze-upstream-pr branch from f8bc23d to 88bd0be Compare October 20, 2026 17:40
…as in tree

Step 6 applied each local patch in the scratch worktree and checked the
PR's diff forward only, and a failure that appeared only after the patch
was a CONFLICT. A patch that already carries the PR fails the same way:
with the PR's own diff as the local patch, the check at commit applies,
--numstat reports OVERLAP, and after the patch it fails with "patch
failed: m.py:1" while "git apply --check --reverse" succeeds. The skill
then recommended "CARRY-AS-PATCH -- conflicts with <its own patch>",
whose next step applies diff.txt a second time, or, for a merged PR,
ADOPT-NOW with a false CONFLICT row. CARRY-AS-PATCH itself creates that
patch, so its own rerun reached the case.

A failure after a local patch now runs the same --reverse check as the
one at commit. Applying backwards is local:<patch> REVERSE, which makes
Step 4 included like a REVERSE at commit, and the already-in-tree row
names it as "-- already in tree via <patch>". Only a failure both ways
is a CONFLICT, as for a patch carrying an older head of the PR. The file
stays at its 280-line budget.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@okorzh-amd

Copy link
Copy Markdown
Contributor Author

@chaojhou Thanks for the review. The description is now rewritten against 6cc6611, and it covers each item in your Action list:

@chaojhou
chaojhou dismissed stale reviews from themself October 22, 2026 07:38

Resolved at the current head; see the follow-up review comment.

@chaojhou

Copy link
Copy Markdown
Collaborator

PR #1851 -- feat(skills): add analyze-upstream-pr to judge an upstream PR for an AMD deployment

What it does: adds the analyze-upstream-pr skill as three Markdown files: SKILL.md, references/dimensions.md and one AGENTS.md row. The skill collects an upstream PR through the check-prior-art read.sh plus gh pr view / gh api, then judges it for an AMD deployment. It adds no script and no test, and changes none of the check-prior-art files. The branch carries #1847 and #1850 and merges after them, as the description states.

Blocking issues: none

The earlier blocker ([desync-body-describes-removed-design] at f8bc23d: the description described fetch_upstream.sh, a 14-test file, measurement.md and the lib.sh lift) is resolved. The description now lists the three Markdown files the PR actually adds.

Checked: the PR's own three files at 6cc6611. Also the check-prior-art scripts and test it carries from #1850, with the same CI-safety result: they call only gh, jq, awk, sort and sleep, write under the work directory and a trapped temp directory, and the tests use a stub gh | Ran: git merge-tree --write-tree origin/main 6cc661136 (clean); CI approved and running at this head | Head: 6cc6611

LGTM

@chaojhou chaojhou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving per the review comment above (resolved at 6cc6611; CI green; merged after #1847 and #1850).

@chaojhou
chaojhou merged commit 82441af into AMD-AGI:main Oct 22, 2026
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants