Skip to content

fix(source-control,review,work-items): paginate every documented GitHub list read and drop the positional-index verifications - #2163

Merged
kyle-sexton merged 10 commits into
mainfrom
fix/truncating-check-runs-query
Aug 11, 2026
Merged

fix(source-control,review,work-items): paginate every documented GitHub list read and drop the positional-index verifications#2163
kyle-sexton merged 10 commits into
mainfrom
fix/truncating-check-runs-query

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

No linked issue

Summary

Every GitHub REST list endpoint this repository's instructions read returns 30 items per page by default and reports nothing when it truncates. A dozen documented call sites read them with no pagination, so the guidance told operators and agents to draw conclusions from silently partial data.

The check-runs case, and the false conclusion it already caused

Reproduced deterministically against this repository's own PR heads with the bare form:

2895890c: total_count=33, returned=30
580fd090: total_count=33, returned=30
435b2fef: total_count=33, returned=30

On all three the dropped set was identical — the three earliest-started checks:

2026-08-10T02:38:01Z  GitGuardian Security Checks
2026-08-10T02:38:03Z  pr-issue-linkage / pr-issue-linkage
2026-08-10T02:38:04Z  do-not-merge / do-not-merge

do-not-merge / do-not-merge is a required status context for main (ruleset 17989001). It is a metadata-only pull_request_target job that reads label metadata and runs no head code, so it completes in about three seconds — well before the heavy pull_request matrix. That is exactly why it is always among the first started, and therefore always the first truncated away.

A prior reading of this query concluded that do-not-merge "never attaches to a head SHA" on three separate PRs, and recorded it as an established finding. It was false. On 2895890c the context attached at 02:38:04Z, completed success at 02:38:07Z, and was green on every one of those heads. The query was truncating.

Under 31 total checks nothing truncates, which is why earlier occurrences looked like intermittent flakiness that resolved itself.

The comment and review case, which is worse

issues/<n>/comments, pulls/<n>/comments, and pulls/<n>/reviews are returned oldest-first (verified — ascending created_at / submitted_at). An unpaginated read therefore drops exactly the newest items: the only ones a monitoring poll or a "did my reply post?" check cares about.

Two call sites paired that list with .[-1]. That shape does not omit — it answers, plausibly, and wrongly, because .[-1] on a truncated oldest-first page is the 30th-oldest item:

issue #657  33 comments | true latest 2026-07-22T18:47:05Z | '.[-1]' unpaginated 2026-07-22T07:20:46Z   (11.5 h stale)
issue #502  31 comments | true latest 2026-07-23T23:29:21Z | '.[-1]' unpaginated 2026-07-23T23:28:27Z

Both are issues, not pull requests — the endpoint and the mechanism are identical, but an earlier draft of this PR miscited them as PRs in prose presented as measurement, and that is corrected here and in the shipped docs.

Rule 3 was found the same way, by the same class of defect, in this change set itself: two commands here reduced across pages inside --jq, one of them in the file that states the rule. Gate 5's codex-comment count printed 10 10 10 3 over four pages instead of 33; the work-item-tracker recipe emitted four separately-sorted arrays instead of one sorted list. Both now slurp with jq -s and flatten with .[][]. The rule draws the line that makes it usable: element-wise filters (select, map over .[]) are safe under --paginate because their results concatenate; folds are not.

Fix

Every corrected site uses --paginate with per_page=100, matching the form plugins/source-control/skills/pull-request/scripts/fetch-annotations.sh:111 already used — that script was already correct and is untouched here.

The .[-1] verifications now select on the fix SHA instead, so the query states what it is asserting and cannot be satisfied by another author's comment.

The sentinel

Pagination alone moves the cliff from 30 to 100 rather than removing it. readiness.md gains a Reading GitHub list APIs section stating three rules once, with Gate 1 pointing at it rather than restating:

  1. Paginate every list read.
  2. Never pair a positional index with a list.
  3. Never reduce across pages inside --jq.

For endpoints that report a total, assert against it. Rule 3 is why the naive assertion is wrong: with --paginate, --jq runs per page, so it reports one page at a time —

$ gh api --paginate ".../check-runs?per_page=10" --jq '"total_count=\(.total_count) returned=\(.check_runs|length)"'
total_count=35 returned=10
total_count=35 returned=10
total_count=35 returned=10
total_count=35 returned=5

— so the published form slurps the page stream first:

gh api --paginate "repos/{owner}/{repo}/commits/<sha>/check-runs?per_page=100" \
  | jq -s -r '"total_count=\(.[0].total_count) returned=\([.[].check_runs[]] | length)"'

Verified against a live head at per_page=100 (one page) and at a forced small page size (four pages), reporting total_count=35 returned=35 both times. .[0].total_count is sound because every page repeats the same total.

/annotations is deliberately given its own form rather than the same one: that endpoint returns a bare array with no envelope and no total_count (verified), so the completeness assertion is unavailable there, --paginate is the only guard, and its pages combine with add rather than through a .check_runs wrapper.

Call sites left unchanged, with reasons

  • plugins/kindle-dedrm/skills/manage/scripts/check-drift.sh:76 and its documented twin references/workflow.md:40Satsuoni/DeDRM_tools has 20 releases total and the newest is a prerelease (v10.0.28, index 0), so nothing truncates today. It is also the one site where a mechanical --paginate would be the wrong fix: it would walk every release ever published to find a match that is always on page 1. The right shape there is a bounded per_page=100, which is a different decision from the one this PR makes. Follow-up.
  • plugins/discovery/skills/research/context/discipline.md:91,224 — prose examples, and releases/latest is the correct single-resource form anyway.
  • plugins/source-control/reference/review-discipline.md:108,109 — a prose inventory of which endpoints get read, not runnable commands.
  • docs/topics/autonomy-ignition/PLAN.md:127 — a sanity-check line inside ### Phase 1 … [DONE], a historical record of a completed phase against a single-gate scratch repo, not guidance anyone would copy today.
  • /replies POSTs and issues/comments/<id> single-resource GETs throughout — neither paginates.
  • plugins/source-control/skills/pull-request/scripts/fetch-annotations.sh:111,157 and plugins/source-control/scripts/fetch-all-pr-comments.sh:128,152,177 — already correct.

Nothing unpaginated remains in source-control, review, or work-items that is a list read.

Known-red check — RESOLVED, kept for the record

Current state: changelog-parity-gate is green. #2159 merged, this branch merged main forward, and the gate now passes on the same 267 KB changelog that failed deterministically before. The account below is what the red meant while it lasted; it is retained because a reviewer reading this PR's check history will see those failures and deserves to know they were never this diff's.

What the red was

changelog-parity-gate failed on this PR, and the failure was not caused by this diff.

scripts/check-changelog-parity.sh's has_heading is rendered_lines - | awk '…{exit}' under set -o pipefail. The reader exits on the first match — line 6, since the newest heading is at the top — while the producer keeps writing; past the pipe buffer it takes SIGPIPE (141), pipefail propagates it, and a heading present at line 6 column 1 is reported as absent. plugins/source-control/CHANGELOG.md is 267 KB, the largest in the repo.

It is a race on pipe scheduling rather than a size threshold, and it is sensitive to the awk implementation and host. Measured locally (GNU Awk 5.4.0 under MSYS), only the largest file failed:

