Skip to content

ci: route the standards-sync caller through the governed selector - #188

Merged
kyle-sexton merged 1 commit into
mainfrom
ci/govern-standards-sync
Jul 18, 2026
Merged

ci: route the standards-sync caller through the governed selector#188
kyle-sexton merged 1 commit into
mainfrom
ci/govern-standards-sync

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

No linked issue.

Standards-sync caller lane from the melodic-software/github-iac#157 plan — the last privileged-control-plane exception in this repository converts.

  • Contract (secret-capable runner-input, selector-lockstep): standards-sync.yml@ec91c343… registered in components/runner-policy/policy.json with routing: runner-input, the full input surface (runner, manifest, standards-ref, dry-run, targets), and the exact reviewed allowedSecrets mapping (app-client-id/app-private-key → the two STANDARDS_SYNC_* secret expressions). No allowedCallerPermissions — the caller stays statically read-only, so the contract alone owns the secret boundary. Secret-capable contracts decline Dependabot auto-approval unconditionally, so every future SHA of this reusable is human-reviewed.
  • Caller: sync.yml adopts the governed selector (success-guard shape per epic decision 4) with the routed runner input and the || 'ubuntu-24.04' fallback, and bumps the reusable pin 35f2684 → ec91c343 (the v0.6.0 revision that added the runner input — no ci-workflows change needed).
  • Exception removal: sync.yml#sync's privileged-control-plane entry is deleted; publish-packages#publish (publication, feat(runner-policy): classify packages-only write jobs as publication #184) is now this repository's only hosted exception.

Verification

  • GITHUB_REPOSITORY=melodic-software/standards node components/runner-policy/runner-policy.mjs passes with the exception removed; removing the contract instead fails closed on the caller.
  • npm run test:runner-policy: 219/219.
  • lint:md 0 errors; full pre-commit suite green.

Related

  • melodic-software/github-iac#157 (conversion plan), melodic-software/github-iac#78 (epic)

🤖 Generated with Claude Code

Standards-sync caller lane from the melodic-software/github-iac#157
plan: sync.yml adopts the governed selector (success-guard shape per
epic decision 4) and passes its routed runner into the ci-workflows
standards-sync reusable at the v0.6.0 pin, whose runner input already
ships at ec91c343. The secret-capable runner-input contract is
registered in components/runner-policy/policy.json — exact reviewed
allowedSecrets mapping (both STANDARDS_SYNC_* expressions), full input
surface, no allowedCallerPermissions (the caller stays statically
read-only) — and the sync.yml#sync privileged-control-plane exception
is removed; the contract, not an exception, is now the admitting
evidence.

## Verification

- GITHUB_REPOSITORY=melodic-software/standards node
  components/runner-policy/runner-policy.mjs passes with the exception
  removed; the contract is required (removing it fails closed).
- npm run test:runner-policy: 219/219.

## Related

- melodic-software/github-iac#157 (conversion plan)
- melodic-software/github-iac#78 (epic)

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

claude Bot commented Jul 18, 2026

Copy link
Copy Markdown

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


PR Review

  • Gather context (CLAUDE.md, REVIEW.md, PR diff)
  • Review .github/runner-policy.json changes
  • Review .github/workflows/sync.yml changes
  • Review components/runner-policy/policy.json changes
  • Cross-check contract correctness and security boundary
  • Post final review

The implementation is correct and consistent with the established governed-selector pattern in this repository. No critical or important findings.

Contract registration (components/runner-policy/policy.json)

The new contract at policy.json#L153–161 is well-formed:

  • SHA lockstep: Contract key (ec91c3433…) matches the caller pin in sync.yml exactly.
  • routing: "runner-input" + runnerInput: "runner": Consistent with the v0.6.0 reusable revision that added the runner input.
  • allowedSecrets expressions: ${{ secrets.STANDARDS_SYNC_APP_CLIENT_ID }} / ${{ secrets.STANDARDS_SYNC_APP_PRIVATE_KEY }} match exactly what the caller passes.
  • No allowedCallerPermissions: Correct — the sync.yml top-level permissions are statically contents: read, no job-level override exists, so there's nothing dynamic for the contract to bind.
  • Old @35f2684 contract preserved: Correct — backward-compat for any callers still on that SHA.
  • Secret-capable contract → no Dependabot auto-approval: Intentional and correct; every future SHA needs human review.
  • allowedInputs includes manifest and standards-ref: These are the full reusable input surface. The current caller doesn't pass them (they take defaults), but permitting them is sound — any caller that later adds them stays within the already-reviewed contract and any new SHA would require its own contract.

Caller (sync.yml)

The select-runner job structure is correct:

  • permissions: {} on the selector job — no repo permissions leak into the fleet-selection step.
  • SHA ec91c3433… is in approvedSelectorReferencesByRepositoryOwner["melodic-software"].
  • Success-guard shape (!cancelled() && needs.select-runner.result == 'success') matches epic decision 4.
  • runner: ${{ needs.select-runner.outputs.runner || 'ubuntu-24.04' }} — the || 'ubuntu-24.04' fallback is the standard pattern and matches approvedHostedRunnerLabels.
  • Both the select-runner and sync jobs now pin to the same ec91c3433… SHA, maintaining lockstep.

Exception removal (.github/runner-policy.json)

The sync.yml#sync privileged-control-plane entry is cleanly removed, leaving publish-packages.yml#publish as the sole hosted exception. The PR description's claim that the policy engine fails closed when the contract is removed (but passes with the exception removed) is consistent with how the runner-policy evaluates contracts vs. exceptions.

Security (REVIEW.md "Always check")

  • No literal secrets, tokens, or credentials in any tracked file. ✓
  • No parameterization concerns — these are CI configuration files, not query/invocation builders. ✓
  • The governed selector pattern closes the former privileged-control-plane gap without reducing the App-credentials security boundary; the secret expressions are unchanged.

No findings.

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.

1 participant