Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
40f176a
ci(fewer-jobs): fold the copyright, quarantine-audit and docs-data ga…
astubbs Sep 6, 2026
1c1e76f
ci(fewer-jobs): fold the PR Checklist job into repo: hygiene
astubbs Sep 6, 2026
c4475ae
ci(fewer-jobs): batch the three no-build scanners into one scan: repo…
astubbs Sep 7, 2026
0d504fc
ci(fewer-jobs): batch Infer and SpotBugs into one static: analysis job
astubbs Sep 7, 2026
b456e1f
ci(fewer-jobs): run the whole-tree CVE scan as a step of scan: repo o…
astubbs Sep 7, 2026
245ba06
ci(fewer-jobs): run PIT as the last step of scan: repo
astubbs Sep 7, 2026
eb9ebe7
Merge remote-tracking branch 'origin/master' into optimize/ci-fewer-jobs
astubbs Sep 7, 2026
a3d75a6
fix(ci): the copyright self-test controls its own environment, so the…
astubbs Sep 7, 2026
1c54ed3
test(core): assert the quarantine audit through the sweep that now ru…
astubbs Sep 7, 2026
1bfa32b
docs(refactoring): register the six-times-duplicated Maven setup bloc…
astubbs Sep 7, 2026
d86f2a7
Merge master into ci/fewer-per-pr-jobs
astubbs Sep 7, 2026
6c310de
docs(inflight): the ruleset checklist names its lists, and stops coun…
astubbs Sep 7, 2026
48d3ebd
ci(fewer-jobs): a prepare-deps failure must not skip the required bat…
astubbs Sep 7, 2026
b94b517
test(ci): a regression case for --strict, the flag holding up the fol…
astubbs Sep 7, 2026
ab82a73
test(core): read the workflow trigger from the on: block, not from th…
astubbs Sep 7, 2026
8bd41a6
docs(solutions): the CI job-batching run, including the hypotheses th…
astubbs Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/hooks/after-push-check-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ print(json.dumps({
"checks that have already cost it once, both recorded in "
"docs/inflight/ci-duplication-report-can-fail-to-post.md:\n"
"\n"
"1. A RED CHECK CAN HAVE NO COMMENT. `dups: clones` is a required check, and it has "
"1. A RED CHECK CAN HAVE NO COMMENT. `dups: clones` is a step of the required `scan: repo` check, and it has "
"failed on this repo with its finding posted nowhere - GitHub rejected the inline "
"annotation and nothing fell back to a summary comment. If a check is red and you "
"cannot find a comment explaining it, the finding is in the job log: "
Expand Down
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

## Checklist

<!-- Keep this checklist and resolve EVERY box: tick it [x], or mark it "N/A - <reason>". The "PR Checklist"
<!-- Keep this checklist and resolve EVERY box: tick it [x], or mark it "N/A - <reason>". The "repo: hygiene"
CI check fails a human PR when the checklist is missing entirely, or when a box has no [x] and no N/A.
Only real bot authors (Dependabot/Renovate etc.) are exempt. -->

Expand Down
7 changes: 4 additions & 3 deletions .github/scripts/changelog-ref-gate.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright (C) 2026 Antony Stubbs and contributors

// Pure logic behind the "Verify new changelog entries cite an issue" step in
// .github/workflows/pr-checklist.yml. It lives here rather than inline in the workflow YAML so it
// can be unit tested - the same job runs changelog-ref-gate.test.js before the gate, so a
// regression here fails the PR Checklist rather than silently misjudging changelogs.
// .github/workflows/repo-hygiene.yml (the `repo: hygiene` job, where the former PR Checklist steps
// now live). It lives here rather than inline in the workflow YAML so it can be unit tested - the
// same job runs changelog-ref-gate.test.js before the gate, so a regression here fails the required
// check rather than silently misjudging changelogs.
//
// This is a reminder for ourselves, not a defence against someone gaming it. Where being exactly
// right would need real cleverness, it takes the simple option and says so: a check nobody can
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/changelog-ref-gate.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (C) 2026 Antony Stubbs and contributors

// Unit tests for changelog-ref-gate.js, run by the PR Checklist job before the gate itself.
// Unit tests for changelog-ref-gate.js, run by the `repo: hygiene` job before the gate itself.
// Plain node, no dependencies, no runner: `node .github/scripts/changelog-ref-gate.test.js`.
// Exits non-zero on the first sign of trouble, so a broken gate fails CI loudly.

Expand Down
4 changes: 2 additions & 2 deletions .github/scripts/file-ref-gate.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ function citationsIn(line) {

/**
* The oracle resolves() reads, built from `git ls-files`. It lives here because BOTH callers need
* one - the CI job in pr-checklist.yml and bin/check-file-refs.sh - and two hand-copied versions are
* one - the CI gate in repo-hygiene.yml and bin/check-file-refs.sh - and two hand-copied versions are
* how they would come to disagree about what exists, which is the single thing they cannot differ
* on. Same reasoning that puts formatFailure here rather than at each call site.
*
Expand Down Expand Up @@ -483,7 +483,7 @@ function newFindings(current, base) {

/**
* The single copy of what an author is told when either rule fires - rendered by both callers, the
* CI job in pr-checklist.yml and the local bin/check-file-refs.sh, so the two cannot tell different
* CI gate in repo-hygiene.yml and the local bin/check-file-refs.sh, so the two cannot tell different
* stories. Its sibling gate learned that the hard way: hand-written copies of one message disagreed
* in both directions within hours of the second being written.
*/
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/file-ref-gate.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (C) 2026 Antony Stubbs and contributors

// Unit tests for file-ref-gate.js. Run by the PR Checklist job before the gate itself, so a broken
// Unit tests for file-ref-gate.js. Run by the `repo: hygiene` job before the gate itself, so a broken
// rule fails loudly rather than silently passing - or failing - every PR.
//
// The cases that matter most here are the NEGATIVE ones. This gate's failure mode is not missing a
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/issue-ref-gate.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ function prBodyEntry(body) {

/**
* The single copy of what an author is told when the gate fires. Both callers render this - the CI
* job in pr-checklist.yml and the local bin/check-issue-refs.sh - so the two cannot tell different
* gate in repo-hygiene.yml and the local bin/check-issue-refs.sh - so the two cannot tell different
* stories. They did exactly that once: hand-written copies disagreed in *both* directions within
* hours of the second one being created, each carrying a correction the other lacked. The script's
* own header already says "NO SECOND COPY OF THE RULE" about the matching logic; the message an
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/issue-ref-gate.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (C) 2026 Antony Stubbs and contributors

// Unit tests for issue-ref-gate.js. Run by the PR Checklist job before the gate itself, so a
// Unit tests for issue-ref-gate.js. Run by the `repo: hygiene` job before the gate itself, so a
// broken rule fails loudly rather than silently passing - or failing - every PR.
const assert = require("assert");
const {
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/roadmap-stage-gate.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (C) 2026 Antony Stubbs and contributors

// Unit tests for roadmap-stage-gate.js. Run by the PR Checklist job before the gate itself, so a
// Unit tests for roadmap-stage-gate.js. Run by the `repo: hygiene` job before the gate itself, so a
// broken rule fails loudly rather than silently passing - or failing - every PR.
// issue-refs: exempt-file - fixtures and check names are deliberately full of bare PR numbers;
// qualifying them would stop testing what the parser actually sees.
Expand Down
4 changes: 2 additions & 2 deletions .github/scripts/sticky-report-comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
// const sticky = require(`${process.env.GITHUB_WORKSPACE}/.github/scripts/sticky-report-comment.js`)
//
// `actions/github-script` runs with the workspace checked out, so `require` of a repo file works and
// is already the convention here (see .github/workflows/pr-checklist.yml, which requires four gate
// modules this way). Tests: .github/scripts/sticky-report-comment.test.js.
// is already the convention here (see .github/workflows/repo-hygiene.yml, whose PR-body gates require four
// gate modules this way). Tests: .github/scripts/sticky-report-comment.test.js.

"use strict";

Expand Down
22 changes: 11 additions & 11 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@ procedure or a steer. Why, and the gate's exact contract: [`docs/ci.md`](../../d
| `claude-code-review-dispatch.yml` | The dispatched code reviewer - the expensive half, run when somebody asks for it. |
| `claude-code-review.yml` | The review gate. Invokes no Claude and costs nothing. What satisfies it: [`docs/ci.md`](../../docs/ci.md), stated once there. |
| `claude.yml` | Answers `@claude` comments, and reviews when asked to - the only route that can raise inline review threads. |
| `copyright.yml` | Checks source headers against the fork policy on every push and PR. |
| `dependency-audit.yml` | Scans the **whole** resolved dependency tree for CVEs - the only place OSS Index is switched on. Per-PR, on demand, and weekly, because a new advisory needs no push to arrive. |
| `maven.yml` | The main build: unit, integration and performance suites, SpotBugs, duplicate detection, PIT, dependency scanning. |
| `dependency-audit.yml` | Scans the **whole** resolved dependency tree for CVEs. On demand and weekly, because a new advisory needs no push to arrive; the per-PR half is the `deps: whole-tree CVE scan` step of `maven.yml`'s `scan: repo`, and the two are the only places OSS Index is switched on. |
| `maven.yml` | The main build: unit, integration and performance suites, static analysis (Infer and SpotBugs, batched as `static: analysis`), and duplicate detection, dependency review, the whole-tree CVE scan and PR-scoped PIT (batched as `scan: repo`, PIT last and advisory). |
| `mutation-full-sweep.yml` | The whole-repo PIT mutation sweep - nightly, plus on demand. Too slow for a PR. Self-hosted high-CPU lane, plus a hosted trial arm. |
| `pr-checklist.yml` | Makes the PR template binding: the checklist must be present and every box resolved. |
| `publish.yml` | Publishes to Maven Central on every push to `master`; the pom version decides snapshot or release. |
| `quarantine-lane.yml` | Runs the quarantined tests separately, so known-flaky tests neither block nor disappear. |
| `release.yml` | Cuts a release. `workflow_dispatch`, and deliberately the most dangerous button here. |
| `repo-hygiene.yml` | Always-on repo checks - shell sigpipe traps, one pinned version per GitHub Action, and expiring the pom's temporary CVE exclusions - plus `shell: macos`, the one lane not on `ubuntu-latest`: it runs the shell self-tests and a `bash -n` sweep against Apple's bash 3.2, where GNU-only constructs fail silently. |
| `repo-hygiene.yml` | Always-on repo checks, every `bin/check-*` gate and self-test by glob - copyright headers against the fork policy, the quarantine registry, the release-documentation data, shell hazards, one pinned version per GitHub Action, expiring the pom's temporary CVE exclusions - and the PR-body gates that were `pr-checklist.yml`: the template checklist must be present and every box resolved, issue and file references must resolve, a changelog entry cites an issue, a roadmap-carrying PR moves its stage - plus `shell: macos`, the one lane not on `ubuntu-latest`: it runs the shell self-tests and a `bash -n` sweep against Apple's bash 3.2, where GNU-only constructs fail silently. |

## One required check is not in this directory

Expand All @@ -48,12 +46,14 @@ trade costs is in [`docs/ci.md`](../../docs/ci.md).
## Two conventions that will bite you

- **Job names are an API.** `claude-review`, `review: human LGTM`, `Check PR Dependencies`,
`quarantine: audit`, `Copyright header check` and the `maven.yml` suites are required status
checks matched **by name** in the master ruleset. Rename a job and the ruleset silently stops
being satisfied by anything - it does not fail, it just never passes. `shell: sigpipe` and
`workflows: action versions` were two such names; both jobs were folded into `repo-hygiene.yml`'s
single `repo: hygiene` lane, and - as of the last live check of the ruleset - that replacement
lane is not itself in the required list. See [`docs/ci.md`](../../docs/ci.md) for the detail.
`repo: hygiene` and the `maven.yml` suites are required status checks matched **by name** in the
master ruleset. Rename a job and the ruleset silently stops being satisfied by anything - it does
not fail, it just never passes. `shell: sigpipe` and `workflows: action versions` were two such
names, folded into `repo-hygiene.yml`'s single `repo: hygiene` lane; `Copyright header check`,
`quarantine: audit` and `docs data: audit` followed them there, and until the ruleset drops those
three contexts they are required names no job produces -
[`docs/inflight/ci-fewer-jobs-ruleset-edits.md`](../../docs/inflight/ci-fewer-jobs-ruleset-edits.md)
carries the edit. See [`docs/ci.md`](../../docs/ci.md) for the detail.
- **Most of these run PR-authored code.** A `pull_request` job checks out the PR, so anything it
executes is whatever the PR says it is. That is why the review jobs hold no write scope, and why
`actions: write` lives alone in a job that checks nothing out. See "The reviewer runs PR code"
Expand Down
50 changes: 0 additions & 50 deletions .github/workflows/copyright.yml

This file was deleted.

Loading
Loading