diff --git a/components/github-actions-conventions/README.md b/components/github-actions-conventions/README.md index d8ff1c93..7e080433 100644 --- a/components/github-actions-conventions/README.md +++ b/components/github-actions-conventions/README.md @@ -170,11 +170,19 @@ what it does. The Apps in use are listed under `apps` in `vocabulary.json`. ## Exemptions -The complete list is `exemptions` in `vocabulary.json`. It holds one entry: -github-iac's OIDC negative test, `release-deploy-oidc-negative-test.yml`, -whose `name:` is `release-deployx`: the deploy workflow's name plus one -character, so the identity policy that trusts `release-deploy` must reject it. -Only that exact value passes: neither the stem nor a Title Case form does. +The complete list is `exemptions` in `vocabulary.json`, each entry with its +reason. Two kinds stand: + +- **OIDC trust inputs.** github-iac's `release-deploy.yml` keeps the `name:` + `release-deploy`, which the Pulumi issuer policy trusts, and its negative + test `release-deploy-oidc-negative-test.yml` keeps `release-deployx`, one + character more, which that policy must reject. Only those exact values pass: + neither the stem's Title Case form nor anything else does. +- **Pending renames.** A `workflow-filename` exemption lets a repository run + the lint enforcing before a file it cannot rename yet is renamed; the + entry's reason links the issue whose rename removes it, and every other rule + still applies to the file. + GitHub's dynamic workflows (Dependabot, CodeQL default setup, Pages) need no entry: they have no file in `.github/workflows/`. diff --git a/components/github-actions-conventions/naming-lint.test.mjs b/components/github-actions-conventions/naming-lint.test.mjs index cf4a9918..cb84c0f0 100644 --- a/components/github-actions-conventions/naming-lint.test.mjs +++ b/components/github-actions-conventions/naming-lint.test.mjs @@ -492,7 +492,7 @@ test("the vocabulary loader rejects cross-field drift the schema cannot express" assert.throws(() => validateVocabulary(nounFunction), /accepted verb/u); const nameWithoutValue = clone(); - delete nameWithoutValue.exemptions[0].expectedName; + delete nameWithoutValue.exemptions.find((e) => e.rules.includes("workflow-name")).expectedName; assert.throws(() => validateVocabulary(nameWithoutValue), /expectedName/u); const badShape = clone(); diff --git a/components/github-actions-conventions/vocabulary.json b/components/github-actions-conventions/vocabulary.json index 45221129..d5b25842 100644 --- a/components/github-actions-conventions/vocabulary.json +++ b/components/github-actions-conventions/vocabulary.json @@ -18,6 +18,7 @@ "intake": [ { "name": "label", "status": "in-use" }, { "name": "label-needs-triage", "status": "in-use" }, + { "name": "reenter", "status": "in-use" }, { "name": "triage", "status": "in-use" } ], "plan": [], @@ -47,6 +48,7 @@ }, { "name": "lint-dockerfile", "status": "in-use" }, { "name": "merge", "status": "reserved", "purpose": "Merge the pull request." }, + { "name": "rate-merge-risk", "status": "in-use" }, { "name": "refine", "status": "reserved", @@ -75,11 +77,13 @@ ], "post-merge": [ { "name": "detect-silent-revert", "status": "in-use" }, + { "name": "select-mutation-range", "status": "in-use" }, { "name": "sweep-comments", "status": "reserved", "purpose": "File late review comments as issues." }, + { "name": "test-mutation", "status": "in-use" }, { "name": "verify", "status": "reserved", @@ -99,6 +103,7 @@ ], "maintenance": [ { "name": "audit-code-review-rules", "status": "in-use" }, + { "name": "audit-lane-runs", "status": "in-use" }, { "name": "audit-link-check-excludes", "status": "in-use" }, { "name": "audit-org-architecture-pointer", "status": "in-use" }, { "name": "audit-pr-convention-pins", "status": "in-use" }, @@ -108,12 +113,18 @@ "status": "in-use", "purpose": "Check every Markdown link with the network on." }, + { "name": "check-upstream-drift", "status": "in-use" }, + { "name": "delete-stale-branches", "status": "in-use" }, + { "name": "file-dead-links", "status": "in-use" }, + { "name": "file-recurring-issues", "status": "in-use" }, { "name": "file-upstream-drift-issues", "status": "in-use" }, { "name": "measure-skill-edit", "status": "reserved", "purpose": "Measure one proposed skill edit against the merged skill on a person's dispatch, and open a draft pull request when it wins." }, + { "name": "monitor-signals", "status": "in-use" }, + { "name": "reconcile-lost-runs", "status": "in-use" }, { "name": "reconcile-pr-creation-policy", "status": "in-use" }, { "name": "repin-account-rotation", "status": "in-use" }, { "name": "repin-ci-workflows", "status": "in-use" }, @@ -148,6 +159,7 @@ "clean", "collect", "compare", + "delete", "deploy", "detect", "download", @@ -161,12 +173,17 @@ "install", "label", "lint", + "log", "measure", "merge", + "monitor", "notify", + "open", "prune", "publish", + "rate", "reconcile", + "reenter", "refine", "regenerate", "repin", @@ -178,6 +195,7 @@ "review", "run", "scan", + "seal", "select", "setup", "sign", @@ -291,6 +309,42 @@ } ], "exemptions": [ + { + "path": ".github/workflows/dependabot-fragments.yml", + "repository": "melodic-software/claude-code-plugins", + "rules": ["workflow-filename"], + "reason": "The file name has no stage word; its rename is tracked in melodic-software/claude-code-plugins#15394, which removes this entry." + }, + { + "path": ".github/workflows/eval-browser-dispatch.yml", + "repository": "melodic-software/claude-code-plugins", + "rules": ["workflow-filename"], + "reason": "The file name has no stage word; its rename is tracked in melodic-software/claude-code-plugins#15394, which removes this entry." + }, + { + "path": ".github/workflows/eval-hill-climb-pass.yml", + "repository": "melodic-software/claude-code-plugins", + "rules": ["workflow-filename"], + "reason": "The file name has no stage word; its rename is tracked in melodic-software/claude-code-plugins#15394, which removes this entry." + }, + { + "path": ".github/workflows/security-outside-actor.yml", + "repository": "melodic-software/claude-code-plugins", + "rules": ["workflow-filename"], + "reason": "The file name has no stage word; its rename is tracked in melodic-software/claude-code-plugins#15394, which removes this entry." + }, + { + "path": ".github/workflows/pr-feedback-dispatch.yml", + "repository": "melodic-software/claude-code-plugins", + "rules": ["workflow-filename"], + "reason": "The file name is not verb-first after its stage; its rename is tracked in melodic-software/claude-code-plugins#15394, which removes this entry." + }, + { + "path": ".github/workflows/post-merge-demotion-alert.yml", + "repository": "melodic-software/claude-code-plugins", + "rules": ["workflow-filename"], + "reason": "The file name is not verb-first after its stage; its rename is tracked in melodic-software/claude-code-plugins#15394, which removes this entry." + }, { "path": ".github/workflows/release-deploy.yml", "repository": "melodic-software/github-iac", diff --git a/components/lychee-online/targets/claude-code-plugins.yml b/components/lychee-online/targets/claude-code-plugins.yml index 43e28ec6..160699c3 100644 --- a/components/lychee-online/targets/claude-code-plugins.yml +++ b/components/lychee-online/targets/claude-code-plugins.yml @@ -27,6 +27,7 @@ permissions: {} jobs: check-links: + name: Check Links permissions: contents: read uses: melodic-software/ci-workflows/.github/workflows/maintenance-check-links-online.yml@main diff --git a/components/managed-files-guard/README.md b/components/managed-files-guard/README.md index 59b9555e..734f3414 100644 --- a/components/managed-files-guard/README.md +++ b/components/managed-files-guard/README.md @@ -20,7 +20,7 @@ the workflow `name:` (each is its destination stem in Title Case) and their head | `managed-files-guard-fleet-caller` | `managed-files-guard-fleet.yml` | `.github/workflows/pr-check-managed-files.yml` | `melodic-ubuntu-24.04-x64` | Both carry the same action pin and the same job name, so the check context is -`pr-check-managed-files` on every target. The destinations differ because the +`PR Check Managed Files` on every target. The destinations differ because the manifest requires each destination to have one owning component. The guard caller is the second recorded exception to the rule that workflow callers stay consumer-owned (`distribution/README.md`), for the same reason as the Claude diff --git a/components/managed-files-guard/managed-files-guard-fleet.yml b/components/managed-files-guard/managed-files-guard-fleet.yml index 981188b1..1d90f185 100644 --- a/components/managed-files-guard/managed-files-guard-fleet.yml +++ b/components/managed-files-guard/managed-files-guard-fleet.yml @@ -29,7 +29,7 @@ name: PR Check Managed Files # The two files differ only in this paragraph, the source lines above, the # workflow `name:` (each is its destination stem in Title Case), the `paths` entries # (each lists its own targets' destinations) and `runs-on`; the job -# name is the same, so the check context is `pr-check-managed-files` on every +# name is the same, so the check context is `PR Check Managed Files` on every # target. # The guard fails only on an exact managed destination path, so the workflow @@ -115,9 +115,9 @@ concurrency: jobs: pr-check-managed-files: - # Equal to the job id so the check context is `pr-check-managed-files`; - # zizmor's pedantic `anonymous-definition` audit flags an unnamed job. - name: pr-check-managed-files + # The check context is this Title Case name (org naming lint, job-name); + # zizmor's pedantic `anonymous-definition` audit also flags an unnamed job. + name: PR Check Managed Files runs-on: melodic-ubuntu-24.04-x64 timeout-minutes: 10 # No checkout step: the action checks out this repository itself, with diff --git a/components/managed-files-guard/managed-files-guard.test.sh b/components/managed-files-guard/managed-files-guard.test.sh index 9857c7f3..620afbbe 100755 --- a/components/managed-files-guard/managed-files-guard.test.sh +++ b/components/managed-files-guard/managed-files-guard.test.sh @@ -120,8 +120,8 @@ check_shape() { assert_eq "$p exactly one job" '1' "$(q '.jobs | length')" assert_eq "$p the job is named pr-check-managed-files" 'pr-check-managed-files' "$(q '.jobs | keys | .[0]')" - assert_eq "$p the job display name equals its id (check context is pr-check-managed-files)" \ - 'pr-check-managed-files' "$(q '.jobs["pr-check-managed-files"].name')" + assert_eq "$p the job display name is the Title Case of its id (check context is PR Check Managed Files)" \ + 'PR Check Managed Files' "$(q '.jobs["pr-check-managed-files"].name')" assert_eq "$p the job runs on $runs_on directly" "$runs_on" \ "$(q '.jobs["pr-check-managed-files"]["runs-on"]')" assert_eq "$p the job has a 10-minute timeout" '10' "$(q '.jobs["pr-check-managed-files"]["timeout-minutes"]')" diff --git a/components/managed-files-guard/managed-files-guard.yml b/components/managed-files-guard/managed-files-guard.yml index c6208510..c43f4973 100644 --- a/components/managed-files-guard/managed-files-guard.yml +++ b/components/managed-files-guard/managed-files-guard.yml @@ -104,9 +104,9 @@ concurrency: jobs: pr-check-managed-files: - # Equal to the job id so the check context is `pr-check-managed-files`; - # zizmor's pedantic `anonymous-definition` audit flags an unnamed job. - name: pr-check-managed-files + # The check context is this Title Case name (org naming lint, job-name); + # zizmor's pedantic `anonymous-definition` audit also flags an unnamed job. + name: PR Check Managed Files runs-on: ubuntu-24.04 timeout-minutes: 10 # No checkout step: the action checks out this repository itself, with diff --git a/distribution/README.md b/distribution/README.md index 3b16c8e4..b3b279f8 100644 --- a/distribution/README.md +++ b/distribution/README.md @@ -422,9 +422,9 @@ Both call `.github/actions/check-managed-files` at the same pin. The two files differ only in `runs-on`, comments, their `on.pull_request.paths` entries (each lists the managed destinations of the targets that receive it) and the workflow `name:`, which is each destination stem in Title Case (`PR Check Managed Files Hosted` and -`PR Check Managed Files`). They share the job id and job name -`pr-check-managed-files`, so the check context is `pr-check-managed-files` -on every target, hosted or fleet-routed. The hosted caller +`PR Check Managed Files`). They share the job id +`pr-check-managed-files` and job name `PR Check Managed Files`, which is the +check context on every target, hosted or fleet-routed. The hosted caller runs on `ubuntu-24.04` and is `managed` for the public targets. The fleet-routed caller runs on `melodic-ubuntu-24.04-x64` and is `managed` for the fleet-enrolled private targets (`agent-automations`, diff --git a/distribution/sync-manifest.yml b/distribution/sync-manifest.yml index b4ef62af..a2818ce4 100644 --- a/distribution/sync-manifest.yml +++ b/distribution/sync-manifest.yml @@ -316,7 +316,7 @@ components: # Two components, one per runner-policy inventory. The files are identical # except for `runs-on`, the workflow `name:`, their headers and their # `paths` entries (each lists its own targets' destinations): same action - # pin, same job name, so the check context is `pr-check-managed-files` on + # pin, same job name, so the check context is `PR Check Managed Files` on # every target. Destinations must be unique across components, so each has # its own path (`pr-check-managed-files-hosted.yml` for the hosted caller). # A target takes one or the other, never both.