Skip to content

feat(runner-policy): admit claude-review@df54d0e as runner-input - #161

Merged
kyle-sexton merged 1 commit into
mainfrom
ci/df54d0e-runner-input-contract
Jul 17, 2026
Merged

feat(runner-policy): admit claude-review@df54d0e as runner-input#161
kyle-sexton merged 1 commit into
mainfrom
ci/df54d0e-runner-input-contract

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

Closes #159.

Flips the claude-review.yml@df54d0e8… contract from hosted-only to runner-input (runner + skip-actors, same secret and caller-permission waiver as the 4dbb0dfc entry). df54d0e (melodic-software/ci-workflows#117, claude-code-action v1.0.174) surfaces real review failures — usage limits, OIDC/SDK errors, max-turns — as explicit PR comments; the six Campaign A callers pinned at 4dbb0dfc sit in that failure-reporting regression window until they can bump to this SHA.

Deviation from the issue's step 1: the issue asked to ADD a second df54d0e entry and keep the hosted-only one, but contract entries are keyed by path@SHA — one JSON key cannot carry both shapes. In-place mutation is safe instead: zero callers reference df54d0e on any main (verified across all nine requires-ci repos), so nothing depends on the hosted-only shape.

Pin-bump wave (issue step 3) follows separately after this syncs to consumers.

Verification

  • Full component suite passes (181/181).
  • Entry mirrors 4dbb0dfc's registered runner-input shape exactly, plus runner in allowedInputs.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_01K3QehVwmWzkBLpKokNCkkt

Closes #159.

Flips the claude-review.yml@df54d0e8 contract from hosted-only to
runner-input (runner + skip-actors, same secret and caller-permission
waiver as 4dbb0dfc). df54d0e (ci-workflows#117, claude-code-action
v1.0.174) surfaces real review failures as explicit PR comments; the
Campaign A callers pinned 4dbb0dfc sit in that failure-reporting
regression window until they can bump to this SHA.

In-place mutation is safe: zero callers reference df54d0e on any main
(verified across all nine requires-ci repos), so no caller depends on
the hosted-only shape the issue proposed to retain.

Full component suite passes (181/181).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01K3QehVwmWzkBLpKokNCkkt

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@claude

claude Bot commented Jul 17, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 20s —— View job


PR Review

  • Gather context
  • Read changed file and diff
  • Review for correctness, security, and guideline alignment
  • Post findings

The change is correct and well-reasoned. Here's what I verified:

Shape fidelity. The mutated df54d0e entry is now structurally identical to the 4dbb0dfc entry — same routing, runnerInput, allowedInputs, allowedSecrets, and allowedCallerPermissions. The PR description is accurate.

Schema compliance. The runnerInputContract schema (policy.schema.json:172-185) requires routing, runnerInput, allowedInputs, and allowedSecrets; all four are present. The removed fixedRunsOn field is correctly absent — it belongs only on hostedOnlyContract. allowedCallerPermissions is a valid optional field on runnerInputContract.

Permission scope. allowedCallerPermissions grants callers the ability to pass contents: read, pull-requests: write, and id-token: write. These are appropriate minimums for a code-review workflow that posts PR comments and uses OIDC. The id-token: write permission is restricted by schema to "write" | "none" (it has no read variant), and is already present in the identical 4dbb0dfc entry — no new privilege surface introduced.

In-place mutation rationale. The PR correctly identifies that JSON object keys must be unique, so a path@SHA key cannot carry two contract shapes simultaneously. The stated verification ("zero callers reference df54d0e on any main") is the right due-diligence step; that claim is not verifiable from this diff alone, but the approach is sound if it holds.

No findings. The change is minimal, schema-valid, and mirrors an already-approved entry exactly. The 181/181 test suite pass confirms mechanical correctness.

@kyle-sexton
kyle-sexton merged commit 586c413 into main Jul 17, 2026
42 checks passed
@kyle-sexton
kyle-sexton deleted the ci/df54d0e-runner-input-contract branch July 17, 2026 12:06
kyle-sexton added a commit that referenced this pull request Jul 31, 2026
…contract (#307)

## Summary

Adds `skip-actors` to the `allowedInputs` of the runner-policy contract
entry for

`melodic-software/ci-workflows/.github/workflows/claude-security-review.yml@c136b27`
— the pin
both security-lane consumers run today.

Why now: `claude-code-plugins` re-deliberated ADR 0002's skip-actor
exception
(melodic-software/claude-code-plugins#1767) and **rejected** the
reusable workflow's inherited
default
(`dependabot[bot],claude[bot],melodic-ai[bot],melodic-standards-sync[bot]`)
for the
*security* lane. `claude[bot]` and `melodic-ai[bot]` carry none of the
compensating controls
that justified the two operator-ratified actors — a Dependabot pin bump
is forced through this
very runner-policy contract, and a standards-sync PR materializes
byte-exact upstream-reviewed
content — so an AI-app-pushed commit would satisfy that repo's
**required** security check with
no review run of any kind. Its caller must therefore pass an explicit
narrow list
(`dependabot[bot],melodic-standards-sync[bot]`), and the caller can only
pass an input the
contract admits.

This **restores parity rather than widening new ground**: the previous
approved security SHA's
contract (`66073e58`) already admitted `skip-actors`; the `c136b27`
entry dropped it, which is why
the consumer's caller could not keep its explicit list through the
re-pin. The `claude-review.yml`
contracts at `df54d0e` and `4dbb0df` admit it too.

A second commit reconciles `components/runner-policy/README.md`, whose
`c136b27` review note still
recorded the security caller as able to pass only `runner` and
`paths-file` — a reviewer consulting
it would have read `skip-actors` as denied. The `(of 8)` denominator is
unchanged: `skip-actors` is
one of the reusable's eight declared inputs at that SHA (`runner`,
`paths`, `paths-file`, `prompt`,
`claude-args`, `exclude-comments-by-actor`, `skip-actors`,
`retry-delay-seconds`), so the admitted
set stays narrower than the declared surface.

**Blast radius — permissive widening only.** `runner-policy` is a
managed component across the
fleet, so this materializes everywhere, but nothing starts passing the
input: the validator
rejects inputs *outside* `allowedInputs` and never requires one. The
only behavior change is
that a caller may now pass `skip-actors` to this pin without the policy
check failing.

The entry carries no `autoApproved` block, so it is a human-reviewed
contract edit by design.

Known residual gap (follow-up, not this PR): the validator rejects
unexpected inputs but cannot
*require* one, so a future re-pin that drops a caller's `skip-actors`
line passes CI and silently
re-widens the exception. Guarding that needs a `requiredInputs`-style
contract field — filed as
#308, which also records the matching hazard in the parked
`claude-security-review-caller`
component (its bytes pass no `skip-actors`, so an adopter inherits the
widened default when it
unparks).

## Test plan

- `node -e "JSON.parse(...)"` on `components/runner-policy/policy.json`
— parses.
- `biome check components/runner-policy/policy.json` — clean (also clean
via the `pre-commit`
  lefthook run: typos, gitleaks, editorconfig, biome all pass).
- Diff is one line: `"allowedInputs": ["runner", "paths-file"]` →
`["runner", "paths-file", "skip-actors"]`, matching the file's
one-line-array style and the
  schema's `uniqueStringArray`.
- Input verified against the reusable at the pinned SHA: `skip-actors`
is a declared
`workflow_call` input on `claude-security-review.yml@c136b27`,
defaulting to the four-actor
list and gating the `security-review` **job** (not the workflow), so a
skipped actor still
  reports a name-stable passing check.
- Downstream verification: with this change applied locally to the
consumer's materialized
`policy.json`, `runner-policy.mjs` reports `Runner policy passed.`
against the narrowed caller;
without it, exactly one finding (`inputs absent from its reviewed
contract: skip-actors`).

## Related

No linked issue in this repository — deliberately. The deliberation and
its record live in the
consuming repo (melodic-software/claude-code-plugins#1767), which is
closed by its own caller PR;
a cross-repo closing keyword here would close that issue on the wrong
merge.

- Decision: melodic-software/claude-code-plugins#1767 (ADR 0002
re-deliberation)
- Sequencing: this PR → standards→claude-code-plugins sync PR → the
caller + ADR PR in
  claude-code-plugins
- Consuming PR (blocked on this one):
melodic-software/claude-code-plugins#1845 — its
`runner-policy` check stays red until this merges and the sync
materializes
- Follow-ups filed, not implemented here: #308 (`requiredInputs`
contract field) and
melodic-software/ci-workflows#330 (narrow the security reusable's own
`skip-actors` default)
- Contract precedent: #211 (security-review contract), #229
(`paths-file` input), #161
  (`claude-review` runner-input contract)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01FVoZoMYXqf8ZVbQYixPVPW

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Register a runner-input contract for claude-review@df54d0e8 and bump Campaign A callers off the failure-reporting regression

1 participant