Skip to content

docs(work-items): de-slop instruction surfaces (0.39.23) #5502

docs(work-items): de-slop instruction surfaces (0.39.23)

docs(work-items): de-slop instruction surfaces (0.39.23) #5502

name: claude-security-review
# Dedicated LLM security-review pass via the ci-workflows reusable workflow — the
# security sibling of claude-review.yml, ADR 0002's default-on advisory posture.
# The caller owns the triggers, the GITHUB_TOKEN grant, and the `paths-file`
# input pointing at `.github/claude-security-paths`, the repo-owned list of this
# repo's security-sensitive surfaces (workflows, scripts, hooks, shell, and
# permission/settings config) that the reusable reads from the PR's BASE branch.
# There is deliberately NO workflow-level path filter: a path miss would leave
# the required execution check Pending and wedge prose PRs, so the reusable
# workflow evaluates those patterns in a job-level gate and a not-applicable PR
# yields a name-stable skipped check the required-check ruleset reads as
# success. The verdict stays advisory per ADR 0002. Fork PRs receive no secrets
# and a read-only token, so they are simply not reviewed by design. Requires
# `claude-code-plugins` in the CLAUDE_CODE_OAUTH_TOKEN org secret's
# selected-repositories scope. Public repo: hosted default runner.
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
permissions:
contents: read
concurrency:
group: claude-security-review-${{ github.event.pull_request.number }}
cancel-in-progress: false
jobs:
security-review:
permissions:
contents: read # checkout + read the diff
pull-requests: write # post the security review
id-token: write # OIDC — mints the Claude GitHub App token
uses: melodic-software/ci-workflows/.github/workflows/claude-security-review.yml@7107b34832a7b6db5d08d3b132621c599fbe5e50 # v0.14.2
with:
runner: ubuntu-24.04
paths-file: .github/claude-security-paths
# Stated explicitly, never left to the reusable's default: this list IS
# ADR 0002's skip-actor exception, and the exception must be readable and
# reviewable in the repo it applies to. An inherited default silently
# rewrites the exception whenever ci-workflows changes it — which is how
# `claude[bot]` and `melodic-ai[bot]` entered it (#1766 dropped this line
# while re-pinning to a version whose default had widened).
#
# DO NOT delete this line at a re-pin, and do not "simplify" it away.
# Deleting it is not a no-op even when the value matches the upstream
# default of the moment: the four actors here are ADR 0002's 2026-08-04
# ratified baseline, and inheriting instead re-delegates that decision to
# whatever ci-workflows ships next. Every actor in an inherited default
# satisfies this repo's REQUIRED `security-review / security-review`
# check with no review run, on security-sensitive paths.
#
# The failure directions are asymmetric, which is why explicit wins here
# and not everywhere. A stale explicit list fails CLOSED and LOUD: for an
# actor the reusable's `allowed_bots` does not permit, the action throws
# and this lane's fail-closed mapping turns that into a RED required
# check no push can fix — a merge block, not an extra review (ADR 0002,
# "skip-actors and the action's allowed_bots are different levers").
# Un-skipping such an actor for real means widening `allowed_bots`
# upstream, which is why removing a name here is never a one-line change.
# A stale inherited default fails OPEN and SILENT: an actor this repo
# never deliberated skips review with CI green — exactly what #1766 did.
# Loud-and-blocking is the direction to fail in; silent-and-permissive is
# not. runner-policy rejects inputs outside the reviewed contract but
# cannot REQUIRE one, so dropping this line re-widens the exception with
# nothing said. That residual gap is standards#308.
skip-actors: dependabot[bot],claude[bot],melodic-ai[bot],melodic-standards-sync[bot],cursor[bot]
# One named secret (least privilege), never `secrets: inherit`.
secrets:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Repo-owned fail-closed supplement for #2337: the reusable workflow is
# advisory on some infra-failure classes and can report success without a
# review when the action skips itself on an in-scope diff. This job is
# intentionally separate so the guard stays readable in-repo.
#
# It reads the lane's DECLARED outputs, passed below out of `needs`. It used
# to scrape the lane's job log instead, and a log is not a contract: the
# lane's `Report review outcome` step is an inline github-script whose source
# is echoed into that log, so the grep matched the source that mentions the
# skip phrases and reddened every successful in-scope pull request (#2517).
# Those outputs exist as of ci-workflows v0.14.2, which is why this job is
# now coupled to the pin above — the guard fails CLOSED when they are absent
# at an unmoved head, so a re-pin BACKWARDS past that release turns this red.
# The release, never a pull request number: a reader checking whether their
# pin carries the outputs needs a version they can compare against, and the
# guard's own message says the same thing the same way.
security-review-evidence:
needs: security-review
if: >-
always() &&
github.event_name == 'pull_request' &&
needs.security-review.result != 'cancelled'
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
# The guard reads the pull request's live head on one path only —
# telling a retired superseded run apart from an absent verdict. It no
# longer reads run jobs or logs, so `actions: read` is gone with them.
pull-requests: read
steps:
- name: Check out
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# Self-test first, so a broken guard cannot mask the regression it exists
# to catch — the shape #2517 was: the guard agreed with itself while
# failing every pull request.
- name: Run security-review evidence guard tests
run: bash scripts/verify-security-review-evidence.sh.test.sh
- name: Verify security-review execution evidence
env:
GH_TOKEN: ${{ github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_ACTOR: ${{ github.actor }}
PR_NUMBER: ${{ github.event.pull_request.number }}
EVENT_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
LANE_RESULT: ${{ needs.security-review.result }}
LANE_RELEVANT: ${{ needs.security-review.outputs.relevant }}
LANE_REVIEW_RAN: ${{ needs.security-review.outputs.review-ran }}
LANE_REVIEW_FAILED: ${{ needs.security-review.outputs.review-failed }}
LANE_FAILURE_CLASS: ${{ needs.security-review.outputs.failure-class }}
SKIP_ACTORS: dependabot[bot],claude[bot],melodic-ai[bot],melodic-standards-sync[bot],cursor[bot]
run: bash scripts/verify-security-review-evidence.sh