source-control  266794 B  rc=141
work-items      115774 B  rc=0
review           43944 B  rc=0

The runner is worse. This PR's actual changelog-parity-gate run failed all three plugins, including the 44 KB one that passes locally:

UNDOCUMENTED BUMP: review went 0.18.0 -> 0.18.1 but plugins/review/CHANGELOG.md has no '## [0.18.1]' entry at head.
UNDOCUMENTED BUMP: source-control went 0.51.4 -> 0.51.5 but plugins/source-control/CHANGELOG.md has no '## [0.51.5]' entry at head.
UNDOCUMENTED BUMP: work-items went 0.35.0 -> 0.35.1 but plugins/work-items/CHANGELOG.md has no '## [0.35.1]' entry at head.

Every one of those three headings is present at line 6, column 1. Independently confirmed per file: the heading list differs from main by exactly one addition, with none deleted, renamed, or absorbed. The gate's own 55 self-tests pass in the same run — no existing fixture is large enough to cross the buffer.

ci-status fails only as the aggregate of that one job. Every other check on this head is green, and all four required contexts attached:

pr-title / pr-title                completed success
do-not-merge / do-not-merge        completed success
ci-status                          completed failure   (aggregate of changelog-parity-gate)
security-review / security-review  completed success

Fixed on main by #2159.

Post-merge validation of #2159, unplanned but worth recording. plugins/source-control/CHANGELOG.md is the largest changelog in the repo and the one that failed deterministically rather than as a race. After merging #2159 forward it passes all three gate modes — --check, --check-bump, --check-order — against the merged tree. That is independent evidence the fix closed the class rather than moving the boundary.

One correction to the mechanism as first written here: the discriminator is the awk engine and host, not file size. CI resolves awk to gawk, which chunks its writes, so the reader's early exit strands every later chunk; mawk cannot produce the failure at any size. That is why a 44 KB changelog failed on CI while passing locally under MSYS gawk — and why the local byte numbers above should not be read as a threshold.

Test plan

  • Bare and corrected check-runs forms run against live heads 2895890c, 580fd090, 435b2fef, 193c9d2e, 043d60ce; dropped sets computed by jq set difference, not read off a list.
  • Published sentinel run verbatim at per_page=100 and at a forced small page size; multi-page behaviour of --jq exhibited, not asserted.
  • /annotations response shape checked directly (type=array, has_total_count=false); published annotations form run against a check run carrying one annotation.
  • Ordering of all three comment/review endpoints verified ascending; #657 and #502 figures re-derived.
  • Every replacement query run against live data with a positive match, not just a clean exit.
  • scripts/check-changelog-parity.sh --check and --check-order pass; --check-bump fails for the reason above.
  • scripts/check-cross-plugin-source-drift.sh rc=0; scripts/check-contract-clause-coverage.py passes.
  • markdownlint-cli2 clean over 78 files.
  • Diff is .md and .json only — no shell files touched, so no shellcheck run applies.
  • CHANGELOG heading lists diffed against main: exactly one heading added per file, none deleted, renamed, or absorbed.

Related

kyle-sexton and others added 3 commits August 10, 2026 11:50
…and assert completeness

The repository instructed operators and agents to run
`gh api repos/{owner}/{repo}/commits/<sha>/check-runs` with no
pagination. The endpoint returns 30 results per page by default and
reports nothing when it truncates, so on any PR carrying more than 30
check runs the response is a silent partial list — and "is check X
present?" answers a false *no* for every check that landed on a page
the caller never fetched, indistinguishable from a check that never
attached.

Reproduced deterministically against this repo's own PR heads:

    2895890: total_count=33, returned=30
    580fd09: total_count=33, returned=30
    435b2fe: total_count=33, returned=30

On all three, the dropped set was `GitGuardian Security Checks`,
`pr-issue-linkage / pr-issue-linkage`, and `do-not-merge /
do-not-merge` — the three earliest-started checks. `do-not-merge /
do-not-merge` is a required status context for `main`, and a prior
reading of this query concluded it "never attaches to a head SHA". It
attached and was green on every one of those heads.

Both call sites now use `--paginate` with `per_page=100`, matching the
form `skills/pull-request/scripts/fetch-annotations.sh` already used.

Pagination alone moves the cliff to 100 rather than removing it, so
both sites also document a completeness assertion — `total_count`
against the flattened count across every page — plus the trap that
makes the naive form of that assertion wrong: with `--paginate`, `--jq`
runs per page, so `.check_runs | length` prints one line per page, each
reporting only its own page's count. The published assertion slurps the
page stream before comparing, and was verified against a live head both
at `per_page=100` (single page) and at a forced small page size
(four pages), reporting `total_count=35 returned=35` in both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ub list read and drop the positional-index verifications

Every GitHub REST list endpoint these plugins document returns 30 items
per page by default and reports nothing when it truncates. The repo's
instructions read a dozen of them with no pagination.

check-runs — reproduced deterministically against this repo's own heads:

    2895890: total_count=33, returned=30
    580fd09: total_count=33, returned=30
    435b2fe: total_count=33, returned=30

On all three the dropped set was `GitGuardian Security Checks`,
`pr-issue-linkage / pr-issue-linkage`, and `do-not-merge /
do-not-merge` — the three earliest-started checks. `do-not-merge /
do-not-merge` is a required status context for `main`, and a prior
reading of this query concluded it "never attaches to a head SHA". It
attached and completed `success` in three seconds on every one of those
heads.

Comment and review reads are worse, because they are ordered
OLDEST-FIRST (verified: `issues/<n>/comments` and `pulls/<n>/reviews`
both return ascending `created_at`/`submitted_at`). An unpaginated read
therefore drops exactly the newest items — the only ones a monitoring
poll or a "did my reply post?" check cares about. Two call sites paired
that list with `.[-1]`, which does not omit but answers wrongly:

    #657  33 comments | true latest 2026-07-22T18:47:05Z
                      | '.[-1]' unpaginated 2026-07-22T07:20:46Z
    #502  31 comments | true latest 2026-07-23T23:29:21Z
                      | '.[-1]' unpaginated 2026-07-23T23:28:27Z

Every corrected site uses `--paginate` with `per_page=100`, matching the
form `skills/pull-request/scripts/fetch-annotations.sh` already used.
The `.[-1]` verifications now select on the fix SHA, so the query states
what it asserts and cannot be satisfied by another author's comment.

Pagination alone moves the cliff rather than removing it, so
`readiness.md` gains a `Reading GitHub list APIs` section carrying both
rules — paginate every list read; never pair a positional index with a
list — plus the completeness assertion for endpoints that report a
total. That assertion has a trap: with `--paginate`, `--jq` runs per
page, so `.check_runs | length` prints one line per page, each counting
only its own page. The published form slurps the page stream first and
was verified against a live head at `per_page=100` (one page) and at a
forced small page size (four pages), reporting `total_count=35
returned=35` both times.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ion that endpoint cannot carry

The annotation-gap guidance told the auditor to paginate "each
check-run's /annotations" the same way, then published only a
check-runs-shaped completeness assertion. That assertion is not
executable against /annotations: the endpoint returns a bare JSON array
with no envelope and no `total_count`, so there is nothing to compare
the flattened count to, and concatenated pages combine with `add`
rather than through a `.check_runs` wrapper.

