Repository navigation
feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream - #1850
okorzh-amd wants to merge 26 commits into
Conversation
74cbf58 to
0599f24
Compare
chaojhou
left a comment
There was a problem hiding this comment.
Thanks @okorzh-amd. This PR's own changes (the check-prior-art skill) have no blockers:
- GitHub data reaches files only through jq
--argor@tsv, and there is noeval. - Temp dirs are cleaned up by a trap, and every call is a read.
- The tests run offline.
- The skill stays repo-local; it is not in the package's data-files.
The PR contains #1847, so I'll approve it once #1847's must-fix is in.
|
Gate job conclusion: success (run) on The report below was uploaded by that run; the conclusion above is the result. Code metrics gate: PASSEDJudged: the 21 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.
106 findings in files this change does not touchOutside this change (informational): 106
...and 81 more; the job log lists every row.
Thresholds and sources: |
0599f24 to
07cff09
Compare
chaojhou
left a comment
There was a problem hiding this comment.
PR #1850 -- feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream
What it does: Before this PR, nothing in the repo let a contributor check whether a planned optimization was already in an open or merged upstream PR, or already in the tree being measured. It adds the repo-local .claude/skills/check-prior-art/ skill: search.sh runs keyword search, overlap.sh runs a paged open-PR path scan with explicit coverage, and read.sh deep-reads a PR; every query and scan records how far it got, and SKILL.md turns the results into a single verdict. The tree check comes from the stacked #1847 (python -m kernelforge.knowledge.pr_knowledge.inclusion and the pr_check_inclusion MCP tool), whose commits this PR's diff also carries.
Blocking issues: 1
- [X2] The description's test run predates the patch-id stage this PR carries [verified]
Problem: The body sayspytest scripts/tests/test_check_prior_art.py scripts/tests/test_check_cli_references.py src/kernelforge/tests/knowledge/pr_knowledge/test_inclusion.py: 67 passed. At head 07cff09 those files hold 82 cases (20 + 4 + 58). 67 is 20 + 4 + 43, the test_inclusion.py count before e4325c7, 6e5359b and 186ff29, all in this PR's diff, added the patch-id, multi-commitunknownand backport cases. src/kernelforge/tests/knowledge/pr_knowledge/test_inclusion.py:110
Impact: The validation the description reports does not cover the head that would merge; the probe that the skill's Step 5 runs changed after that run.
Action: Rerun the listed commands at the current head and update the counts.
SKIPPED: V4 -- PARTIAL overlap for #1032, #1154, #1276, #1675 (each cut at its first 100 files), overlap there unchecked
SKIPPED: local test run -- no pytest, bash or jq on this host; the shell scripts were reviewed statically and the inclusion cases traced by reading _patch_equivalence
Checked: .claude/skills/check-prior-art/{SKILL.md,lib.sh,search.sh,overlap.sh,read.sh,references/gotchas.md}, scripts/tests/test_check_prior_art.py, #1847 files inclusion.py probe_inclusion/_merge_ancestry/_subject_suffix/_patch_equivalence/main, search.py PRLifecycle/PRState, references.py, pr_stdio_server.py, kernel_names.py rename and its importers; tree-wide sweep at head for every non-test reader of is_merged, PRReference, reference_to_entry/entry_to_reference, tools.pmc, pr_check_inclusion and TOOL_NAMES (orchestrator/agent.py:268); body.txt against the head tree | Ran: python ..\pr_tools\hl_fetch.py 1850; python ..\pr_tools\c901.py 1a444f6 07cff09 <8 src paths> (max 10, none over 20); git grep at head; AST test-case count of the three listed test files at 07cff09 and 05c2e84; git blame on test_inclusion.py:110; gh run list --commit 07cff09 after approving the fork runs (9 workflows success, the 2 label-gated E2E skipped) | Base: 1a444f6 | Head: 07cff09
chaojhou
left a comment
There was a problem hiding this comment.
PR #1850 -- feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream
What it does: Adds the repo-local .claude/skills/check-prior-art/ skill so a contributor can check, before building an optimization, whether it already exists in an open or merged upstream PR or in the tree being measured: search.sh (keyword search with a status per query), overlap.sh (paged open-PR path scan), read.sh (deep read of one PR), and SKILL.md (shortlist, classify, probe with #1847, refute, one verdict with its coverage). The diff also carries the stacked #1847.
Blocking issues: 4
-
[free:open-pr-carried-by-fork] A fork that already carries an open upstream PR is told to apply and measure it again [verified]
Problem: for an open PRread.shwritesmerge_commit_sha: -(read.sh:45), so Step 5 runs the probe without--merge-commit-sha(SKILL.md:174), and without a sha the probe answersincludedonly for a(#N)subject, otherwiseunknown. Step 6 attacks onlynot_includedanswers with the fork'sgit log -S/--grepsearch (SKILL.md:182,:200-201), so for an open PR that search never runs. .claude/skills/check-prior-art/SKILL.md:182
Impact: a fork that cherry-picked an open PR under its own subject -- the G5 case -- getsEXISTS-OPENand the next step "apply the open PR's diff to your tree and measure it" (SKILL.md:46) for a change its baseline already runs, althoughreferences/gotchas.md:55-58promises-- in your treefor exactly this case.
Action: in Step 6, attack every DUPLICATE inclusion answer that is notincluded, open PRs included, with the log search; giveEXISTS-OPENan-- inclusion uncheckedqualifier when nothing settles it. -
[free:overlapping-definition] OVERLAPPING is defined two ways, so a PR that carries part of the mechanism in another file ends UNRELATED [verified]
Problem: Step 4 classes a PR that "changes the same files or functions, or carries part of the mechanism" as OVERLAPPING (SKILL.md:159); Step 6 drops an OVERLAPPING PR to UNRELATED unless "files.txtor the diff shows a planned path or function". .claude/skills/check-prior-art/SKILL.md:198
Impact: an upstream PR doing the same trick in a sibling backend's file is dropped, and the verdict can beNOT-FOUNDwith "proceed" (SKILL.md:30,:48).
Action: keep one definition: drop "or carries part of the mechanism" from:159, or let Step 6 keep such a PR as OVERLAPPING. -
[free:read-cap-coverage] The 12-PR read budget never reaches the report's coverage [verified]
Problem: Step 4 reads at most 12 candidates and keeps the rest as# cut:lines inshortlist.txt, but the Coverage part lists only non-OKqueries, non-COMPLETEscan lines andSKIPPED:lines (SKILL.md:35-37), and the-- incompletequalifier has the same triggers (SKILL.md:29). .claude/skills/check-prior-art/SKILL.md:141
Impact: a bareNOT-FOUNDcan rest on 12 of 76 candidates picked by title (the live run in the description found 76 for one phrase), against the skill's own rule that "nothing found" must not read as "not searched".
Action: add "read N of M; cut: ..." to Coverage, and make any# cut:line a trigger for-- incomplete. -
[X2] The description's test run predates the head [verified]
Problem: unchanged since the last round at this head: it says the three listed test files ran "67 passed"; at head they hold 82 cases (20 + 4 + 58). src/kernelforge/tests/knowledge/pr_knowledge/test_inclusion.py:110
Impact: the validation the description reports does not cover the head that would merge.
Action: rerun the listed commands at the head and update the counts.
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 (240 lines), references/gotchas.md (G1-G8); scripts search.sh, overlap.sh, read.sh, lib.sh; tests scripts/tests/test_check_prior_art.py (20 cases against a stub gh); one AGENTS.md row. Everything is a read through authenticated gh (REST and GraphQL) plus jq.
1. Completeness. Items 1-3. By design it cannot see closed-unmerged attempts (queries are open and merged only, although SUPERSEDED covers "is closed"), issues and RFCs (is:pr only), a duplicate in a second repository, or a planned path that does not exist upstream (the scan reports COMPLETE with an empty overlap and nothing records that). Say these in Coverage or gotchas.
2. Simpler / smaller.
- Keyword search:
gh api search/issues --jqis the right call (it returnstotal_countandincomplete_results, whichgh search prsdoes not), but the machinery around it is not needed: the-iheader split and reset arithmetic (search.sh:56-82) can be a fixedsleep 2from the documented 30/min search limit, and the pre-written PENDING rows, full-file rewrites andreplace_file(search.sh:51-54,84-99;lib.sh:29-34) can be appends -- Step 7 already counts two rows per term. - Open-PR scan: the GraphQL query and the REST re-listing for more than 100 files or a rename are warranted (
gh pr list --json filesstops at 100 files and has no change type); the killed-run bookkeeping on top of it (overlap.sh:76,152-153,162) is not, since SKILL.md already runs the scan in the background and checks the counts at Step 7. - Deep read:
gh pr view N -R R --json number,title,state,isDraft,mergedAt,mergeCommit,changedFiles,body,comments,reviews,url, plusgh pr diffand the timeline call, replaces most ofread.sh:mergeCommitis null until merge andstatealready says MERGED. - Probing merged OVERLAPPING PRs (
SKILL.md:165) feeds no verdict. - Follow-up: review-pr's
fetch.shanswers the same open-PR overlap question with the 100-file and rename blind spotsoverlap.shfixes; one shared script would serve both.
3. Redundancy and silent failure. A killed run is handled three times (script pre-writes, background run, Step 7 count check); keep one. The merged-state jq is written three times (search.sh:105; read.sh:42, :83), pulls/N/files is fetched by two scripts, and every gh call gets < /dev/null although nothing reads stdin. Failures become FAILED or PARTIAL rows, so no failed query reads as "nothing found"; two silent negatives remain: the planned path that does not exist upstream, and files_listed, which read.sh writes and SKILL.md never compares.
4. Docs and comments. Besides items 1-4: gotchas.md:51-53 says a folded backport "still reads as not_included", but for a multi-commit merge the probe answers unknown, which Step 6 never attacks; read.sh:10-11 says it "exits non-zero, except a diff GitHub refuses to render", but :62-66 turn every diff failure into SKIPPED with exit 0; SKILL.md:123 promises overlap-path columns overlap.tsv does not have.
5. Tests. Keep: dedupe and ranking (test_check_prior_art.py:169), the query-status vocabulary (:189), file and directory matching (:274), the page cap (:296), REST re-listing for a rename and file 101 (:305), an undecidable PR becomes PARTIAL (:326), a failed page (:343), an unknown repo (:378), the diff-refusal half of :456. Redundant or freezing the current shape: the rate-limit pacing test with wall-clock bounds (:211), the two killed-run tests (:228, :361) and the stub's SIGTERM path, the merge-sha test whose open and closed cases take the same branch (:449), three parameters of one pipefail mechanism (:471), the trivial input check (:243), and exact-English equality on scan.txt (assert status, repo and counts instead).
Starting from zero. SKILL.md of about 150 lines, search.sh of about 40 (loop, --jq, fixed pace, appends), overlap.sh of about 100 (paging, re-listing, one line per repository at the end), the deep read as four gh commands, and about 8 tests -- roughly 500 lines instead of 1,221. The skill is worth having: the full open-PR path scan and the tree probe are what a contributor cannot do reliably by hand.
Change list, in order of impact:
- Item 1: Step 6 attacks every DUPLICATE answer that is not
included;-- inclusion uncheckedforEXISTS-OPEN. If #1847 becomes two-valued, this is simply "every answer that is notincluded". - Item 2: one OVERLAPPING definition.
- Item 3: "read N of M" in Coverage; a cut triggers
-- incomplete. - Delete the killed-run bookkeeping and append instead (
search.sh:51-54,84-99;overlap.sh:76,152-153,162;lib.sh:29-34; tests:228,:361). - Replace the header pacing with
--jqplussleep 2; drop test:211. - Deep read via
gh pr view --json ...,mergeCommit; shrinkread.sh; drop test:449and two parameters of:471. - Fix the doc mismatches in point 4, and use or drop
files_listed. - Loosen the exact-prose assertions; update the description (item 4).
- Follow-up PR: one open-PR overlap script shared with review-pr's
fetch.sh.
Checked: SKILL.md (verdict and next-step tables, Steps 4-6), references/gotchas.md G5, read.sh (meta fields), search.sh, overlap.sh, lib.sh, test_check_prior_art.py, the #1847 probe's behaviour without a merge sha, the description against the head | Ran: static review only (no bash or jq on this host); no test suite | Base: 1a444f6 | Head: 07cff09
1b4eeb5 to
56702fb
Compare
chaojhou
left a comment
There was a problem hiding this comment.
PR #1850 -- feat(skills): add check-prior-art, a contributor skill that finds a planned change upstream
What it does: The PR adds a repo-local contributor skill, .claude/skills/check-prior-art/, which checks whether a planned optimization already exists upstream, either in an open or merged PR or in the tree being measured. It does this with search.sh (keyword search), overlap.sh (a paged GraphQL scan of open PRs plus a REST relist of their files), read.sh (a deep read of each candidate through gh pr view/gh pr diff) and the inclusion probe from the stacked #1847, which this diff also carries along with the pr_check_inclusion MCP tool and #2028's move of derive_kernel_names.
Blocking issues: 1
- [X2] The PR description describes an earlier shape of the scripts, the verdicts and the tests, not the code at head 56702fb [verified]
Problem: Later commits in this PR (a9bf0ff append instead of pre-write, 527c562 --jq plus a fixed sleep, 10084a9 gh pr view, 4e9d705, 7a6f72e never not_included) rewrote the skill, but the body was never updated. Each of these claims is false at head. (1) search.sh 'paces itself from each response's own rate-limit headers': search.sh:51-52 is a fixedsleep 2. (2) queries.tsv 'lists every planned query as PENDING from the start, and both files are rewritten after each answer': search.sh:40-50 appends rows as each query is answered, has no PENDING status, and writes candidates.tsv once at the end (:59-65). (3) scan.txt 'names every repository before the first page and is rewritten after each page', with 'did not finish -- N of M' and 'not scanned': overlap.sh:136-144 writes a repository's line only once its scan ends, and neither string exists. (4) read.sh writes 'meta.txt (merge_commit_sha)', 'body.txt and comments.txt, each headed as untrusted data': read.sh:29-31 writes pr.json with mergeCommit instead. (5) The verdicts EXISTS-MERGED-INCLUDED / EXISTS-MERGED-NOT-IN-TREE: SKILL.md:24-31 uses 'EXISTS-MERGED -- in your tree' / '-- inclusion unchecked' and 'EXISTS-OPEN -- inclusion unchecked'. (6) 'a not_included answer is dropped': inclusion.py:63-67 only answers included or unknown. (7) lib.sh 'one atomic file replace': lib.sh:1-31 has no such helper. (8) Tests: the body claims '20 tests' and '67 passed', and says the quota-wait, killed-run PENDING and merge-sha-only-when-merged contracts are tested and mutation-checked. At head, test_check_prior_art.py has 10 tests and the three listed files collect 10+4+33=47, and none of those three contracts has a test left. The previous card's X2 ('67 passed') is therefore still open, and it has grown. .claude/skills/check-prior-art/search.sh:52
Impact: The release notes would tell contributors about artifacts and statuses (meta.txt, PENDING rows, 'did not finish', header-based pacing) and verdict names that the skill never produces. The test and mutation evidence in the description covers code that is no longer in the diff, so the head as it would merge has no stated validation.
Action: Rewrite the Steps, Tests and Observable-effect sections of the body against the head: fixed 2 s pacing, appended queries.tsv rows, per-repository scan lines written only at the end, pr.json, the four EXISTS-* verdict lines, included/unknown only, no atomic replace in lib.sh. Then rerun the listed pytest commands and quote the real counts (10/4/33) and the mutation checks that still apply.
Checked: I read the head versions of .claude/skills/check-prior-art/{SKILL.md, lib.sh, search.sh, overlap.sh, read.sh, references/gotchas.md}, scripts/tests/test_check_prior_art.py, src/kernelforge/knowledge/pr_knowledge/inclusion.py, the pr_stdio_server.py diff, the kernel_names move (implementation.py, source_resolution.py, campaign_config.py), .importlinter (the acyclic contract), the consumer at kernelforge/orchestrator/agent.py:25/541 (PR_TOOL_NAMES), the hyperloom gate.py PR_MONITOR_TOOL_NAMES list (a different tool set, not a twin), kernelforge/llm/git.py git/git_bytes at head and at main (identical), and the body against the head tree, using the previous review cards in comments.txt. I ran git archive of head, main and the merge-tree result (bd600b12c) into a local script, then pytest --noconftest: on head, test_inclusion plus test_pr_stdio_server gave 84 passed, 1 skipped and one cp1252 failure that passes with PYTHONUTF8=1; on the merged tree, the same plus writer_helpers and injection_point gave 125 passed, with the remaining failures all fcntl or Windows permission errors; the shell tests were skipped because jq is not on this host. I also ran per-file collect-only counts (10/4/ | Base: b7004d1 | Head: 56702fb
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>
…ting them for a killed run search.sh wrote every query as PENDING up front and rewrote queries.tsv and candidates.tsv after each answer; overlap.sh wrote a "not scanned" line for every repository up front and rewrote scan.txt after each page. All of that kept a killed run's files in step, which Step 7 already checks by counting two queries.tsv rows per term and one scan.txt line per repository. search.sh now appends one queries.tsv row per answered query and writes candidates.tsv once, at the end. overlap.sh appends a PR's PARTIAL line when its overlap cannot be decided and a repository's line when its scan ends. lib.sh's replace_file goes, with the two killed-run tests and the stub's kill path. The gh calls no longer redirect stdin from /dev/null; nothing they run reads it. SKILL.md drops the PENDING status, and states overlap.tsv's columns as the script writes them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… a fixed sleep search.sh asked gh for the response headers, split them from the body and waited until the rate-limit reset whenever the remaining count reached zero. The search API allows an authenticated user 30 queries a minute, so a fixed two-second pause between queries keeps under it with no header arithmetic. Each query is now one gh api call whose --jq filter prints the query's status row and its PRs, so search.sh no longer needs jq itself. The merged-or-state expression moves into lib.sh as PR_STATE_JQ for the scripts that read the issues API. The wall-clock pacing test goes; the stub applies --jq with the real jq, as gh does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ew and gh pr diff read.sh read GET /pulls/N, rewrote it as meta.txt with a "-" in place of the test-merge sha of an unmerged PR, and fetched issue comments, reviews and inline comments in three more paginated calls. gh pr view --json returns the state (OPEN, CLOSED or MERGED), draft flag, body, comments and reviews in one call, and its mergeCommit is null until the PR merges, so read.sh now writes that as pr.json, the diff with gh pr diff, files.txt from the REST files list, and xrefs.tsv from the timeline. Step 5 reads the merge sha from pr.json. The REST files list stays: above 300 files GitHub refuses the diff, gh pr diff --name-only fails the same way and gh pr view's files stop at 100. A diff that cannot be read is a SKIPPED line saying how many of the changed paths files.txt lists, which replaces the files_listed field nothing read, and the script header now says that any diff failure, not only a refusal, is SKIPPED. The merge-sha test, whose open and closed cases took the same branch, goes; the failure test keeps one of its three cases. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
56702fb to
75d77c9
Compare
|
@chaojhou Thanks. The description is now rewritten against 75d77c9, and it covers all eight points: the fixed |
Description: what and why
Before someone builds an optimization for an upstream framework or kernel library, nothing in this repo checks whether the same change is already in an open or merged upstream PR, or already in the tree they measure. review-pr's overlap check starts from a PR that already exists and covers this repository's own open PRs. Duplicate work therefore shows up late: in upstream review, or as a "gain" that the baseline already had.
This adds
.claude/skills/check-prior-art/, a contributor skill in the style of review-pr. It is written for an agent to run top to bottom. Every step writes an artifact into a work directory, and a step that cannot run is recorded asSKIPPED. It reads GitHub throughghandjqand never posts to it.Everything below describes head
75d77c943, rebased ontomainat7ac8c1229(through feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847's head005924d3a).Inputs: a one-sentence mechanism, the upstream repositories, the search phrases, the planned paths in each repository's layout (a path ending in
/covers a directory), and optionally a local tree and commit.Steps 0 and 1 write those inputs. Then:
Step 2, keyword search.
search.shsends onegh api search/issues --jqcall per repository, state (is:open,is:merged) and phrase, with a fixedsleep 2after each, which keeps under the search API's 30 queries a minute. It does not read rate-limit headers.queries.tsvrow per query as that query is answered (there is no pre-written or pending row): its status, how many PRs it listed and how many matched. The status isOK,TRUNCATED(more matched than the 100 listed),INCOMPLETE(GitHub timed out searching) orFAILED(with gh's error). A failed query does not stop the others.candidates.tsv: one row per PR, ranked by how many queries found it, then by most recent update.Step 3, open PRs on the planned paths.
overlap.shcompares every open PR of each repository that has a planned path.old->new.overlap.tsvhas one row per overlapping PR, with the planned paths it changes.scan.txtgets aPARTIALline for each PR whose overlap still cannot be decided. A repository gets its one line only when its scan ends, never before:COMPLETE;PARTIAL(the page cap, or a failed page, keeping what was already compared); orSKIPPED(no planned path there). An emptyoverlap.tsvmeans "none" only wherescan.txtsaysCOMPLETE.git log --first-parent <commit>..<default branch> -- <paths>adds the merged PRs on those paths that the tree's commit lacks: each(#N)squash suffix and eachMerge pull request #N fromsubject in it.--first-parentkeeps a merge commit, where a path-limited log would list the PR's own commits, which name no PR.Step 4, deep read. The shortlist takes every overlap row, every path-log PR and every keyword candidate. At most 12 are read; the rest stay in the shortlist as
# cut:lines.read.shmakes fourghcalls:gh pr view --jsonintopr.json: state, draft flag,mergeCommit(null until the PR merges), body, comments and reviews;files.txt;gh pr diffintodiff.txt;xrefs.tsv.A diff that cannot be read, such as one GitHub refuses past 300 files, becomes a
SKIPPED: diffline that says how many of the PR's changed filesfiles.txtlists. Any other failure exits non-zero.Step 4, classes. Each read PR takes the first row of one table that applies:
SUPERSEDED: closed, reverted, or a replacement is named;DUPLICATE: its diff does the mechanism in the same code path;OVERLAPPING: it changes a planned file or function, or carries part of the mechanism in any file, a sibling backend's included;UNRELATED.Step 5, inclusion. Each DUPLICATE, open or merged, is checked against the tree with
python -m kernelforge.knowledge.pr_knowledge.inclusion. For a merged PR, the call passesmergeCommit.oidfrompr.json; an open PR has none and is probed by subject alone. The probe prints one JSON object (inclusion,method,evidence) and answers onlyincluded(exit 0) orunknown(exit 3); exit 2 is a usage error and exit 1 a crash with no answer. There is nonot_included: local git can show that a PR is in a tree, never that it is absent.subjectanswer can be a fork's own PR with the same number, so the commit the evidence names is checked against the PR's title: in its subject for a(#N)match, and in its body (git log -1 --format=%b), where GitHub writes the title, for aMerge pull request #N frommatch. A(#N)commit with another title still counts when it changes the PR's files, as a squash of a single commit keeps that commit's subject. Any other mismatch is recorded asunknown.SKIPPED: inclusionline.Step 6, refutation. Each DUPLICATE and OVERLAPPING class is attacked on mechanism, failure mode, operating point and revert, and ends as the first row of the class table it still meets. Each DUPLICATE's inclusion answer that is not
included, an open PR's too, is attacked in the tree's own history withgit log -S<symbol>and--grep=<title words>; so is a probe that could not run on a tree and commit. A hit whose diff is the PR's change makes the answerincluded. ASKIPPED: inclusionline with no tree or no commit stands.Step 7, report.
report.mdholds one verdict, an evidence row per classified PR, Coverage, and the next step. Before writing it, the agent checks that each repository has ascan.txtline and twoqueries.tsvrows per phrase; a missing one did not run and goes on the report asSKIPPED.references/gotchas.mdholds eight generic traps (G1-G8), each named from the steps that need it. Among them: PR and issue numbers share one counter per repository, and where GitHub writes a PR's title in a squash or a merge commit; a merged PR is not in an image built before it; and the inclusion probe matches a cherry-pick by subject, or by patch-id given the merge commit, but not an edited or folded backport, which is why Step 6 searches the log. AGENTS.md gains a row for the skill next to review-pr's.Stacked on "feat(pr_knowledge): probe whether an upstream PR is in a local git tree" (feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847), which provides the probe Step 5 runs. feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847 no longer stacks on anything: the
derive_kernel_namesmove it used to carry landed on main as refactor(kb_store): move derive_kernel_names into kb_store.identity.kernel_names #2028, so this diff no longer has it. Until feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847 merges, this PR's diff also carries feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847's commits (7 files, 253 production Python lines). In short, as they stand at005924d3a:kernelforge/knowledge/pr_knowledge/inclusion.pyanswersincludedorunknown, withmethodsubject,merge_commitornone.subject: a commit reachable from the checked commit whose subject ends in(#N)(alone or in a run such as(#N) (#M)) or starts withMerge pull request #N from;(#N)anywhere else in the subject, or only in the body, does not count.merge_commit, only when a merge commit sha is given:rev-list --cherry-pickfinds every non-merge commit that merge commit brought in already in the tree, by sha or by patch-id. Each git call has a 15 s timeout, and a git call that fails, cannot start or times out answersunknownwith the error as the evidence. Nothing is fetched.pr_monitorMCP server (mcp_server.py, its name since refactor(kernelforge): reorganize evaluation, shared tools, MCP servers and the test root #1907) gains a fourth tool,pr_check_inclusion. It takesnumber(integer) andtree(string), both required, and an optionalcommitthat defaults toHEAD. It answers from subjects alone, because nothing the agent can call returns a merge commit sha. It returnsnumber,tree,commit,inclusion,methodandevidence.docs/kernelforge/reference/cli.mddescribes it under--pr-kb.test_inclusion.py(33 tests, new),test_mcp_server.py(the tool count goes from 3 to 4) andtest_pr_kb_injection_point.py(the registered tool set gainsmcp__pr_monitor__pr_check_inclusion).test_stdio_tool_schemas.pyis not changed: it is parametrized over every tool in the server'sTOOL_DEFINITIONS, so it covers the new tool's schema as it stands.Follow-up, not in this PR: review-pr's
fetch.shcompares a PR against the newest 100 open PRs on their first 100 files each, and names the restPARTIAL.overlap.shpages every open PR and re-lists the cut and renamed ones. One open-PR overlap script, shared by both skills, would serve both. Also not in this PR:overlap.shstops a repository at the first failed page, and on a large repository a 100-PR page that also lists 100 files per PR can fail with HTTP 502 (all three smoke runs below hit one). Retrying a failed page, or retrying it at a smaller size, would let the scan get further.Linked issue(s): none.
Tests: added/updated? commands run? (what to test; if a test was replaced, where its assertions live now):
New
scripts/tests/test_check_prior_art.py(10 tests). It runs the three scripts end to end withbash, against a stubghonPATH. The stub answers from a fake GitHub, applies--jqwith the realjq, and answersgh pr viewwith only the--jsonfields asked for, as gh does. A stubsleepreturns at once. The module is skipped whenjqis missing.The tests pin these contracts:
mergedcomes frommerged_at, and a tab in a title cannot break a column;queries.tsvasTRUNCATED,INCOMPLETE,FAILED(with gh's error) orOK, with its counts; a failed query does not stop the rest, and comment and blank lines inrepos.txtare skipped;/directories, and each repository endsCOMPLETEwith its counts, orSKIPPED;PARTIAL, 10,000 of 10,050;old->new, a planned path at file 101 is found, and a rename elsewhere is no row;PARTIALline;PARTIAL100 of 250, and the next repository is still scanned;repos.txtstops the scan and names the line;SKIPPED: diffwith gh's error and "2 of 2" files, whilefiles.txtkeeps every path andxrefs.tsvkeeps only the PR cross-references;pr.jsonholds themergeCommitthatread.shasks gh for, null for an open PR, and the diff line's count comes from thechangedFilesit asks for;The
scan.txtassertions compare each line's status, its repository or PR, and the numbers it states, not its wording. The inclusion logic is not re-tested here; its tests come with feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847.No test pins a quota wait, a pre-written
PENDINGrow or a merge sha that is read only for a merged PR: the scripts no longer do any of these, so the tests and mutation checks that covered them were removed with that code.pr.json'smergeCommitis gh's own field, null until the PR merges, and the deep-read test pins that it is requested and null for an open PR.Mutation checks, re-run against the scripts at
75d77c943. Each of these 14 made at least one of the 10 tests fail:TRUNCATEDstatus;merged_at;COMPLETEat the page cap;PARTIALline for a cut REST list;read.sh;-efromread.sh'sset;mergeCommit, orchangedFiles, fromread.sh's--jsonfields.Commands:
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 src/kernelforge/tests/knowledge/pr_knowledge/ src/kernelforge/tests/test_stdio_tool_schemas.py: 387 passed. The schema test collectstest_tool_schema_has_no_top_level_combinator[pr_check_inclusion].pytest -n 16 scripts/tests: 738 passed, 4 failed. Main at7ac8c1229gives 728 passed and the same 4 failed: Node-harness tests intest_code_metrics.py, which fail withReferenceError: require is not definedunder this environment's Node 12.ruff checkandruff format --checkon the test file and the Python files feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847 changes: clean.pre-commit run --fileson this PR's 8 files: shellcheck, reuse, gitleaks, codespell, check-broad-suppress, check-cli-references and ruff-format pass. Theruffhook wrapper rejects its own duplicated--force-excludefor any Python file in this checkout, the same as on main, so ruff was run directly.lint-imports: 10 kept, 0 broken.python scripts/code_metrics.py --base-ref origin/main: PASSED, 15 files judged. Against feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847's head: PASSED, 8 files judged.python scripts/check_cli_references.py: exit 0 (the skill'spython -m kernelforge.knowledge.pr_knowledge.inclusionresolves).python scripts/check_broad_suppress.pyon the test file,inclusion.pyandmcp_server.py: exit 0.python scripts/pr_hygiene.py size: 253 production Python lines against main, 0 against feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847's head.Live read-only smoke run of the scripts at
75d77c943against a large public upstream repository:search.shwith two phrases sent 4 queries in 15 s: 2OKand 2TRUNCATED(100 of 105, 100 of 202). It found 247 candidates.overlap.sh, with one planned file and one planned directory, ran three times. Each run hit a GraphQL page that GitHub answered with HTTP 502, after 700, 3,200 and 700 of 6,487 open PRs. Each run exited 0, kept the rows already compared (72, 288 and 72 overlapping PRs), and wrotePARTIAL: <repo>: the scan stopped after N of 6487 open PRs -- gh: HTTP 502. That line makes the report-- incomplete, see Coverage. None of the three runs reachedCOMPLETE, and a fourth, run while checking this description, stopped the same way after 2,700 of 6,490 open PRs (23 overlapping rows kept, exit 0).read.shon two merged PRs wrote apr.jsonwithstateMERGEDand amergeCommit.oid. One of them had 2 linking PRs inxrefs.tsv. On an open PR,mergeCommitwas null. All three diffs were read.included(exit 0, methodsubject), on a commit whose subject is the PR's title followed by(#N);pr.json:unknown(exit 3, methodnone), the evidence saying that the 1 non-merge commit each merge commit brought in is not in the tree by sha or by patch-id;unknown(exit 3), "no merge commit sha was given".Step 3's path log and Step 5's title check, on this repository, which lands PRs both as squash and as merge commits:
src/kernelforge/knowledge/pr_knowledge/references.pyfrom5b4ac6fae~1toa56028d11listsMerge pull request #1846 from ...with--first-parent, and without it only the PR's own commit, which names no PR;includedby subject for feat(pr_knowledge): carry PR state, draft, merge time and base branch #1846 ata56028d11, naming the merge commitfc25a6d73, and the first line of that merge commit's body is the PR's title;git show --name-onlyon it lists the same 4 files as its PR.On a scratch upstream whose merge commit is
Merge pull request #5 from dev/topicwith the PR's title in its body, the probe answersincludedby subject and Step 5's check finds the title in that body. On a scratch fork, the probe also answersincludedby subject for a commitFork's own work (#5). Its subject is not the PR's title andgit show --name-onlylists none of the PR's files, so Step 5 records it as a collision,unknown. The probe matches numbers; the title check is what tells a fork's own PR apart.Size/complexity triggers crossed: split, or why the shape is right: none.
SKILL.mdis 239 lines, under the 240-line budget it states for itself, as review-pr'sSKILL.mdstates one of 350. Material that only some checks need goes inreferences/gotchas.md(90 lines), and collection goes in the scripts.search.sh70 lines,overlap.sh148,read.sh46 andlib.sh31.lib.shholds what the three share:die, the tool check, the input-line reader, the repository check, and the PR-state jq thatsearch.shandread.shboth apply.overlap.shdefines its path match and row format once, as jq functions that its GraphQL pass and its REST pass both use.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):
--pr-kb, when the repository resolves and the Agent provider supports MCP, the Implementer also getsmcp__pr_monitor__pr_check_inclusion, andpython -m kernelforge.knowledge.pr_knowledge.inclusionbecomes runnable.check-prior-artskill, listed in AGENTS.md. Given a planned change, it writes a work directory that ends inreport.md. The report's verdict is one of six lines:EXISTS-MERGED -- in your treeandEXISTS-OPEN -- in your tree: a DUPLICATE that the probe or the tree's log found in the tree. The next step is to drop the plan, and, for an open PR, to carry results to it.EXISTS-MERGED -- inclusion uncheckedandEXISTS-OPEN -- inclusion unchecked: a DUPLICATE that nothing found in the tree. Local git can show that a PR is present, never that it is absent. The next step is to take the upstream commit, or apply the open PR's diff, and measure it rather than write it again.PARTIAL-OVERLAPandNOT-FOUND. Each reads-- incomplete, see Coveragewhen a query is notOK, ascan.txtline is notCOMPLETE, a shortlisted PR was cut from the deep read, or a step wroteSKIPPED.plan.txt,repos.txt,terms.txt,paths.txt,queries.tsv,candidates.tsv,overlap.tsv,scan.txt,overlap.log,pathlog.txt,shortlist.txt,prs/<owner>/<name>/<N>/(pr.json,files.txt,diff.txt,xrefs.tsv),classes.txt,inclusion.txt,refutations.txtandreport.md. A PR's body, comments and reviews are fields of itspr.json; there are no separate files for them.queries.tsvhas a row for each query that ran, a failed one included, and none for a query a killed run never reached; a repository with noscan.txtline is one whose scan did not end. Step 7 reports either gap asSKIPPED.EXISTS-OPEN -- in your treeonce the log search finds the change. It is not told to apply the PR and measure it again.Merge pull request #N fromsubjects, and a probe match on such a subject counts asincludedwhen the merge commit's body holds the PR's title. A fork's own commit that only shares the PR's number is reportedunknown, notin your tree.in your treerather thaninclusion unchecked.OVERLAPPING, so the verdict isPARTIAL-OVERLAP, notNOT-FOUND.SKIPPEDline;repos.txt, and planned paths that do not exist upstream, which match nothing while the scan saysCOMPLETE.gh, authenticated because the open-PR scan uses GraphQL, andjq.Breaking changes: no.
PR addresses single concern: yes. The skill is one capability; the other commits in the diff are feat(pr_knowledge): probe whether an upstream PR is in a local git tree #1847's, the PR beneath it.
Root cause is upstream (Magpie/TraceLens/GEAK/IntelliKit/AgentKernelArena), ticket filed: no; not applicable.