Skip to content

fix(ci): re-narrow the security lane's skip-actor exception to two actors - #1845

Closed
kyle-sexton wants to merge 5 commits into
mainfrom
fix/1767-adr-0002-skip-actors
Closed

fix(ci): re-narrow the security lane's skip-actor exception to two actors#1845
kyle-sexton wants to merge 5 commits into
mainfrom
fix/1767-adr-0002-skip-actors

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the delegated-authority decision recorded on #1767: the skip-actor widening
introduced by the v0.9.1 lane re-pin is REJECTED for the security lane.

  • .github/workflows/claude-security-review.yml restores an explicit
    skip-actors: "dependabot[bot],melodic-standards-sync[bot]", re-narrowing the exception to
    exactly the two actors ADR 0002's step-3 addendum ratified. Inheriting the reusable workflow's
    default (dependabot[bot],claude[bot],melodic-ai[bot],melodic-standards-sync[bot]) let
    claude[bot] and melodic-ai[bot] satisfy the required security-review / security-review
    check with no review run, on security-sensitive paths, with no compensating control — and the
    general review lane skips them too, so an AI-app-pushed commit would get zero review of any kind.
  • .github/workflows/claude-review.yml is deliberately untouched — advisory with no required
    check, so its skip list is runner-minute economy, not evidence. It keeps inheriting.
  • ADR 0002 gains a 2026-07-31 addendum with the ruling, the three rationale strands
    (compensating controls, latency-is-not-absence, staleness asymmetry), the three rejected
    alternatives, the cross-vendor advisory and its accepted counterargument, the composition with
    github-iac feat(claude-ops): add plugins skill for plugin-fleet sync/audit/converge #248/feat(disk-hygiene): post-#160 follow-ups — leak-signature hints, standing policy discovery, OS auto-clean awareness #228, and the sequencing.
  • The ADR's final revisit trigger is rewritten to cover both widening channels: a caller-list
    edit (Channel A) and inherited-default drift at a re-pin (Channel B — the one that actually
    fired here and which the old trigger did not name). The re-pin trigger above it now cross-links.