Both endpoints now carry a runnable form of their own, and the
difference between them is stated rather than elided — check-runs
asserts `total_count`, /annotations cannot, so there `--paginate` is
the only guard. Verified live against a check run carrying one
annotation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@claude

claude Bot commented Aug 10, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 25s —— View job


Review complete

  • Gather context (PR body, changed files list)
  • Review diff for each changed file
  • Check alignment with CLAUDE.md / repo instructions
  • Post inline findings
  • Post final summary

Summary

This PR does what it says: every documented GitHub list-read call site in source-control, review, and work-items now carries --paginate with per_page=100, and the two .[-1]-on-oldest-first verifications are replaced with SHA-based selectors. I confirmed by diffing origin/main...HEAD and reading each changed file directly (not just the PR body's described commands) — I did not re-run the author's gh api reproductions against live PR heads myself, so those specific numbers (total_count=33/returned=30, the #657/#502 timestamps) are author-claimed and unverified by me.

The new "Reading GitHub list APIs" section in readiness.md correctly calls out the sharper trap: with --paginate, --jq runs per page, so an aggregating filter (length, sort_by) must be applied via an external jq -s after slurping the page stream, not via --jq itself. That fix is applied correctly to the check-runs sentinel (readiness.md, ci-log-auditor.md) and to the /annotations fetch. However, two other call sites in this same diff add --paginate to an aggregating --jq filter without slurping first — reintroducing the exact class of bug the PR is otherwise fixing (on inline comments, at readiness.md#L149-L150, and on issue comments, at README.md#L101-L104). Flagged both inline with suggested fixes.

Everything else checked out:

  • Version bumps (0.18.0→0.18.1, 0.51.4→0.51.5, 0.35.0→0.35.1) and CHANGELOG entries accurately describe the diffs, one heading added per file.
  • monitor.md's poll loop correctly keeps non-aggregating --jq filters (.[] | "...") as-is under --paginate — no bug there, since per-item iteration concatenates safely across pages.
  • The PR's own explanation of why --annotations needs a different combinator (add over a bare array, no total_count) matches what's actually in the diff.
  • The "call sites left unchanged" list in the PR body (kindle-dedrm, discovery prose, review-discipline prose inventory, PLAN.md historical record, already-correct fetch-annotations.sh/fetch-all-pr-comments.sh) is consistent with what's actually in scope here — I didn't find any other unpaginated list read across the 12 changed files.
  • The known-red changelog-parity-gate failure is plausible given the described SIGPIPE mechanism and is out of scope for this diff (.md/.json only, no shell files touched).

@claude

claude Bot commented Aug 10, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 29s —— View job


Security review complete

  • Gather context (PR diff, changed files)
  • Review each changed file for security issues (injection, auth gaps, unsafe token/secret handling, dangerous Actions patterns)
  • Post findings as PR review

No security issues found.

This PR is documentation-only — 12 files, all .md (agent/skill/reference prose and CHANGELOG.md entries) and .claude-plugin/plugin.json version bumps. No shell scripts, no .github/workflows/* files, and no executable code paths are touched. Confirmed via git diff origin/main...HEAD --stat.

Reviewed for the categories in scope:

  • Injection surfaces (command/jq): the gh api/jq examples in monitor.md, readiness.md, review-discipline.md, and ci-log-auditor.md only add --paginate/per_page=100 and swap a .[-1] positional index for a property-based select(...) filter (matching on a SHA or in_reply_to_id). The pre-existing variable interpolation patterns ($last_comment_ts, <original-id>, <sha>) are unchanged in shape and were already present on main — this PR doesn't alter that risk surface.
  • Auth/access-control: no changes to identity, permission, or merge-authority logic. The reply-routing and thread-resolution rules (bot vs. human, self-resolution bans) are untouched.
  • Secrets/tokens: none referenced or handled differently; no new credentials, no logging of sensitive values.
  • GitHub Actions patterns: no workflow files in the diff, so pull_request_target/workflow_run trust-boundary concerns don't apply here.
  • Supply chain / pins: only plugin.json version fields bump (semver strings); no dependency or action pin changes.

The substance of the change — fixing silent pagination truncation and replacing an unsound .[-1] "verification" with a positive, content-based match — is itself a correctness/integrity improvement to the guidance (it closes a false-negative gap where a required check or a posted reply could be wrongly reported as missing), not a new risk.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8375296eb0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/work-items/tools/work-item-tracker/adapters/github/README.md Outdated
Comment thread plugins/source-control/skills/pull-request/reference/readiness.md Outdated
Comment thread plugins/source-control/skills/pull-request/SKILL.md Outdated
Comment thread plugins/source-control/skills/pull-request/reference/readiness.md Outdated
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

Claude has reviewed this PR 5 times. The lane skips further automatic reviews after 5; deleting this comment resets the count.

@claude

claude Bot commented Aug 10, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 3m 39s —— View job


Review complete

  • Gather context (diff against origin/main, prior review comments on this PR)
  • Check whether previously-flagged aggregation-under---paginate findings are still present
  • Review remaining changed files for correctness / alignment with CLAUDE.md
  • Post inline findings
  • Post final summary

Summary

I re-diffed origin/main...HEAD (still 138/-23 across the same 12 files as the PR description; the current head 0ad6c62 is a no-op merge of main into this branch, so the reviewable content is unchanged from earlier passes) and read every changed file directly rather than relying on the PR body's or prior comments' descriptions.

Two aggregation-under---paginate bugs already flagged by Codex and a prior review pass are still present and unfixed:

Both are the exact class of bug this PR is otherwise systematically fixing (it says so itself, two paragraphs above the second site: "--jq runs per page"), and both already have a fix pattern demonstrated elsewhere in this same diff (the check-runs sentinel in readiness.md, the /annotations fix in ci-log-auditor.md) — external jq -s after slurping the page stream. I independently re-verified both are still live in the current diff and posted inline comments confirming them with the slurp-based fix.

One correctness gap Codex flagged that I independently verified and extended to a second, identical call site:

Everything else checked out:

  • monitor.md's three review-surface polls correctly use non-aggregating, per-item --jq '.[] | "..."' filters under --paginate — safe across pages, no bug.
  • review-discipline.md's D5 verify (select(.in_reply_to_id == ...)) is also non-aggregating and safe.
  • ci-log-auditor.md's check-runs/annotations fix correctly slurps pages before asserting total_count/length, matching the documented sentinel.
  • Version bumps (review 0.18.0→0.18.1, source-control 0.51.4→0.51.5, work-items 0.35.0→0.35.1) and their CHANGELOG entries accurately describe what's actually in the diff — I diffed each CHANGELOG against the corresponding file changes to confirm this, rather than taking the entries at face value.
  • The "call sites left unchanged" list in the PR body is consistent with what's actually in scope — I didn't find any other unpaginated list read, or any other aggregating --jq under --paginate, across the 12 changed files.

I did not re-run the author's gh api reproductions against live PR heads or issues — the total_count=33/returned=30 figures, the #657/#502 timestamps, and the changelog-parity-gate SIGPIPE explanation are author-claimed and unverified by me; I verified them by reading the diffs, not by reproducing the underlying API calls.

@claude

claude Bot commented Aug 10, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 27s —— View job


Security review complete

  • Gather context (PR diff via git diff origin/main...HEAD, changed files)
  • Review each changed file for security issues (injection, auth gaps, unsafe token/secret handling, dangerous Actions patterns)
  • Post findings

No CRITICAL or IMPORTANT security issues found.

Scope confirmed via git diff origin/main...HEAD --stat: 12 files, all .md (skill/agent/reference prose, CHANGELOG.md entries) and .claude-plugin/plugin.json version bumps. No .github/workflows/* files, no shell scripts, no executable code paths in the diff — this is documentation guiding how an agent should later run gh api/jq commands, not code that runs itself.

Reviewed against the categories in scope:

  • Injection (command/jq): every recipe touched only adds --paginate/per_page=100 to the URL and swaps a .[-1] positional index for a property-based select(...)/contains(...) filter. Pre-existing variable interpolation into the REST path ($last_comment_ts in monitor.md, <original-id>/<sha> placeholders in review-discipline.md and SKILL.md) is unchanged in shape and was already present on main — this PR doesn't widen that surface.
  • Auth / access-control: no changes to identity, permission, merge-authority, or bot-detection logic. The thread-resolution rules (bot vs. human, self-resolution bans) in review-discipline.md and SKILL.md are untouched by this diff.
  • Secrets/tokens: none referenced or handled differently; no credentials logged or newly exposed.
  • GitHub Actions patterns: no workflow files touched, so pull_request_target/workflow_run trust-boundary and permissions: concerns don't apply here.
  • Supply chain / pins: only plugin.json version semver strings bump; no dependency or action pins changed.

One SUGGESTION worth noting (already flagged by @chatgpt-codex-connector on SKILL.md#L183, and mirrored at review-discipline.md#L239 — I concur independently rather than duplicating a fresh finding): the new D7 issue-level verification (select(.body | contains("<sha>")) | .body) checks only comment content, not author. Since anyone who can comment on the PR can cause a comment containing the fix SHA to exist (another reviewer quoting it, a bot restating it, or — in a lower-trust repo than this one — an adversarial commenter), this verification step could report the agent's own required follow-up reply as posted when it was not. That's a real gap in a control gate that an autonomous agent uses to decide whether to proceed (e.g., toward thread resolution / readiness), so it's worth scoping the selector to the posting identity (and ideally a reply-specific marker) rather than body content alone — Severity: SUGGESTION, Confidence: Medium (requires another actor to independently reference the same SHA string, and in this repo's actual usage that's unlikely, but the selector as written doesn't rule it out).

No other authorization, injection, secret-handling, or Actions-trust-boundary issues identified in this diff.

Comment thread plugins/work-items/tools/work-item-tracker/adapters/github/README.md Outdated
Comment thread plugins/source-control/skills/pull-request/reference/readiness.md Outdated
Comment thread plugins/source-control/skills/pull-request/SKILL.md Outdated
Comment thread plugins/source-control/reference/review-discipline.md Outdated
#2135 bumped source-control to 0.51.5 on main — the number this branch
already claimed — so the two 0.51.5 sections collided in the changelog.

Resolved by renumbering this branch's entry to 0.51.6 and keeping main's
0.51.5 under its own heading with its own content. Neither section is
relabelled, absorbed, or dropped: the heading list of all three touched
changelogs differs from main by exactly one addition, and each manifest
is exactly one increment above main.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Aug 10, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 23s —— View job


Review complete

  • Gather context (diff against origin/main, prior review threads on this PR)
  • Check whether previously-flagged findings are still present at current HEAD
  • Review remaining changed files for correctness / alignment with CLAUDE.md
  • Post inline findings
  • Post final summary

Summary

The diff at the current HEAD (948a538, a no-op merge of main — still 138/-23 across the same 12 files) is unchanged in substance from the version reviewed in the prior two passes. I re-verified each site directly by reading the files at this SHA rather than trusting the earlier comments' descriptions.

All three previously-flagged findings are still present, unaddressed:

  1. plugins/work-items/tools/work-item-tracker/adapters/github/README.md:103--paginate paired with an aggregating --jq ([.[] | ...] | sort_by(.id)), which runs per page rather than once overall.
  2. plugins/source-control/skills/pull-request/reference/readiness.md:150 — same shape, Gate 5's codex-comment-count check.
  3. plugins/source-control/skills/pull-request/SKILL.md:183 and its duplicate at plugins/source-control/reference/review-discipline.md:239 — the D7 SHA-in-body selector has no author/identity constraint, so another actor's comment mentioning the fix SHA would false-positive the "did my follow-up post?" check.

I posted inline comments confirming all four locations with suggested fixes (items 1–2 use the external-jq -s-slurp pattern the PR itself already applies to the check-runs sentinel and /annotations; item 3's fix adds an identity constraint to the select(...)).

Everything else checked out and is unchanged from the prior pass:

  • monitor.md's three poll-loop --jq filters (plugins/source-control/skills/pull-request/reference/monitor.md) are non-aggregating, per-item .[] | "..." projections — safe under --paginate since per-item iteration concatenates correctly across pages.
  • ci-log-auditor.md's new check-runs/annotations guidance correctly slurps pages (jq -s) before asserting total_count/length.
  • review-discipline.md's D5 verify (select(.in_reply_to_id == ...)) is non-aggregating and safe.
  • Version bumps (review 0.18.0→0.18.1, source-control 0.51.4→0.51.5, work-items 0.35.0→0.35.1) and CHANGELOG entries still accurately describe the diff.
  • No other unpaginated list read, and no other aggregating --jq under --paginate, found across the 12 changed files.

I did not re-run the author's gh api reproductions against live PR heads or issues — the total_count=33/returned=30 figures, #657/#502 timestamps, and the changelog-parity-gate SIGPIPE explanation remain author-claimed and unverified by me; I verified this pass's findings by reading the diff at 948a538, not by reproducing the underlying API calls.

@claude

claude Bot commented Aug 10, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 15s —— View job


Security review complete

  • Gather context (PR diff via git diff origin/main...HEAD, changed files)
  • Review each changed file for security issues (injection, auth gaps, unsafe token/secret handling, dangerous Actions patterns)
  • Post findings

No CRITICAL or IMPORTANT security issues found.

Scope confirmed at HEAD 948a538: 12 files changed, all .md (skill/agent/reference prose, CHANGELOG.md entries) and .claude-plugin/plugin.json version bumps (138/-23, same as prior passes on this PR — content unchanged since the last review, this sync only pulled in unrelated main history via merge). No .github/workflows/* files, no shell scripts, no executable code paths — this is documentation instructing an agent how to later run gh api/jq, not code that runs itself.

Reviewed against the categories in scope:

  • Injection (command/jq): every touched recipe only adds --paginate/per_page=100 to the URL and swaps a .[-1] positional index for a property-based select(...) filter. Pre-existing variable interpolation into the REST path ($last_comment_ts, $OWNER, $REPO in monitor.md, <original-id>/<sha>/$HEAD_SHA placeholders in review-discipline.md and SKILL.md) is unchanged in shape from main — this PR doesn't widen that surface.
  • Auth / access-control: no changes to identity, permission, merge-authority, or bot-detection logic. Thread-resolution rules (bot vs. human, self-resolution bans) are untouched.
  • Secrets/tokens: none referenced or handled differently; no credentials logged or newly exposed.
  • GitHub Actions patterns: no workflow files touched — pull_request_target/workflow_run trust-boundary and permissions: concerns don't apply.
  • Supply chain / pins: only plugin.json version semver strings bump (review 0.18.0→0.18.1, source-control 0.51.5→0.51.6, work-items 0.35.0→0.35.1); no dependency or action pins changed.

One SUGGESTION, confirmed independently and posted inline at SKILL.md#L183 and its duplicate at review-discipline.md#L239 (originally raised by @chatgpt-codex-connector, mirrored by prior review passes on this PR — I re-verified it's still present at current HEAD rather than re-reporting on trust): the new D7 issue-level verification (select(.body | contains("<sha>")) | .body) checks comment content only, with no constraint on posting identity. Since anyone who can comment on the issue could cause a comment containing the fix SHA to exist, this selector can report the agent's own required follow-up as posted when it wasn't — a false positive on a control gate an autonomous agent uses to decide whether to proceed. Severity: SUGGESTION, Confidence: Medium (requires another actor to independently reference the same SHA string; unlikely in this repo's actual usage, but the selector as written doesn't rule it out).

Two other findings live in review comments on this PR (aggregating --jq under --paginate at readiness.md:150 and work-items/.../github/README.md:103, still unresolved as of this HEAD) are correctness/reliability bugs, not security issues, so out of scope for this lane — they're already tracked by the general code-review passes on this PR.

No other authorization, injection, secret-handling, or Actions-trust-boundary issues identified in this diff.

Comment thread plugins/source-control/skills/pull-request/reference/readiness.md Outdated
Comment thread plugins/source-control/skills/pull-request/SKILL.md Outdated
Comment thread plugins/source-control/reference/review-discipline.md Outdated
Comment thread plugins/source-control/skills/pull-request/SKILL.md Outdated
Comment thread plugins/source-control/reference/review-discipline.md Outdated
kyle-sexton and others added 2 commits August 11, 2026 18:07
…q, and correct two miscited items

Two commands in this change set broke the rule the change set
publishes, one of them ~90 lines below where the file states it.

With `--paginate`, `gh` applies `--jq` to each page SEPARATELY, so any
expression that folds a whole list answers per page:

  * `readiness.md` Gate 5 counted codex comments with
    `--jq '[…] | length'`. Forced to four pages that printed
    `10 10 10 3` instead of `33`.
  * `work-item-tracker/adapters/github/README.md` — a command this
    change set itself rewrote — reduced with `--jq '[…] | sort_by(.id)'`
    and emitted four separately-sorted arrays, each ordered only within
    its own page.

Both now drop `--jq`, slurp the page stream with `jq -s`, and flatten
with `.[][]`. Verified against a live 33-comment item forced to four
pages: both corrected forms return the true total, the originals
reproduce the per-page output above.

The trap is promoted from a sentence inside the completeness-assertion
prose to rule 3 of `Reading GitHub list APIs`, since it is a distinct
failure from the two rules already there and is what both violations
broke. It also draws the line the other two rules do not: element-wise
filters (`select`, `map` over `.[]`) are safe under `--paginate`
because their results concatenate; folds are not.

Also corrected: #657 and #502 are ISSUES, not pull requests
(`gh api …/issues/657` carries no `.pull_request`). They were miscited
as PRs in `readiness.md` and the source-control changelog, in prose
presented as measurement. The endpoint and the mechanism are identical,
but measurement that misnames its subject invites doubt about the rest.

The `33–37 check runs` range is left as written; it is substantiated
across five measured heads — 33, 33, 33, 35, 37.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Second merge-forward. main reached d18075e and had taken every number
this branch claimed: source-control 0.51.6 and 0.51.7, review 0.18.2,
work-items 0.35.1.

Renumbered above what main holds — 0.51.8, 0.18.3, 0.35.2 — with every
main section kept under its own heading and its own content. work-items
needed the most care: both sides wrote under a shared `## [0.35.1]`
heading, so main's content stays under 0.35.1 and this branch's moves
to a new 0.35.2 above it, rather than the two being merged into one
section.

Verified per file: the heading list differs from main by exactly one
addition, none deleted, renamed, or absorbed; each manifest is exactly
one increment above main; no conflict marker of any of the four forms
survives anywhere in the tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Aug 11, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 40s —— View job


Security review complete

  • Gather context (gh pr diff 2163, changed files)
  • Review each changed file for security issues (injection, auth gaps, unsafe token/secret handling, dangerous Actions patterns)
  • Post findings

No CRITICAL or IMPORTANT security issues found.

Scope confirmed via gh pr diff 2163 --repo melodic-software/claude-code-plugins at HEAD 9fe2d54: 12 files, all .md (skill/agent/reference prose, CHANGELOG.md entries) and .claude-plugin/plugin.json version bumps. No .github/workflows/* files, no shell scripts, no executable code paths in the diff — this is documentation instructing an agent how to later run gh api/jq commands, not code that runs itself.

Reviewed against the categories in scope:

  • Injection (command/jq): every touched recipe only adds --paginate/per_page=100 to the URL and swaps a .[-1] positional index for a property-based select(...) filter, or moves an aggregating --jq fold (sort_by, length) out to an external jq -s slurp. Pre-existing variable interpolation into the REST path/query string ($OWNER, $REPO, $PR_NUMBER, $last_comment_ts in monitor.md, <original-id>/<sha>/$HEAD_SHA placeholders in review-discipline.md and SKILL.md) is unchanged in shape from main and $last_comment_ts is agent-generated (an ISO-8601 timestamp from the prior poll), not attacker-controlled input — this PR doesn't widen that surface.
  • Auth / access-control: no changes to identity, permission, merge-authority, or bot-detection logic. Thread-resolution rules (bot vs. human, self-resolution bans) are untouched.
  • Secrets/tokens: none referenced or handled differently; no credentials logged or newly exposed.
  • GitHub Actions patterns: no workflow files touched — pull_request_target/workflow_run trust-boundary and permissions: concerns don't apply; deferring to zizmor's advisory lane for anything statically coverable regardless.
  • Supply chain / pins: only plugin.json version semver strings bump (review 0.18.2→0.18.3, source-control 0.51.7→0.51.8, work-items 0.35.1→0.35.2); no dependency or action pins changed.

One SUGGESTION, still unresolved at current HEADSKILL.md#L183 and its duplicate at review-discipline.md#L165 (originally raised by @chatgpt-codex-connector, confirmed independently across multiple prior review passes on this PR — re-verified present at this HEAD rather than re-reported on trust): the D7 "verify follow-up reply posted" issue-level check —

--jq '.[] | select(.body | contains("<sha>")) | .body'

— matches on comment content only, with no constraint on .user.login or any other posting-identity signal. This is a control gate an autonomous agent uses to conclude a required write succeeded and it may proceed (toward D7.5 thread resolution / the readiness gate). Any other comment on the issue that happens to contain the fix SHA — a reviewer quoting it, a bot restating it, or an adversarial commenter in a lower-trust repo — makes the selector return non-empty and the check reports success even though the agent's own reply was never posted. It's the same failure shape as the .[-1] bug this PR replaces: a plausible-looking positive answer that isn't actually scoped to what it claims to verify.
Severity: SUGGESTION, Confidence: Medium (requires another actor to independently reference the same SHA string — unlikely in this repo's actual single-committer usage, but the selector as written doesn't rule it out).

No other authorization, injection, secret-handling, or Actions-trust-boundary issues identified in this diff. The two aggregation-under---paginate findings from earlier review passes (readiness.md Gate 5, work-items/.../github/README.md) are already fixed at this HEAD via jq -s slurping — confirmed in the current diff — and were correctness/reliability issues rather than security findings regardless.

@claude

claude Bot commented Aug 11, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 31s —— View job


Review complete

  • Gather context (diff against origin/main, prior review threads on this PR)
  • Check whether previously-flagged findings are still present at current HEAD
  • Review remaining changed files for correctness / alignment with CLAUDE.md
  • Post inline findings
  • Post final summary

Summary

Reviewed the diff at current HEAD 9fe2d54 (12 files, +158/-25, via gh pr diff 2163) against the three prior review passes on this PR.

Progress since the last review pass: the two more severe correctness bugs are now fixed.

I independently re-verified both fixes by reading the current file contents, and grepped all six changed reference/skill files for any remaining --paginate + aggregating---jq pairing (length, sort_by, add, max, group_by) — none found. I also grepped for any remaining live .[-1] usage — none found; the two remaining mentions are prose explaining the old bug, not code.

One finding, already raised by Codex and three prior review passes on this PR, is still unresolved — reconfirmed and reposted inline since it wasn't touched by the commits that fixed the two items above:

  • plugins/source-control/skills/pull-request/SKILL.md:183 and its duplicate at plugins/source-control/reference/review-discipline.md:239 — the D7 "verify follow-up reply posted" selector (select(.body | contains("<sha>"))) checks comment content only, with no author/identity constraint, so another actor's comment mentioning the fix SHA can false-positive the check. Severity: Suggestion (a correctness/reliability gap in a control-gate check, not a security issue — out of this lane's security scope per REVIEW.md, and I'd size it as Suggestion given it requires another actor to independently reference the same SHA string).

Everything else checked out and is unchanged from the prior passes:

  • monitor.md's poll-loop --jq filters and readiness.md's Gate 1/4 review-surface reads are element-wise (.[] | select(...) | "..."), which concatenate safely across pages under --paginate — no bug.
  • ci-log-auditor.md's new check-runs/annotations guidance correctly slurps pages (jq -s) before asserting total_count/length, matching the pattern the PR describes.
  • Version bumps (review 0.18.2→0.18.3, source-control 0.51.7→0.51.8, work-items 0.35.1→0.35.2) and their CHANGELOG entries accurately describe what's in this diff.
  • No unpaginated list read and no other aggregating --jq under --paginate found across the 12 changed files.

I did not re-run the author's gh api reproductions against live PR heads or issues — the total_count=33/returned=30 figures, the #657/#502 timestamps, and the changelog-parity-gate SIGPIPE explanation remain author-claimed and unverified by me; I verified this pass's findings by reading the diff and file contents at 9fe2d54, not by reproducing the underlying API calls. Per this repo's REVIEW.md, security findings are scoped to the dedicated security lane and are out of scope here.
· branch fix/truncating-check-runs-query

Comment thread plugins/source-control/skills/pull-request/SKILL.md Outdated
Comment thread plugins/source-control/reference/review-discipline.md Outdated
Fourth merge-forward, onto fd7ffc4. No conflict this time: main's
versions (source-control 0.51.7, review 0.18.2, work-items 0.35.1) are
all still below the numbers this branch already claimed, so 0.51.8 /
0.18.3 / 0.35.2 stand unchanged.

Verified rather than assumed, since a merge-forward can silently change
a file already signed off: `git diff 9fe2d54 HEAD` over
plugins/source-control, plugins/review, and plugins/work-items is
EMPTY. Everything main brought in lands in guardrails and
markdown-format, which this branch does not touch.

Heading lists still differ from main by exactly one addition per file;
no conflict marker of any of the four forms survives; all three parity
modes pass against the merged tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Aug 11, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 29s —— View job


Security review complete

  • Gather context (gh pr diff 2163, changed files)
  • Review each changed file for security issues (injection, auth gaps, unsafe token/secret handling, dangerous Actions patterns)
  • Post findings

No CRITICAL or IMPORTANT security issues found.

Scope confirmed via gh pr diff 2163 --repo melodic-software/claude-code-plugins at HEAD 88dc411: 12 files, all .md (skill/agent/reference prose, CHANGELOG.md entries) and .claude-plugin/plugin.json version bumps. No .github/workflows/* files, no shell scripts, no executable code paths in the diff — this is documentation instructing an agent how to later run gh api/jq commands, not code that runs itself. I also diffed 9fe2d54 (the SHA reviewed by the prior security pass) against this HEAD directly (git diff 9fe2d54 88dc411 -- plugins/review plugins/source-control plugins/work-items) and confirmed it is empty — the current head is a no-op merge of main, so the reviewable content is byte-for-byte unchanged from the last security review.

Reviewed against the categories in scope:

  • Injection (command/jq): every touched recipe only adds --paginate/per_page=100 to the URL, swaps a .[-1] positional index for a property-based select(...) filter, or moves an aggregating --jq fold (sort_by, length) out to an external jq -s slurp. Pre-existing variable interpolation into the REST path/query string ($OWNER, $REPO, $PR_NUMBER, $last_comment_ts in monitor.md, <original-id>/<sha>/$HEAD_SHA placeholders in review-discipline.md and SKILL.md) is unchanged in shape from main, and $last_comment_ts is agent-generated (an ISO-8601 timestamp from the prior poll iteration), not attacker-controlled input — this PR doesn't widen that surface. Gate 5's fix in readiness.md#L305-L307 actually tightens this: $HEAD_SHA now passes through jq -s --arg sha "$HEAD_SHA" instead of string interpolation into the filter expression — a strictly safer pattern than what it replaced.
  • Auth / access-control: no changes to identity, permission, merge-authority, or bot-detection logic. Thread-resolution rules (bot vs. human, self-resolution bans) are untouched.
  • Secrets/tokens: none referenced or handled differently; no credentials logged or newly exposed.
  • GitHub Actions patterns: no workflow files touched — pull_request_target/workflow_run trust-boundary and permissions: concerns don't apply; deferring to zizmor's advisory lane for anything statically coverable regardless.
  • Supply chain / pins: only plugin.json version semver strings bump (review 0.18.2→0.18.3, source-control 0.51.7→0.51.8, work-items 0.35.1→0.35.2); no dependency or action pins changed.

One SUGGESTION, still unresolved at current HEADSKILL.md#L183 and its duplicate at review-discipline.md#L239 (originally raised by @chatgpt-codex-connector, reconfirmed across five prior review passes on this PR): the D7 "verify follow-up reply posted" issue-level check —

--jq '.[] | select(.body | contains("<sha>")) | .body'

— matches on comment content only, with no constraint on .user.login or any other posting-identity signal. This is a control gate an autonomous agent uses to conclude a required write succeeded and it may proceed (toward D7.5 thread resolution / the readiness gate). Any other comment on the issue that happens to contain the fix SHA — a reviewer quoting it, a bot restating it, or an adversarial commenter in a lower-trust repo — makes the selector return non-empty and the check reports success even though the agent's own reply was never posted. It's the same failure shape as the .[-1] bug this PR replaces elsewhere: a plausible-looking positive answer that isn't actually scoped to what it claims to verify.
Severity: SUGGESTION, Confidence: Medium (requires another actor to independently reference the same SHA string — unlikely in this repo's actual single-committer usage, but the selector as written doesn't rule it out). This is a correctness/reliability gap in a control-gate check rather than a security vulnerability per this repo's REVIEW.md lane scoping, so it's noted for completeness but doesn't change the "no security issues" verdict above.

No other authorization, injection, secret-handling, or Actions-trust-boundary issues identified in this diff.

@claude

claude Bot commented Aug 11, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 29s —— View job


Review complete

  • Gather context (diff via gh pr diff 2163, prior review threads on this PR)
  • Check whether previously-flagged findings are still present at current HEAD
  • Review remaining changed files for correctness / alignment with CLAUDE.md
  • Post inline findings
  • Post final summary

Summary

Current HEAD is 88dc411, a no-op merge of main. I confirmed the reviewable content (the 12 files in scope) is byte-identical to the last-reviewed SHA 9fe2d54 (git diff 9fe2d54 88dc411 -- plugins/review plugins/source-control plugins/work-items returns empty) — the merge only pulled in an unrelated main commit outside these plugin directories. So this pass re-verifies the same diff rather than reviewing new content.

Progress confirmed since the pre-9fe2d54 passes: both aggregation-under---paginate bugs are fixed and stayed fixed.

I independently re-read both files at this HEAD (not just trusting the prior pass's description), and re-grepped all six changed reference/skill files for any remaining --paginate + aggregating---jq pairing (length, sort_by, add, max, group_by) and for any live .[-1] usage — none found; the two .[-1] mentions remaining are prose explaining the old bug, not code. monitor.md's poll-loop filters and readiness.md Gate 1/4 reads remain correctly element-wise (.[] | select(...) | "..."), which concatenate safely across pages — no bug there.

One finding remains open, unchanged across six review passes now (Codex + five Claude passes on this PR):

Everything else checked out, consistent with prior passes:

  • Version bumps (review 0.18.2→0.18.3, source-control 0.51.7→0.51.8, work-items 0.35.1→0.35.2) and their CHANGELOG.md headings match plugin.json exactly — verified by grep, not just read.
  • ci-log-auditor.md's check-runs/annotations guidance correctly slurps pages before asserting total_count/length, and correctly treats /annotations as a bare-array endpoint combined with add rather than the .check_runs wrapper form.
  • No unpaginated list read and no other aggregating --jq under --paginate found across the 12 changed files.

I did not re-run the author's gh api reproductions against live PR heads or issues — the total_count=33/returned=30 figures, #657/#502 timestamps, and the changelog-parity-gate SIGPIPE explanation remain author-claimed and unverified by me; I verified this pass's findings by reading the diff and file contents at 88dc411, not by reproducing the underlying API calls.

Comment thread plugins/source-control/skills/pull-request/SKILL.md Outdated
Comment thread plugins/source-control/reference/review-discipline.md Outdated
kyle-sexton and others added 2 commits August 11, 2026 18:50
…ting identity, not just the SHA

The reviewers are right, and this is the same defect class the PR
exists to fix.

D7's "did my follow-up reply post?" check selected on
`select(.body | contains("<sha>"))`. That proves the SHA was MENTIONED,
not that this agent posted it. A reviewer quoting the fix commit, or a
bot restating it, satisfies the selector — so the gate reports the
reply as posted while the agent's own write silently failed.

That is precisely the shape of the `.[-1]` bug this line replaced: a
plausible positive instead of a real presence signal, on a control gate
an autonomous agent acts on. Replacing one under-constrained selector
with another under-constrained selector fixed the ordering bug and left
the class intact.

Both copies of the checklist step — SKILL.md and the canonical
review-discipline.md — now pin `.user.login` alongside the SHA.
Verified against live data: SHA-only matches, SHA + correct identity
matches, SHA + wrong identity returns empty. The selector stays
element-wise, so it remains safe under `--paginate` per rule 3.

Rule 2 gains the general form rather than just the instance: where a
query is a control gate you will act on, ask what ELSE could satisfy
the selector and constrain that too — one property is usually not
enough.

The two `--jq` folds these threads also flagged were already fixed in
89646d8; the most recent review pass confirms that on this head.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fifth merge-forward, onto 3ccf7df. No conflict: main's versions
(source-control 0.51.7, review 0.18.2, work-items 0.35.1) remain below
the numbers this branch holds, so 0.51.8 / 0.18.3 / 0.35.2 stand.

Re-verified rather than assumed: `git diff fae6f5c HEAD` over
plugins/source-control, plugins/review, and plugins/work-items is
EMPTY. Everything main brought in lands in songwriting.

Heading lists still differ from main by exactly one addition per file;
no conflict marker of any of the four forms survives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Aug 11, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 53s —— View job


Security review complete

  • Gather context (gh pr diff 2163 / git diff origin/main...HEAD, changed files)
  • Review each changed file for security issues (injection, auth gaps, unsafe token/secret handling, dangerous Actions patterns)
  • Post findings

No CRITICAL, IMPORTANT, or SUGGESTION security issues found.

Scope confirmed at HEAD 49ba418: the same 12 files as every prior pass (.md skill/agent/reference prose and CHANGELOG.md entries, plus .claude-plugin/plugin.json version bumps). No .github/workflows/* files, no shell scripts, no executable code paths — this remains documentation instructing an agent how to later run gh api/jq, not code that runs itself. I also diffed the previously-reviewed SHA (88dc411) against this HEAD directly (git diff 88dc411 49ba418 -- plugins/review plugins/source-control plugins/work-items) to isolate what changed since the last security pass, rather than re-reviewing everything from scratch.

The one open finding from every prior security/general review pass on this PR is now fixed. The D7 "verify follow-up reply posted" selector previously matched on SHA-in-body alone with no posting-identity constraint (originally raised by @chatgpt-codex-connector, reconfirmed across six review passes). The new commit on this HEAD adds the identity constraint at both copies of the checklist step:

Both now read:

select((.body | contains("<sha>")) and .user.login == "<posting-identity>") | .body

This closes the gap: a reviewer or another bot quoting the fix SHA can no longer satisfy the control-gate check on its own. <posting-identity> and <sha> remain template placeholders (not live shell interpolation), consistent with the rest of this file's existing pattern — no new injection surface introduced.

Re-verified the other categories in scope, unchanged from prior passes:

  • Injection (command/jq): no new interpolation patterns; all touched recipes still only add --paginate/per_page=100, swap positional indices for property-based select(...), or slurp aggregating filters via jq -s. Pre-existing variable interpolation ($OWNER, $REPO, $HEAD_SHA, $last_comment_ts) is unchanged in shape from main and remains agent-generated, not attacker-controlled.
  • Auth/access-control: no changes to identity, permission, merge-authority, or bot-detection logic beyond the D7 fix above, which tightens an existing control gate rather than loosening anything.
  • Secrets/tokens: none referenced or handled differently.
  • GitHub Actions patterns: no workflow files touched.
  • Supply chain/pins: only plugin.json semver version bumps; no dependency or action pins changed.

No other authorization, injection, secret-handling, or Actions-trust-boundary issues identified in this diff.
· branch fix/truncating-check-runs-query

@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Review-thread triage — all 19 threads

Recorded here so the disposition survives the session that produced it. Every thread has an individual threaded reply with its evidence; I resolved none of them — disposition is the reviewer's or the operator's.

Nineteen threads (17 at first count, two more arrived on the pre-fix head) collapse to two distinct findings, each restated by Codex and several successive review passes. Neither was wrong; neither was superseded-but-invalid. Both are now fixed.

# Path Threads Finding Verdict Action
A readiness.md Gate 5 · work-item-tracker/…/README.md:102 8 --jq fold across --paginate — the filter runs per page, so a count prints one number per page and a sort_by emits one array per page REAL Fixed in 89646d8d; promoted to rule 3
B SKILL.md:183 · review-discipline.md:239 11 D7's follow-up check selects on SHA-in-body with no identity constraint REAL — was not fixed Fixed in fae6f5c4; rule 2 generalized

Finding A — reproduced, then fixed

Against issue #657 (33 comments) forced to four pages:

readiness.md Gate 5    --jq '[…] | length'        ->  10 10 10 3   (should be 33)
work-item-tracker:102  --jq '[…] | sort_by(.id)'  ->  4 separate arrays, each sorted only within its page

Corrected forms return 33 and one sorted 33-element array on that same response.

Rather than patch the two sites, the trap became rule 3 of Reading GitHub list APIs, with the carve-out that makes it usable: element-wise filters (select, map over .[]) are safe under --paginate because their results concatenate; folds are not. Without that line the rule collapses to "never use --jq with --paginate", which is false and would condemn half this diff. Every --paginate + --jq pair across the three touched plugins was then re-swept against the new rule; the remainder are all element-wise, and every fold now lives in jq -s outside --jq.

Finding B — the sharpest finding on this PR, and it was mine

select(.body | contains("<sha>")) proves the SHA was mentioned, not that this agent posted it. A reviewer quoting the fix commit satisfies it — so D7's "did my follow-up post?" gate reports success while the agent's own write silently failed.

That is exactly the shape of the .[-1] bug the line replaced. The original fix corrected the ordering defect and left the class intact: one under-constrained selector swapped for another. Rule 2 already said a query must "state its own intent and cannot be silently satisfied by the wrong record" — and a SHA-only selector is silently satisfied by the wrong record. The reviewers caught a self-violation written into the fix itself.

Both copies now pin .user.login. Verified against live data:

SHA only               -> kyle-sexton   (matches)
SHA + correct identity -> kyle-sexton   (matches)
SHA + wrong identity   -> (empty)       (correctly rejects)

The selector stays element-wise, so it does not reintroduce Finding A.

Rule 2 took the general form rather than the instance: where a query is a control gate you will act on, ask what else could satisfy the selector and constrain that too — one property is usually not enough. The narrow fix would have left the next such gate free to repeat it.

Verification

Replies enumerated with --paginate / per_page=100 and reduced in jq -s, per rule 3:

replies carrying in_reply_to_id: 19   threads: 19   last comment mine: 19

Two threads remain open on work-item-tracker/…/README.md (3751631057, 3753205541). Both are Finding A, both fixed in 89646d8d, both replied to — they need disposition only.

@kyle-sexton
kyle-sexton merged commit 1b338d7 into main Aug 11, 2026
34 checks passed
@kyle-sexton
kyle-sexton deleted the fix/truncating-check-runs-query branch August 11, 2026 23:00
kyle-sexton added a commit that referenced this pull request Aug 11, 2026
#2163 merged and took source-control 0.51.8, so this branch's ruleset-fold
fix moves to 0.51.9. main's 0.51.8 section is kept intact under its own
heading with its own content.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kyle-sexton added a commit that referenced this pull request Aug 12, 2026
… per_page=100 on every paginated read (#2262)

Closes #2244

## Summary

- **D6 verify-commit-pushed** (`reference/review-discipline.md` and
`skills/pull-request/SKILL.md` — the SKILL.md site *is* present on
current `main` at line 181, contrary to the issue's grep against older
`c1b4c629`): replaces the branch-tip read
`commits?sha=<branch>&per_page=1` + `--jq '.[0].sha'` with the
single-resource presence read `repos/{owner}/{repo}/commits/<fix-sha>`.
The tip form asks a presence question but answers a tip question — any
push after the fix (follow-up commit, rebase, sibling lane) makes it
report the fix missing while present: a false negative on a control
gate, and a positional index on a list, which `readiness.md` rule 2
forbids six lines above D7. The single-resource form echoes the SHA on
exit 0 when present and fails HTTP 422 (`No commit found for SHA`) when
absent — index-free, identity-bound, cannot be satisfied by the wrong
record.
- **#2246 (source-control rows only)**: adds `per_page=100` to the six
remaining `--paginate` sites in this plugin: `merge.md:14-16`,
`SKILL.md:166-168` (C1–C3), `monitor.md:195`,
`fetch-all-pr-comments.sh:141`, `telemetry-upsert.md:39`. Not truncation
defects — `--paginate` alone is complete — but non-conformant with rule
1 as `readiness.md:55` publishes it, at 3.3x the request cost.
- **Deliberately unchanged**: `babysit_gh.py:441` and
`request_review.py:186` from the #2246 re-sweep are false positives —
the `per_page=100` sits in the endpoint URL on the line adjacent to the
`--paginate` flag the line-based sweep matched (`request_review.py:185`;
every `fetch_paginated_api` caller URL at
`babysit_gh.py:459/478/523/691`). The other-plugin rows (claude-ops,
work-items) belong to the parallel lane, not this PR.
- Version 0.51.11 + CHANGELOG section (renumbered above the 0.51.10 that
#2167 landed on main mid-flight).

## Test plan

- Live verification of the replacement form in both directions on this
repo: `commits/33f0df51…` → echoes the SHA, exit 0; `commits/0000…0000`
→ HTTP 422, exit 1.
- `plugins/source-control/scripts/fetch-all-pr-comments.test.sh` — all
23 checks pass (the three endpoints the script pages carry no existing
query string, so `?per_page=100` composes safely).
- `scripts/check-changelog-parity.sh --check` and `--check-bump
origin/main` — both pass.
- `markdownlint-cli2` on all six changed markdown files — 0 issues.
- Conflict-marker sweep (all four forms) — clean.

## Related

- #2244 (closes)
- #2246 (source-control rows; claude-ops/work-items rows remain for the
parallel lane)
- #2163 / #2238 / #2239 — prior instances of the same defect class

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
kyle-sexton added a commit that referenced this pull request Aug 12, 2026
…se-safe (#2285)

## Summary

Fixes #2245. `readiness.md` rule 3's carve-out incorrectly listed `map`
over `.[]` as element-wise-safe. `map(f)` is defined as `[.[] | f]` — it
builds an array per page under `--paginate`, so it is a per-page fold
unless followed by `| .[]`.

The carve-out now names `select` and `.[] | f` as safe, and calls out
that bare `map(f)` is not — use `map(f) | .[]` or `.[] | f` instead.

## Test plan

- [x] Read the updated rule 3 prose for accuracy against the `gh
--paginate` + `--jq` behavior described in the issue
- [x] Version bump `source-control` 0.51.12 → 0.51.13 with CHANGELOG
entry

## Related

- #2245
- #2163 (rule 3 author)
- #2238 (same pagination doc family)

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
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.

1 participant