Description
The release QA report (.github/scripts/release-qa-status) walks every PR merged between two release tags and asks whether its linked issue carries a QA : Passed / QA : Not Needed / QA : Failed label. PRs with no answer land in the missing / unlinked buckets, which drive the :rotating_light: Slack warning on the release announcement and the cc line that @-mentions the authors.
Since Spec-Kit adoption, every feature ships two PRs, and PR 1 carries spec.md alone under specs/<issue>-<slug>/. Those PRs contain nothing runnable, so QA has nothing to exercise and they never earn a QA label — yet the report counts them as un-QA'd. Pure documentation PRs have the same problem.
Impact, measured on v26.09.03-01...v26.09.09-01 (38 PRs): 9 of the 25 missing PRs — 36% of the reported QA gap — were spec- or docs-only. Their authors were @-mentioned on #release-notifications for work QA cannot act on, and the real gaps were diluted by the noise.
The report cannot currently tell the difference: classifyExclusion only ever reads authorType, author, labels and title, and PRDetails has no notion of which files a PR touched. Nothing anywhere under .github/ fetches a PR's changed files.
Acceptance Criteria
Classification
Agent tooling stays in QA scope
Fail-safe behaviour
Rule precedence
Reporting
Verification
Priority
Medium
Additional Context
Scope: CI tooling only — the standalone TypeScript CLI under .github/scripts/release-qa-status/, invoked by .github/workflows/cicd_6-release.yml. No product code and no workflow changes.
Design notes:
- Changed files are fetched with batched GraphQL (20 PRs per query) rather than one REST
pulls.listFiles per PR — the report already spends 3+ REST calls per PR, so this adds roughly one request per twenty rather than one per PR.
- Unlike the sibling
fetchClosingIssueRefs, a failure fetching files is swallowed rather than re-thrown. There, an empty result silently demotes PRs to unlinked and floods Slack, so failing loudly is correct; here an unknown file list simply means no path-based exclusion — the behaviour that existed before this change — and killing the whole QA section over it would be the worse trade.
- The file list is not paginated past the first page. It exists only to answer "is every file documentation?", and a 100+ file PR never is.
Known side effect: a spec PR whose linked issue already carried QA : Passed moves from the passed bucket to excluded, so passed no longer counts spec PRs that happened to be labelled.
Implemented in #37484.
Description
The release QA report (
.github/scripts/release-qa-status) walks every PR merged between two release tags and asks whether its linked issue carries aQA : Passed/QA : Not Needed/QA : Failedlabel. PRs with no answer land in themissing/unlinkedbuckets, which drive the:rotating_light:Slack warning on the release announcement and the cc line that @-mentions the authors.Since Spec-Kit adoption, every feature ships two PRs, and PR 1 carries
spec.mdalone underspecs/<issue>-<slug>/. Those PRs contain nothing runnable, so QA has nothing to exercise and they never earn a QA label — yet the report counts them as un-QA'd. Pure documentation PRs have the same problem.Impact, measured on
v26.09.03-01...v26.09.09-01(38 PRs): 9 of the 25missingPRs — 36% of the reported QA gap — were spec- or docs-only. Their authors were @-mentioned on#release-notificationsfor work QA cannot act on, and the real gaps were diluted by the noise.The report cannot currently tell the difference:
classifyExclusiononly ever readsauthorType,author,labelsandtitle, andPRDetailshas no notion of which files a PR touched. Nothing anywhere under.github/fetches a PR's changed files.Acceptance Criteria
Classification
specs/is excluded with reasonspec-onlyspecs/is excluded with reasondocs-onlyspecs/**,docs/**, and loose*.md/*.mdxanywhere in the treespecs/x/spec.md+.java) is not excluded and is evaluated for QA as beforeREADME.MD,SKILL.md,CLAUDE.mdall resolve correctly)Agent tooling stays in QA scope
.claude/,.agents/,.cursor/and.specify/are treated as implementation, not documentationCLAUDE.mdorAGENTS.md, at any depth, is treated as implementation.claude/skills/x/SKILL.mdcannot match the loose markdown ruleFail-safe behaviour
Rule precedence
bot-authorReporting
textandmarkdownoutputs, showing their reasonVerification
Priority
Medium
Additional Context
Scope: CI tooling only — the standalone TypeScript CLI under
.github/scripts/release-qa-status/, invoked by.github/workflows/cicd_6-release.yml. No product code and no workflow changes.Design notes:
pulls.listFilesper PR — the report already spends 3+ REST calls per PR, so this adds roughly one request per twenty rather than one per PR.fetchClosingIssueRefs, a failure fetching files is swallowed rather than re-thrown. There, an empty result silently demotes PRs tounlinkedand floods Slack, so failing loudly is correct; here an unknown file list simply means no path-based exclusion — the behaviour that existed before this change — and killing the whole QA section over it would be the worse trade.Known side effect: a spec PR whose linked issue already carried
QA : Passedmoves from thepassedbucket toexcluded, sopassedno longer counts spec PRs that happened to be labelled.Implemented in #37484.