The restored value is byte-identical to the list this caller carried before the re-pin deleted it
(9ed2956a, #1766) — verified against that commit's diff, which dropped the same line from both
callers.

⚠️ The runner-policy check is EXPECTED RED on this PR — do not "fix" it by dropping the input

policy.json is a managed materialization of the standards runner-policy component, and the
contract for claude-security-review.yml@c136b27 currently admits only runner and paths-file.
Restoring skip-actors is an input-surface change that must land upstream first. This is
intentional fail-closed ordering, not a defect:

  1. feat(runner-policy): admit skip-actors on the claude-security-review contract standards#307 — adds skip-actors to that contract entry's allowedInputs
    (open, this is the blocker). It restores parity: the previous approved SHA's contract
    (66073e58) already admitted the input; the c136b27 entry dropped it.
  2. standards → here sync PR materializes the new policy.json.
  3. This PR goes green.

Verified locally: with the local policy.json temporarily patched to the post-sync shape, the
validator reports Runner policy passed.; reverted before commit, so this PR touches no managed
file.

The reason a green-check reflex is dangerous here is the residual gap the ADR records: runner-policy
rejects unexpected inputs but cannot require one, so deleting the skip-actors line turns the
check green and silently re-widens a required-check bypass. The caller carries an inline comment
saying exactly that, and the gap is filed upstream as melodic-software/standards#308.

Test plan

  • actionlint .github/workflows/claude-security-review.yml — clean.
  • zizmor .github/workflows/claude-security-review.yml — "No findings to report."
  • markdownlint-cli2 on the ADR — 0 issues.
  • node .github/standards/runner-policy/runner-policy.mjs — fails with exactly one finding,
    runner-target-contract: the reusable workflow call has inputs absent from its reviewed contract: skip-actors, and nothing else; passes cleanly (Runner policy passed.) once the contract entry
    is patched to the post-sync shape (simulated locally, then reverted).
  • Input verified against the reusable at the pinned SHA
    (ci-workflows@c136b27f404dd32ce3873f39a6f3443891d1c16e): skip-actors is a declared
    workflow_call input whose default is the four-actor list, and it gates the security-review
    job, not the workflow — so the check stays name-stable and a skipped actor still reports a
    passing context. No wedge risk for the two retained actors.
  • Provenance claim verified: git grep over .github/workflows/ on main finds no issue_comment
    trigger and no @claude dispatcher, so neither app can push in this repo today. The change is
    therefore dormant-by-construction on merge. The activating step is charted but gated:
    feat(claude-assistant): org @claude mention-responder lane (reusable workflow, V1 answer/re-review only) ci-workflows#255's V1 is read-only (no Edit/Write, no push) and would not open
    the channel; only its V2 (tag-mode fix-and-push, behind that issue's own approval gate) would.
    Landing now keeps that gate from silently carrying a security-review-bypass decision it was never
    scoped to make.
  • Every other check on this PR is green, including security-review / security-review itself.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_01FVoZoMYXqf8ZVbQYixPVPW

…o ratified actors

The v0.9.1 lane re-pin deleted this caller's explicit `skip-actors` list, so it
inherited the reusable workflow's default
(`dependabot[bot],claude[bot],melodic-ai[bot],melodic-standards-sync[bot]`) and
silently widened ADR 0002's operator-ratified two-actor exception to four —
`claude[bot]` and `melodic-ai[bot]` PRs would satisfy the REQUIRED
`security-review / security-review` check with no review run, on
security-sensitive paths.

Re-deliberated per the ADR's own revisit trigger (#1767) and rejected. The
caller restores `skip-actors: "dependabot[bot],melodic-standards-sync[bot]"`.
The two ratified actors each carry a compensating control; the two new ones
carry none, and the general review lane skips them too, so an AI-app-pushed
commit would get zero review of any kind. A stale explicit list fails closed; a
stale inherited default fails open — which is exactly how this widening
happened.

The general lane (`claude-review.yml`) is deliberately untouched: advisory with
no required check, so its skip list is runner-minute economy, not evidence.

ADR 0002 gains an addendum recording the ruling, its rationale, the rejected
alternatives, the cross-vendor advisory, and the composition with github-iac
#228/#248. The final revisit trigger is rewritten to cover both widening
channels — a caller-list edit AND inherited-default drift at a re-pin, the
channel that actually fired — and the caller carries a matching comment, since
runner-policy rejects unexpected inputs but cannot require one.

Fixes #1767

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVoZoMYXqf8ZVbQYixPVPW
@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 29s —— View job


Review: PR #1845

  • Gather context (diff, CLAUDE.md, REVIEW.md)
  • Understand the request (code review of this PR)
  • Review .github/workflows/claude-security-review.yml changes
  • Review ADR 0002 addendum changes
  • Post final review feedback

Per REVIEW.md, the code-review lane owns correctness/design/conventions/docs/maintainability and explicitly excludes security findings on this repo (.github/workflows/claude-security-review.yml exists, so that scope belongs to the security lane). Reviewed with that split in mind.

Findings

No correctness, design, or maintainability issues found. This is a well-scoped, well-documented two-file change.

Independently verified the PR's factual claims rather than taking them on faith:

  • Confirmed .github/standards/runner-policy/policy.json's approvedReusableWorkflowContracts entry for claude-security-review.yml@c136b27... (policy.json:527-539) lists allowedInputs: ["runner", "paths-file"] — no skip-actors. Adding the input in this PR will trip reusableWorkflowStatus()'s unexpected-input check in runner-policy.mjs:991-998, whose message template (the reusable workflow call has inputs absent from its reviewed contract: ...) matches the PR body's test-plan output verbatim. The "expected red" claim is accurate, not hand-waved.
  • Confirmed runner-policy.mjs has no requiredInputs-style concept anywhere (grepped the whole directory) — the "residual gap" the ADR addendum records (validator rejects unexpected inputs but can't require one) is real, not overstated.
  • Confirmed claude-review.yml has zero diff in this PR (git diff origin/main...HEAD -- .github/workflows/claude-review.yml is empty) — matches the PR body's claim that the general lane is deliberately untouched.

Documentation quality: the new ADR addendum (docs/adr/0002-...md:116-207) is internally consistent with the existing document — addendum heading level matches siblings, the quoted "creatable only by the App... cannot forge it" sentence it deliberately avoids touching is quoted verbatim correctly, and the rewritten revisit-trigger bullets (lines 200-207) correctly describe both widening channels including the one that actually fired (#1767). .markdownlint-cli2.jsonc disables MD013 (no line-length limit) and sets MD024 to siblings_only, so the long prose lines and repeated "## Addendum" headings are not lint violations.

Workflow comment (claude-security-review.yml:40-51): the inline "DELIBERATE DIVERGENCE" comment is dense but earns its place — it's the only thing standing between a future re-pin and a silent required-check bypass, and it correctly cross-references the ADR addendum rather than duplicating its reasoning.

I wasn't able to execute actionlint, zizmor, markdownlint-cli2, or node runner-policy.mjs myself in this sandbox (Bash tool required approval for each invocation and none was available), so I verified the underlying claims by reading the contract data and validator source directly instead of just trusting the PR body's reported tool output.

No [Fix this →] links — nothing here needs a fix.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ac1ad67cd3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# inherited default fails open. runner-policy rejects inputs outside the
# reviewed contract but cannot require one, so dropping this line
# re-widens the exception silently and CI stays green.
skip-actors: "dependabot[bot],melodic-standards-sync[bot]"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sync the runner contract before adding this input

At this revision, .github/standards/runner-policy/policy.json allows only runner and paths-file for the pinned security-review workflow, while the Runner policy CI job rejects every input absent from that contract. Consequently, this line makes every CI run fail with runner-target-contract: ... inputs absent from its reviewed contract: skip-actors, so the upstream contract change and its standards sync must land before this input is added rather than leaving the commit permanently unmergeable or patching the managed copy locally.

AGENTS.md reference: AGENTS.md:L11-L16

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown

Claude has reviewed this PR 5 times. The lane skips further automatic reviews after 5; deleting this comment resets the count.

The addendum claimed the ci-workflows split-the-default deliberation was
"recorded there" and left the standards `requiredInputs` gap unfiled, so both
follow-ups could drop silently. File them and cite the numbers:
ci-workflows#330 (narrow the security reusable's own skip-actors default) and
standards#308 (a `requiredInputs` contract field, so deleting the caller's
input cannot pass green).

Also grounds the latency-is-not-absence strand: the `@claude` dispatcher whose
arrival would activate the bypass is not hypothetical — ci-workflows#255
proposes exactly that lane org-wide.

Refs #1767

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVoZoMYXqf8ZVbQYixPVPW
@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 3m 24s —— View job


Review: PR #1845 (update — new commit e6c1193)

  • Gather context (diff since the previously-reviewed ac1ad67, PR body, ADR content)
  • Review the new commit's changes
  • Cross-check the PR description against the updated ADR content
  • Check citation formatting consistency in the ADR addendum
  • Post final review feedback

Per REVIEW.md, this lane owns documentation/correctness/design/conventions and excludes security findings (a claude-security-review.yml workflow exists here, so that scope stays with the security lane). The new commit touches no security-relevant lines — claude-security-review.yml is unchanged since ac1ad67, which the security lane and Codex already reviewed.

e6c1193 is a small, clean docs-only follow-up: it replaces three vague assertions in the 2026-07-31 ADR addendum ("a routine, plausible change," "recorded there as a follow-up," "the guard until standards grows...") with citations to the issues that were actually filed — ci-workflows#255, ci-workflows#330, and standards#308. Good practice: turning an assertion into a checkable citation.

Findings

  1. Suggestion — PR description is now stale against the ADR it ships. The PR body's ## Related section still reads "Follow-ups deliberately not opened here... ci-workflows splitting the security reusable's skip-actors default... and a standards requiredInputs-style contract field" — but the ADR addendum this same PR carries now cites both as filed: ci-workflows#330 and standards#308 (see lines 161-162 and line 190). A reader who trusts the PR description over the ADR body would conclude the opposite of what actually happened. Worth a description edit before merge so the two stop contradicting each other.

  2. Suggestion — inconsistent cross-repo citation style within the same addendum. The composition paragraph references github-iac's #248 and #228 as bare numbers, while the paragraphs just above it (added in this same PR) cite ci-workflows#255, ci-workflows#330, and standards#308 as owner-qualified markdown links. A bare #248 in this repo's markdown won't auto-link to github-iac's tracker (GitHub only auto-links bare #N within the same repo) and reads as if it could be this repo's own issue feat(claude-ops): add plugins skill for plugin-fleet sync/audit/converge #248. Suggest qualifying both to match the convention this commit just established, e.g. [github-iac#248](https://github.com/melodic-software/github-iac/issues/248).

Neither finding blocks merge — both are documentation polish, not correctness or security issues. Everything substantive from the first review pass (workflow comment quality, ADR internal consistency, markdownlint config, the runner-policy sequencing claim) still holds; this delta doesn't change it.

I wasn't able to run markdownlint-cli2 or gh pr view myself in this sandbox (both required approval that wasn't available), so finding 1 was verified by comparing the PR body text provided in the trigger context directly against the current file content on disk, and finding 2 by reading the rendered file.

The latency-is-not-absence strand said adopting the proposed `@claude`
responder lane would activate the review bypass. Its V1 is deliberately
read-only — no Edit/Write, no push — so V1 would not; only its V2
(tag-mode fix-and-push, behind that issue's own approval gate) opens the
channel. Overstating the imminence weakens the argument it was meant to
support, and the accurate version is the stronger one: the activating step is
gated, which is precisely when pre-authorizing a bypass is cheapest to avoid.

Refs #1767

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVoZoMYXqf8ZVbQYixPVPW
@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 58s —— View job


Review: PR #1845 (update — new commit ce4a906)

  • Gather context (diff since previously-reviewed e6c1193, PR body, ADR content)
  • Review the new commit's changes
  • Cross-check the PR description against the updated ADR content
  • Post final review feedback

Per REVIEW.md, this lane owns documentation/correctness/design/conventions and excludes security findings (a claude-security-review.yml workflow exists here, so that scope stays with the security lane). This commit touches no security-relevant lines — .github/workflows/claude-security-review.yml has zero diff since ac1ad67, which the security lane and Codex already reviewed.

ce4a906 is a small, docs-only refinement of rationale strand 2 ("Latency is not absence") in the 2026-07-31 ADR addendum (lines 139-147). It sharpens an overbroad claim — "adopting [ci-workflows#255] would silently activate a review bypass" — into a version-scoped one: V1 of that proposed lane is read-only (no push), so only its gated V2 (tag-mode fix-and-push) would activate the channel. This is a genuine improvement in precision; the prior wording implied any adoption of #255 was risky, when only a specific, separately-gated mode is.

Findings

No correctness or maintainability issues in this delta.

Note (not new, still open): the second suggestion from the previous review pass — inconsistent citation style for cross-repo references (ci-workflows#255/#330/standards#308 as owner-qualified markdown links vs. bare #248/#228 for github-iac in the composition paragraph, lines 176-181) — remains unaddressed. Still non-blocking documentation polish, not correctness.

I could not independently verify the substance of the ci-workflows#255 V1/V2 claim (that repo's issue content isn't available to me in this sandbox), so I'm treating it as the PR author's representation of that external issue's state, consistent with how the prior review passes handled cross-repo claims.

Everything substantive from the first two review passes (workflow comment quality, ADR internal consistency, markdownlint config, the runner-policy sequencing claim) still holds; this delta doesn't change it.

`#248` and `#228` were written bare inside a claude-code-plugins document, so
GitHub autolinks them to this repository's own items — the wrong targets. The
repo's ADR convention requires a repository-qualified URL for a cross-repository
reference. Qualify both, and name the four app-pinned contexts explicitly
(`pr-title / pr-title`, `do-not-merge / do-not-merge`, `ci-status`,
`security-review / security-review`) rather than leaving "all four" for a
future reader to reconstruct; verified against the live rulesets, all pinned to
integration_id 15368.

Refs #1767

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVoZoMYXqf8ZVbQYixPVPW
@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown

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


Review: PR #1845 (update — new commit d41e5ed)

  • Gather context (diff since previously-reviewed ce4a906, PR body)
  • Review the new commit's changes
  • Check whether it resolves the outstanding citation-style finding
  • Post final review feedback

Per REVIEW.md, this lane owns documentation/correctness/design/conventions and excludes security findings (a claude-security-review.yml workflow exists here, so that scope stays with the security lane). This commit touches no security-relevant lines — .github/workflows/claude-security-review.yml has zero diff since ac1ad67, which the security lane and Codex already reviewed.

d41e5ed is a small, docs-only refinement of the composition paragraph in the 2026-07-31 ADR addendum (lines 173-185).

Findings

None. This commit resolves the one still-open item from the prior review passes:

Previously flagged (suggestion, non-blocking): inconsistent cross-repo citation style. The composition paragraph referenced github-iac's #248 and #228 as bare numbers while the paragraphs above it cited ci-workflows#255/#330 and standards#308 as owner-qualified links — a bare #N only auto-links within the same repo, so it read as if it could be this repo's own issue. This commit fixes it: both are now qualified and linked (github-iac#248, github-iac#228), matching the convention established by the earlier commits. I checked the rest of the addendum for the same pattern (grep -n '#[0-9]\+') — every remaining bare #N reference (#696, #509, #618, #697, #1767) is a same-repo issue, which is correctly left unqualified since GitHub auto-links those. Citation style is now consistent throughout the document.

The commit also adds concrete detail to the github-iac#248 claim (the four specific required contexts it app-pins — pr-title / pr-title, do-not-merge / do-not-merge, ci-status, security-review / security-review — and the GitHub Actions app's integration_id 15368, plus a merge date). This is a strictly-more-verifiable version of the prior prose; I could not independently confirm the github-iac-side facts (that repo isn't available in this sandbox), so I'm treating them as the PR author's representation, consistent with how earlier passes handled cross-repo claims.

Both suggestions from the earlier review rounds are now addressed (the PR-description staleness was fixed between e6c1193 and this update, and this commit fixes the citation-style inconsistency). Everything substantive from the first three review passes (workflow comment quality, ADR internal consistency, markdownlint config, the runner-policy sequencing claim) still holds.

No [Fix this →] links — nothing here needs a fix.

The parenthetical explained why the reference is repository-qualified. The ADR
convention already owns that rule; restating it beside one use is duplication
that says nothing about the decision.

Refs #1767

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVoZoMYXqf8ZVbQYixPVPW
@claude

claude Bot commented Jul 31, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 48s —— View job


Review: PR #1845 (update — new commit 9aabae0)

  • Gather context (diff since previously-reviewed d41e5ed, PR body)
  • Review the new commit's changes
  • Post final review feedback

Per REVIEW.md, this lane owns documentation/correctness/design/conventions and excludes security findings (a claude-security-review.yml workflow exists here, so that scope stays with the security lane). This commit touches no security-relevant lines — .github/workflows/claude-security-review.yml has zero diff since ac1ad67, which the security lane and Codex already reviewed.

9aabae0 is a one-line docs-only trim to the composition paragraph in the 2026-07-31 ADR addendum (lines 176-177). It drops the parenthetical (repository-qualified — a bare #N here would resolve to this repository) that explained why github-iac#248/#228 were written as owner-qualified links.

Findings

None. This is a clean simplification, not a regression:

  • The citations themselves ([github-iac#248](...), [github-iac#228](...)) are untouched — only the explanatory aside about the linking convention was removed. The convention is self-evident from the qualified form itself; the meta-note was explaining a citation-style rule inline in prose that's about CI/security ruling, which is a reasonable thing to trim once the fix (from d41e5ed) already speaks for itself.
  • No new ambiguity introduced: nothing else in the addendum depended on that parenthetical for context.

Everything substantive from the earlier review passes (workflow comment quality, ADR internal consistency, markdownlint config, the runner-policy sequencing claim, the now-resolved citation-style and PR-description-staleness findings) still holds; this delta doesn't change any of it.

No [Fix this →] links — nothing here needs a fix.

kyle-sexton added a commit to melodic-software/standards 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>
@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Cross-lane finding: this PR cannot pass as written, and the fix belongs upstream rather than in this caller.

Runner policy fails because the managed runner-policy contract for claude-security-review.yml@c136b27f… (v0.9.1) declares allowedInputs: ["runner", "paths-file"]skip-actors is deliberately excluded at that pin. That exclusion is what forced this repo's inline-pathspaths-file migration in #1766; passing any input outside the reviewed contract fails the runner-policy lane, which is a needs: dependency of the required ci-status aggregate.

The substantive argument here is correct and I am not disputing it: inheriting the reusable's four-actor default lets claude[bot] and melodic-ai[bot] satisfy the required security-review / security-review check with no review run, which exceeds ADR 0002's ratified two-actor bound.

But the concern is fleet-wide, not repo-local — every consumer that re-pins to v0.9.1 inherits the same widening, and a per-caller override is exactly the drift the centralization was meant to end (this repo's callers previously lost the ban list that way). So the durable fix is to narrow the security reusable's own default in ci-workflows, leaving the review lane's wider default alone since that lane is advisory and gates nothing.

I am opening that upstream change now and will link it here. Suggest holding this PR until it lands, then closing it as superseded — or keeping only its ADR 0002 documentation half, which stands on its own merits.

@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Full analysis and disposition recorded on #1767 (issuecomment-5147088300).

Summary for this PR specifically: the workflow half cannot be salvaged as written — passing skip-actors fails the runner-policy contract at the v0.9.1 pin, and even upstream, narrowing alone would pin claude[bot]/melodic-ai[bot] PRs permanently red rather than reviewing them, because the action's hardcoded allowed_bots gate throws and core.setFaileds for unlisted bots. Recommend dropping the workflow change and keeping the ADR amendment, which stands on its own merits either way.

The real fix lives upstream in ci-workflows (both the skip-actors default and allowed_bots) and needs an operator decision first — see the two options on #1767.

@kyle-sexton kyle-sexton added the do-not-merge Hard merge gate: do not merge while applied. label Jul 31, 2026
@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Stopping here rather than resolving this one — the conflict is a decision, not a mechanic, and the decision is yours.

What happened

I rebased this branch onto main to clear its DIRTY state. Two conflicts:

.github/workflows/claude-security-review.yml — mechanical, resolvable. Both sides restore an explicit skip-actors list for the same stated reason (an inherited default silently rewrites the exception). They differ only in the list's contents, which is the second conflict's subject.

docs/adr/0002-...md — not mechanical. This branch adds an Addendum (2026-07-31) ruling the widening REJECTED and re-narrowing to two actors. main now carries an Addendum (2026-08-04):

the widened four-actor exception is ratified. The silence default was not exercised.

and states four is the ratified baseline that future additions are measured against.

Why I did not pick

That is an explicit operator decision dated four days after this branch's argument, on a security-gate exception. Merging this PR reverses it; closing it discards the reasoning that produced it. Neither is a call I should make silently inside a conflict resolution — and the ADR itself demands the opposite, since its revisit trigger requires the widening be re-deliberated and recorded, not settled by whichever branch merged last.

Worth noting: the 2026-08-04 addendum reads as having considered this branch's position — its Branch A / Branch B framing weighs the two-actor case explicitly and rules against it on the allowed_bots cross-repo dependency. So this may already be decided and the PR simply outlived its ruling.

The branch is untouched; git merge --abort left it at 9aabae0d. Nothing here is lost whichever way you go.

The three ways forward

  1. Close it as superseded (my read of the evidence). The 2026-08-04 addendum decided this question with your explicit pick. If so, the branch's residual value is the workflow comment — the DELIBERATE DIVERGENCE block explaining why the line must not be deleted at a re-pin, which main's shorter comment does not fully carry. I can port just that comment as a small separate PR.

  2. Re-open the decision. If the 2026-08-04 ratification rested on premises that have since changed — the addendum names them: both actors dormant, low lane value on agent-authored chores — then this branch's argument is live again and needs a new addendum superseding the ratification, not a merge that silently contradicts it.

  3. Merge as-is. Only if you intend to reverse 2026-08-04. It would need its own addendum saying so, or the ADR ends up carrying two contradictory rulings with no record of which governs.

Tell me which and I will finish it.

@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Closing as superseded, with the operator's confirmation.

What supersedes it

This branch's Addendum (2026-07-31) rules the skip-actor widening REJECTED and re-narrows the exception to two actors. main carries an Addendum (2026-08-04) recording an explicit operator decision:

Branch A — the widened four-actor exception is ratified. The silence default was not exercised.

and it names four as the baseline future additions are measured against. That addendum weighs this branch's position on its merits — its Branch A / Branch B framing puts the two-actor case directly against the allowed_bots cross-repo dependency and rules on the margin — so this is a decided question, not an open one. A dated operator decision made four days after the branch's own argument governs.

Merging would have reversed that decision inside a conflict resolution, which is not where a security-gate exception should be re-decided.

What was salvaged

The branch's DELIBERATE DIVERGENCE comment block was its remaining unique value, and it survives the ruling: it explains why the skip-actors line must not be deleted at a re-pin even when its value matches the upstream default — the state that makes deletion look safe, and the state #1766 was actually in. main's shorter comment did not carry that, nor the fail-closed / fail-open asymmetry the ADR's rationale turns on.

Ported in #2069, with the actor list left at the ratified four.

What is NOT closed by this

#1767 stays open. The residual gap it tracks is structural, not editorial: runner-policy rejects inputs outside the reviewed contract but cannot require one, so a future re-pin that again drops this line passes CI silently. A comment is a guard against a careful reviewer, not against an inattentive one. melodic-software/standards#308 is the requiredInputs-style contract field that would close it properly.

The branch stays

fix/1767-adr-0002-skip-actors at 9aabae0d is left in place rather than deleted. It is the only record of the two-actor deliberation as originally argued, and the ADR's revisit trigger explicitly anticipates this being reopened if the ratification's premises change — the addendum names them: both new actors dormant, low lane value on agent-authored chores. If either stops holding, this branch is the starting point, not a blank page.

@kyle-sexton kyle-sexton closed this Aug 9, 2026
kyle-sexton added a commit that referenced this pull request Aug 9, 2026
No linked issue — this closes nothing. #1767 stays open deliberately:
its residual gap is
structural (`runner-policy` cannot *require* an input), and a comment
guards against a careful
reviewer, not an inattentive one. Closing it on a documentation change
would record the gap as
fixed when it is not.

## What

Seventeen lines of comment above `skip-actors` in
`.github/workflows/claude-security-review.yml`. No value changes; the
actor list stays at the
ratified four.

## Why

The existing comment says the list is stated explicitly rather than
inherited, and records how
#1766 widened it. It does not say why deleting the line is unsafe **when
its value matches the
upstream default of the moment** — which is the only state in which a
reviewer is tempted to delete
it, and precisely the state #1766 was in.

Two things the re-pin reviewer needs and the comment did not carry:

- **Deleting is not a no-op at parity.** The four actors are ADR 0002's
2026-08-04 ratified
baseline. Inheriting re-delegates that ratification to whatever
`ci-workflows` ships next, and
  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 what makes
explicit correct here without
contradicting the fleet's inheritance-over-explicit-list default
elsewhere. A stale explicit list
fails CLOSED and LOUD — for an actor the reusable's `allowed_bots` does
not permit, the action
throws and the fail-closed mapping turns that into a red required check
no push can fix. A stale
inherited default fails OPEN and SILENT — an actor this repo never
deliberated skips review with
CI green. Loud-and-blocking is the direction to fail in;
silent-and-permissive is not.

The first draft of this comment described the closed direction as "an
actor gets a review it may
not have needed — visible, cheap." That was wrong, and review caught it:
ADR 0002's
`skip-actors`-vs-`allowed_bots` section records that removing an actor
from `skip-actors` alone
does not restore review of its PRs — it blocks the merge. Corrected in
the second commit, which
also notes that un-skipping an actor for real requires widening
`allowed_bots` upstream, so
  removing a name here is never a one-line change.

The comment also names standards#308 at the point of use:
`runner-policy` rejects inputs outside the
reviewed contract but cannot *require* one, so dropping this line
re-widens the exception with CI
green and nothing said.

## Provenance

Salvaged from PR #1845, which I closed as superseded. That branch
re-narrowed the exception to two
actors on 2026-07-31; the 2026-08-04 addendum records an explicit
operator decision ratifying four
and names four as the baseline future additions are measured against,
having weighed the two-actor
case on its merits. The narrowing is therefore correctly dead — but the
branch's comment block was
the better explanation of an invariant that survives the ruling either
way, so it is ported here
with the actor list left alone.

## Verification

- `actionlint` clean on the changed file.
- The `skip-actors` value is byte-identical before and after — confirmed
by reading the parsed line
  back, not by eye.
- Comment-only diff: 17 insertions, 0 deletions.

## Related

- #1767 — the re-deliberation issue this comment serves; left open,
since the residual
  `requiredInputs` gap is not closed by a comment.
- #1845 — the superseded branch this is salvaged from.
- #1766 — the re-pin that dropped the line and silently widened the
exception.
- `docs/adr/0002-default-on-ai-review-advisory-with-earned-promotion.md`
— the 2026-08-04 addendum
  that ratified four actors.
- melodic-software/standards#308 — the `requiredInputs`-style contract
field that would make this
  structurally enforced rather than comment-enforced.

---------

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

Labels

do-not-merge Hard merge gate: do not merge while applied.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Re-deliberate ADR 0002: skip-actors inheritance widens the security-review exception to claude[bot] and melodic-ai[bot]

1 participant