Conversation
security.yml's audit job now checks out the repository and runs .github/actions/dependency-audit, which holds the audit's steps, so the PR workflow can run the same audit without a second copy. The job keeps its read-only permissions and its gate_failed output, and the artifact keeps its dependency-audit name. Dependabot's github-actions entry also scans .github/actions/*, so the pins inside the composite action stay updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pre-commit job moves from pre-commit.yml, which is deleted, and the Dependency Audit, Post Audit Comment, Dependency Review, Audit Script Tests and Workflow Hardening jobs move from security.yml. Job ids and names are unchanged, so the check names the ruleset requires keep passing. test.yml's audit job runs the shared composite action. security.yml keeps only the weekly and manual audit and its Slack notification. Since test.yml runs on every push to main, the audit now runs on each one instead of only when a dependency file changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Post Audit Comment job used to be skipped on pushes and on PRs from forks. It now runs on every event (still after a failed audit) and its download and comment steps run only on a pull request from a branch of this repository, so the job succeeds with its steps skipped elsewhere. An aggregate check can then require it to succeed. Same-repo PRs get the comment as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI, the job named CI in test.yml, runs whatever happened to the jobs it needs and fails when any of them failed, was cancelled or was skipped, since GitHub counts a skipped required check as passing. Dependency review may be skipped on a push, where it does not run. CI exits 2 when the job results cannot be read or their count is not EXPECTED_JOBS. CI needs every job in test.yml but e2e-unpinned-pdp, which tests PDPs this repository does not pin and stays non-blocking. Comments that named the per-job required checks now describe CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new step in the Workflow Hardening job reads test.yml with yq and fails when its jobs, minus CI and the ADVISORY_JOBS list, differ from CI's needs, when an ADVISORY_JOBS entry is not a job, is listed twice or is also in CI's needs, or when CI's EXPECTED_JOBS is not the number of jobs CI needs. It exits 2 when it reads no jobs. ADVISORY_JOBS holds e2e-unpinned-pdp, with the reason it does not block a PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test_ci_checks.py reads both steps' bash and env from test.yml with yq and runs them as a `shell: bash` step runs, against planted job results (failed, cancelled and skipped jobs, a missing or extra result, unreadable JSON, dependency review skipped on a push and on other events) and planted workflows (a job CI does not need, a need that is not a job, stale, repeated or needed advisory entries, a wrong EXPECTED_JOBS, no jobs). The Audit Script Tests job runs it with the other CI script tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CONTRIBUTING.md gains a CI section: CI in test.yml is the one check to require, how to add a job (to CI's needs with EXPECTED_JOBS, or to ADVISORY_JOBS with the reason), what the job-list check enforces, and that the weekly audit runs from security.yml. The e2e and CI script test sections, the skills tests README and the Dependabot comments no longer name security.yml as the PR audit or the per-job required checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test.yml has no concurrency group, so a newer push no longer cancels the run of the older commit, and that run can finish last. Its comment step now reads the PR's current head and posts only when it is the commit the run audited, so the audit comment never goes back to an older commit's report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing reads the audit jobs' gate_failed output, in test.yml or in security.yml, nor the composite action's failed output that feeds it: the comment and Slack jobs read the dependency-audit artifact and the audit job's result. The gate step still fails the job on a fixable HIGH or CRITICAL advisory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
actionlint shellchecks the run blocks of workflows only, so moving the audit's bash into .github/actions/dependency-audit took it out of CI's shellcheck. A new Workflow Hardening step shellchecks every bash step of the local actions with actionlint's options: expressions replaced by a placeholder and the checks it turns off left off. It exits 1 on a finding and 2 when it reads no bash step. test_ci_checks.py runs the step against the committed actions and against planted ones: a finding, an expression, a variable set by env:, no action and no bash step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CONTRIBUTING.md's CI section said CI needs every other job in test.yml, which left out e2e-unpinned-pdp, the one job in ADVISORY_JOBS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moving the jobs dropped the comments that said the pytest lanes' and pre-commit's names are required checks. Until the ruleset on main requires CI alone, it still requires six check names, so CI's leading comment and CONTRIBUTING.md's CI section list them and say not to rename those jobs until then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The header named the audit formatter's and the schema drift check's tests only, and said they need nothing but pytest and the standard library. It now covers every test file in .github/scripts and says that test_ci_checks.py also runs bash, jq, yq and shellcheck. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dependency Security AuditScanned: pyproject.toml dependencies + dev group, resolved at Python 3.10 (the current resolution, and the lowest versions the published specs permit under each pydantic major) ✅ No known vulnerabilities found. Both the resolved dependency set and the lowest versions the published specs permit are clean at HIGH and CRITICAL. |
Reverted in the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI failed on it as intended: "Jobs that did not succeed: migration-skill skipped", exit 1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 3, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linear issues
Stacked on #144, the top of the 3.x stack.
Why
The ruleset on
mainrequires six job names spread across three workflows (test.yml,security.yml,pre-commit.yml). A renamed job silently stops being required, a new job is not required until someone adds it to the ruleset, and GitHub counts a job skipped by anif:as success for a required check.needsworks only inside one workflow, so the jobs a pull request must pass move intotest.yml, where oneCIjob checks all of them.What changed
test.ymlnow holdspre-commit(moved frompre-commit.yml, which is deleted) andDependency Audit,Post Audit Comment,Dependency Review,Audit Script TestsandWorkflow Hardening(moved fromsecurity.yml), next to its existing jobs. Job ids and names are unchanged, so the six check names the ruleset requires today still report..github/actions/dependency-audit. Theauditjobs intest.ymlandsecurity.ymlboth check out the repository and call it. The jobs keepcontents: read, and the artifact is still nameddependency-audit. Thegate_failedjob output, which nothing read, is removed.security.ymlruns only on the Monday 09:00 UTC schedule and on manual dispatch, withauditandnotify. The Slack message is unchanged.CIjob, copied from the Terraform provider. It hasif: always()andpermissions: {}, and needs 10 jobs: api-coverage, audit, audit-scripts-test, comment, compatibility, dependency-review, migration-skill, pre-commit, pytest and workflow-hardening (EXPECTED_JOBS: 10). It exits 2 when the job results cannot be read or their count is notEXPECTED_JOBS, and 1 when any result is notsuccess. The one exception:dependency-review skippedpasses onpush.e2e-unpinned-pdp(the latest-PDP-image and cloud-PDP legs) stays intest.ymlafterpytestand does not block a PR:CIdoes not need it.Post Audit Commentruns on every event, still after a failed audit. Its steps run only on a PR from a branch of this repository, so on pushes and fork PRs the job succeeds with its steps skipped. The comment step also skips posting when the PR head has moved past the commit the run audited.Workflow Hardeninggains two steps:test.yml's jobs, minusciandADVISORY_JOBS, differ fromci.needs; anADVISORY_JOBSentry is not a job, is listed twice or is also inneeds; orEXPECTED_JOBSis not the number of jobs inneeds. It exits 2 when it reads no jobs.ADVISORY_JOBSholdse2e-unpinned-pdp, with a comment giving the reason.shell: bashstep under.github/actionswith actionlint's options, because actionlint does not lint composite actions. It exits 1 on a finding and 2 when it reads no bash step..github/scripts/test_ci_checks.py(50 tests, run byAudit Script Tests) reads the bash andenv:of those three steps fromtest.ymlwith yq. It runs them the way ashell: bashstep runs, against planted job results, workflows and actions./.github/actions/*.python-sdk-publish.ymlpointed at the Trivy step insecurity.yml; it now points at.github/actions/dependency-audit. Nothing else in that workflow changes.CIas the one check to require, how to add a job, what the job-list check enforces, the six check names not to rename until the ruleset switch, andsecurity.ymlas the home of the weekly audit. The headers of.github/scripts/pytest.iniandskills/tests/README.md, and the Dependabot comments, no longer name the old workflows or describe only some of the tests. The repository has no CLAUDE.md or AGENTS.md.Behaviour changes
main/master, becausetest.ymlhas no path filter. Before, a push ran it only whenpyproject.toml,security.yml,audit-deps.shorformat_audit.pychanged.test.ymlhas no concurrency group. The audit comment is not posted from a run whose commit is no longer the PR head. Dependency Review's own on-failure summary comment can still come from a superseded run.security.ymlno longer runs onpull_requestorpush. Its concurrency group stays; the cancel-on-PR setting is dropped.Post Audit Commentruns on every event and succeeds with its steps skipped where it cannot post. Same-repo PRs, Dependabot's included, get the comment as before.CI, more jobs block a merge:API Coverage, the compatibility legs, the Migration Skill Tests legs,Dependency Reviewon PRs andPost Audit Comment.e2e-unpinned-pdpstill does not block.Workflow Hardeningnow fails in three more cases: atest.ymljob is in neitherci.needsnorADVISORY_JOBS,EXPECTED_JOBSis wrong, or a local action's bash has a shellcheck finding.Audit Script Testsalso runstest_ci_checks.py, which uses bash, jq, yq and shellcheck from the runner image.shell: bash(bash --noprofile --norc -eo pipefail) instead of the defaultbash -e. Every script already sets pipefail.Notify Slackcheck run (skipped) no longer appears on PRs.How it was tested
.github/scriptssuite, run asAudit Script Testsruns it: 273 passed, 50 of them intest_ci_checks.py. Local tools: bash 3.2.57, yq v4.53.3, jq, shellcheck 0.11.0.Planted inputs, run locally against the step bash extracted from
test.yml. Exit codes:CI: all success on pull_request 0 and on push 0; pytest skipped 1; audit cancelled 1; compatibility failed 1; 9 results 2; 11 results 2; unreadable JSON 2; dependency-review skipped on push 0 and on pull_request 1; comment skipped on push 1.test.yml0; a job missing fromneeds1; aneedsentry that is not a job 1; a stale advisory entry 1; a duplicated advisory entry 1; an advisory job also inneeds1;EXPECTED_JOBS9 gives 1 and 11 gives 1; no jobs read 2.echo $x1; a missing directory 2.Mutation check: I made 23 mutations. 17 were in the CI and job-list bash:
cinot dropped from the job listADVISORY_JOBSEXPECTED_JOBSoff by oneneedsThe other 6 were in the shellcheck step: no placeholder, no exclusions, a finding not failing, bash steps skipped, no exit 2 when nothing is read, and an unreadable action exiting 0. Each mutation failed at least one test.
The comment script was run under Node 24 with a mocked GitHub client:
The script before this change updated the comment on a moved head.
actionlint 1.7.12: no findings. zizmor 1.30.1, offline and online: no findings (2 ignored, 17 suppressed).
uv run pre-commit run --all-filespasses.Job ids and names, compared with the base using yq, are unchanged. The bodies of pre-commit, dependency-review, notify, pytest, e2e-unpinned-pdp, api-coverage, compatibility and migration-skill are identical. The composite action's steps match the old audit steps except for the
python-versioninput,shell: bash, one comment and the removedgate_failedlines.On GitHub:
CIincluded, with 29 checks green. The composite action ran insideDependency Audit, andPost Audit Commentposted the audit comment.CIdidn't wait for the non-blockinge2e (latest PDP image)leg.Migration Skill Teststo skip.CIfailed with "Jobs that did not succeed: migration-skill skipped" and exit code 1. The next commit (559241a) reverts it, so the tree is the same as at bdb639c.Owner actions before merge
mainproduces theCIcheck, replace the six per-job required checks in themainruleset withCIalone. Do it only then: requiringCIearlier leaves every PR waiting. The six checks arepytest (Pydantic pydantic<2.0.0),pytest (Pydantic pydantic>=2.0.0),pre-commit,Dependency Audit,Audit Script TestsandWorkflow Hardening. Then, in a follow-up, remove the note that lists those six names fromCI's leading comment intest.ymland from CONTRIBUTING.md's CI section.PREK_HOMEcache) into thepre-commitjob intest.yml, and keepspre-commit.ymldeleted.🤖 Generated with Claude Code