Skip to content

ci(security): regenerate the approved publish-workflow revisions on a schedule - #3568

Merged
devantler merged 8 commits into
mainfrom
claude/approved-revisions-regenerate-3552
Sep 4, 2026
Merged

devantler merged 8 commits into
mainfrom
claude/approved-revisions-regenerate-3552

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Agentic Engineer

Why

Every consumer's pin on a shared publish workflow moves with each devantler-tech/actions bump, several times a month. The approved-revision set the cosign matchers will be narrowed from is stale the moment that happens, and regenerating it by hand would turn every dependency bump into a two-repository ordered rollout. Until the set regenerates itself, the narrowing in #3308 is not safe to switch on.

What

A daily and on-demand workflow regenerates the set against prod and, when it has moved, opens or updates a single draft pull request carrying only the regenerated file. A consumer it cannot resolve is a red run and never a pull request that dropped that consumer. A contract test pins the workflow's safety properties, each with an ablation, and runs on every PR that touches the workflow, the test, the generator or its test.

The matchers themselves are not rewritten here: that is the narrowing switch, which follows only after this workflow has run green at least once (feature-flag-first, generated file first).

Part of #3308 · Fixes #3552

… schedule

Add a daily / on-demand workflow that runs the approved-revision generator
against prod with a least-privilege App token and, when the committed set
has moved, re-reads it through the approved-revisions guard and opens (or
updates) ONE draft pull request from a fixed branch carrying only the
regenerated data file. A consumer that cannot be resolved is a red run with
nothing committed, never a pull request that dropped it.

A contract test pins the workflow's shape — schedule + dispatch, empty
ambient grant, queue: single, environment prod, the generator pinned to
admin@prod on the App token, exactly actions=read/contents=write/
pull-requests=write minted, persist-credentials off, a fixed draft branch
with an API-signed commit limited to the data file, no continue-on-error,
and generator -> guard -> pull request ordering — with 20 ablations that
each trip exactly their own assertion. It is wired into the validate job,
and both this pair and the generator pair join the k8s change filter so a
PR editing either half alone still runs the proof.

Part of #3308 (third child, #3552).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

I repaired the current PR head after its required CI failed on three ShellCheck SC2015 findings in scripts/tests/test-regenerate-publish-workflow-approved-revisions.sh. The branch now includes live Platform main (e827ebda) plus signed repair commit 1e67c46b; locally, ShellCheck is clean, the 1 control + 20 ablations pass, the four-consumer approved-revision guard passes, and git diff --check is clean.

Fresh CI is running on 1e67c46b. This is a repair/readback note only, not a readiness or review claim; substantive current-head review remains outstanding after terminal checks.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Automated user evaluation on exact head 1e67c46b3ea84349ad94abfd64c32ceb4f5093e4: all terminal checks are green after rerunning only the production-authorization job that had stopped on curl: (35) Recv failure: Connection reset by peer; the rerun passed unchanged. The head is based on current main, no review threads exist, and the branch's focused local tests and ShellCheck evidence are recorded above. Requesting one substantive current-head review.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

@devantler I will review the current PR head and its security workflow changes.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 10 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Team

Run ID: 5940de3b-b508-44f6-b4ad-ca6cfdc8dfc1

📥 Commits

Reviewing files that changed from the base of the PR and between e827ebd and 1e67c46.

📒 Files selected for processing (3)
  • .github/workflows/ci.yaml
  • .github/workflows/regenerate-publish-workflow-approved-revisions.yaml
  • scripts/tests/test-regenerate-publish-workflow-approved-revisions.sh

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

CodeRabbit explicitly refused the exact-head request as rate limited. Automated user evaluation remains unchanged on 1e67c46b3ea84349ad94abfd64c32ceb4f5093e4: all terminal checks are green, including the unchanged retry of the transient production-authorization failure; the base is current and there are no review threads. Advancing to the next lane for one substantive current-head review.

@codex review

@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: 1e67c46b3e

ℹ️ 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".

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-04T10:18:49.543078Z 87225d7 Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

…own base

Four review findings, each a way the daily run would have gone red or opened
the wrong pull request, with an ablated contract assertion pinning every one.

- App token: create-github-app-token given neither `owner` nor `repositories`
  mints a token scoped to the repository the job runs in. The generator reads
  each consumer's cd.yaml and CD runs on the consumer's own repository, so
  every read would 404 and the generator would refuse on the pin half. Scope
  the token to this repository plus the consumers the approved set names.
- Concurrency and base: the pushed branch is fixed, so a dispatch from another
  ref and the scheduled run contend for the same pull request; a ref-keyed
  group put them in different groups. Make the group ref-independent and pin
  the pull request's base so a dispatch cannot target its own ref.
- Disclosure: the action supplies the complete body, so every pull request it
  opened lacked the attribution line AGENTS.md requires.
- Stable API endpoint: KUBE_CONFIG can name a control-plane node that
  `cluster update` later rolls, so reading OCIRepositories through it fails
  against a healthy cluster. Resolve the floating IP before any API call, the
  same ordering the deploy-prod composite uses.

The contract test gains A10-A13 with ten ablations. A10 derives the expected
token scope from the generated set, so CI's path filter now also watches that
file: without it, adding a consumer — the exact change that would strand the
token — would not run the check that catches it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI's shellcheck runs at its default severity and refused the `for c in $(…)`
form (SC2013), which my local `-S warning` pass had suppressed.

The obvious rewrite — a `while read` inside a command substitution — is worse
than the lint: the `case` pattern's own `)` closes the substitution early, so
the loop's source text was captured as the result and A10 failed the control
naming shell code as a missing consumer. Measured, not predicted.

Reads the consumers one per line and keeps the loop in the current shell via a
here-string, so nothing is split on whitespace, nothing runs in a subshell, and
no `case` sits inside a substitution. All 30 ablations still pass, including
the one that drops a consumer from the token scope.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Pull request metadata =='
gh pr view 3568 --repo devantler-tech/platform \
  --json number,title,headRefName,headRefOid,baseRefName,files \
  --jq '{number, title, headRefName, headRefOid, baseRefName, files: [.files[].path]}'

printf '%s\n' '== Checked-out commit =='
git rev-parse HEAD

printf '%s\n' '== Changed files relative to merge base =='
BASE="$(git merge-base HEAD origin/main)"
printf 'merge-base=%s\n' "$BASE"
git diff --name-status "$BASE"...HEAD

printf '%s\n' '== Structure of changed shell and workflow files =='
while IFS= read -r file; do
  case "$file" in
    *.sh) ast-grep outline "$file" --items all || true ;;
    *.yml|*.yaml) sed -n '1,260p' "$file" ;;
  esac
done < <(git diff --name-only "$BASE"...HEAD)

Length of output: 1974


🏁 Script executed:

#!/bin/bash
set -euo pipefail

BASE="$(git merge-base HEAD origin/main)"

printf '%s\n' '== Complete merge-base diff =='
git diff --find-renames --find-copies --no-ext-diff "$BASE"...HEAD -- \
  .github/workflows/ci.yaml \
  .github/workflows/regenerate-publish-workflow-approved-revisions.yaml \
  scripts/tests/test-regenerate-publish-workflow-approved-revisions.sh

printf '%s\n' '== Shell test outline =='
ast-grep outline scripts/tests/test-regenerate-publish-workflow-approved-revisions.sh --items all || true

printf '%s\n' '== Related workflow/data references =='
rg -n -C 4 --hidden \
  -g '*.yaml' -g '*.yml' -g '*.sh' -g '!**/.git/**' \
  'approved[-_ ]revisions|publish-workflow|cosign|workflow_ref' \
  .github scripts

Length of output: 206706


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Existing queue: single usage =='
rg -n -C 8 --hidden -g '*.yaml' -g '*.yml' -g '!**/.git/**' \
  '^[[:space:]]*queue:[[:space:]]*single[[:space:]]*$|^[[:space:]]*concurrency:' \
  .github

printf '%s\n' '== Workflow validation-related configuration =='
fd -HI -t f 'actionlint|workflow|github' . \
  | sed -n '1,160p'

printf '%s\n' '== create-pull-request references and workflow pins =='
rg -n -C 3 --hidden -g '*.yaml' -g '*.yml' -g '!**/.git/**' \
  'peter-evans/create-pull-request@|sign-commits:|branch-token:' \
  .github

Length of output: 23984


🤖 web_search executed:

GitHub Actions documentation concurrency syntax supported keys group cancel-in-progress queue single

Length of output: 18060


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Requested head and whitespace =='
test "$(git rev-parse HEAD)" = '7e9c56473633c5fdb9412f2ce8a44388820563fe'
git diff --check "$(git merge-base HEAD origin/main)"...HEAD

printf '%s\n' '== Parse workflow structure =='
yq -e '
  .name == "Regenerate Approved Publish Revisions" and
  .concurrency.group == "${{ github.workflow }}" and
  .concurrency.queue == "single" and
  .jobs.regenerate.environment == "prod" and
  (.jobs.regenerate.steps | length) > 0
' .github/workflows/regenerate-publish-workflow-approved-revisions.yaml

printf '%s\n' '== Local actionlint configuration =='
cat .github/actionlint.yaml

printf '%s\n' '== Static lint of changed shell test, if tool is available =='
if command -v shellcheck >/dev/null 2>&1; then
  shellcheck scripts/tests/test-regenerate-publish-workflow-approved-revisions.sh
else
  printf '%s\n' 'shellcheck is unavailable in the inspection environment'
fi

printf '%s\n' '== Pull request check summary =='
gh pr checks 3568 --repo devantler-tech/platform --json name,state,workflow,link \
  --jq '.[] | [.workflow, .name, .state, .link] | `@tsv`' || true

Length of output: 1557


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Local actionlint configuration =='
cat .github/actionlint.yaml

printf '%s\n' '== Available static linters =='
command -v shellcheck || true
command -v actionlint || true

printf '%s\n' '== Static lint of changed shell test =='
if command -v shellcheck >/dev/null 2>&1; then
  shellcheck scripts/tests/test-regenerate-publish-workflow-approved-revisions.sh
else
  printf '%s\n' 'shellcheck is unavailable in the inspection environment'
fi

printf '%s\n' '== Static lint of changed workflow =='
if command -v actionlint >/dev/null 2>&1; then
  actionlint .github/workflows/regenerate-publish-workflow-approved-revisions.yaml
else
  printf '%s\n' 'actionlint is unavailable in the inspection environment'
fi

printf '%s\n' '== Pull request check summary =='
gh pr checks 3568 --repo devantler-tech/platform --json name,state,workflow,link \
  --jq '.[] | [.workflow, .name, .state, .link] | `@tsv`' || true

Length of output: 5791


@devantler Reviewed the requested head 7e9c56473633c5fdb9412f2ce8a44388820563fe.

I found no blocking findings.

The workflow uses a fixed pull-request branch and main base. It scopes the App token to the required repositories and permissions. It runs the generator, detects changes outside the generated file, runs the guard, and then creates or updates the draft pull request.

The contract test has one control and 30 ablations. ShellCheck and Actionlint passed in the inspection environment. git diff --check passed. The current GitHub checks are successful.

You are interacting with an AI system.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

CodeRabbit answered this head as chat, not a review — the reply carries no review command invocation marker, there are zero review objects at 7e9c5647, and the head's CodeRabbit status still reads the never-reviewed default (Review skipped: automatic reviews are disabled). Its "no blocking findings" line is therefore not a gate-satisfying artifact, and its analysis chain shows a lint-level inspection (shellcheck, actionlint, the check summary) rather than a semantic review. The same shape on #3546 today opened with the identical chat preamble and its tail carried a real, specific bypass — so this reply's silence is not evidence of correctness.

Advancing the lane rather than spending a deeper CodeRabbit pass here: Codex already reviewed this PR at 1e67c46b and found all five issues, so it has the context, and the free lane's deeper form is better spent on #3546's guard logic.

@codex review

Both sides added to the same two places in ci.yaml, so the resolution keeps
both: #3565's OCIRepository verify step and its path-filter pair alongside this
branch's regeneration-workflow validation step and its filter entries. Nothing
from either side is dropped — verified by grepping for each step name and each
filter path after resolving.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@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: 7e9c564736

ℹ️ 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".

The tenants guide said the App token is minted from an org-level `APP_ID`
variable. Every workflow in the org that mints one passes
`client-id: ${{ vars.APP_CLIENT_ID }}` with `secrets.APP_PRIVATE_KEY` —
devantler-tech/actions' own template-sync, agent-plugins' update-agent-skills,
and ksail's cd — and that path demonstrably works: agent-plugins commit
744f0e06 was authored by the resulting App identity today.

The stale name is not merely untidy. Review on this PR read it as authoritative
and raised the workflow's correct `APP_CLIENT_ID` as a P1 that would break every
run, so the doc was actively generating false findings against correct code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

🔐 Validate Production Authorization timed out — diagnosed as a transient before re-running, not re-run on hope.

The step did not fail; it was killed by the 10-minute action limit mid-render, with every file it had reached logged ✔ … validated. Three things say this is not the change:

evidence reading
The same job on platform#3546, 15 minutes earlier: started 09:14:52Z → completed 09:16:51Z, success in ~2 min the job is normally nowhere near the limit
This run: 09:33:33Z → 09:45:12Z, ~11.6 min a ~5× stall, not a gradual creep
git diff origin/main…HEAD -- .github/workflows/ci.yaml is purely additive the production-authorization job is byte-identical to main's; the conflict resolution added a step and four filter entries and changed nothing else

This PR touches a workflow file, a path filter, a shell contract test and one documentation line — none of which can reach Helm rendering. Re-running the failed job once.

If it times out again, that is a different finding: the job would then be genuinely near its ceiling and the fix is the job's own budget, not this PR.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

The timeout was a transient, as diagnosed: the re-run is 21/21 green with no change to the tree, which is the falsifier I named coming out on the "transient" side rather than the "job near its ceiling" side.

Re-requesting at the current head. Two pushes have landed since the review at 7e9c5647, so that result is stale: a merge of main (resolving an additive ci.yaml conflict against the newly-merged #3565 — both sides kept) and the docs/TENANTS.md correction that came out of your APP_CLIENT_ID finding.

@codex review

@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: d94f8cc708

ℹ️ 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".

…ted set

Three review findings, each with an ablated assertion.

- ONE token became TWO. An installation token carries one permission set across
  every repository it names, so scoping a single token to this repository plus
  the four consumers granted contents/pull-requests WRITE over `.github`,
  `ascoachingogvaner`, `aws` and `wedding-app` — two of them private — when the
  generator only ever reads them. That widening arrived with the previous commit
  and is undone here: a read-only consumer token (contents+actions read) for the
  generator, and a platform-only write token for the checkout and the pull
  request. A10 now also refuses a consumer appearing in the WRITE list.
- The checkout is pinned to `main`. `base:` chooses the pull request's target but
  does not remove the dispatched ref's commits from the fixed regeneration
  branch, so a manual dispatch from a topic branch would carry them into the
  automation's pull request.
- The pull-request step is no longer gated on `moved`. A reverted consumer
  rollout returns the set to the committed one, so a gated step skipped and left
  the earlier run's pull request standing with the superseded set, still
  mergeable. devantler-tech/actions' sync-cluster-policies drives this same
  action at this same pinned revision ungated, for the same reason. A failure
  still stops it — there is no `if: always()`, which A9 pins.

Nine new ablations; 39 in total.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

All three findings from the last round are fixed and their threads resolved; CI is 21/21 green at this head and mergeStateStatus is CLEAN.

Staying on this lane rather than restarting at CodeRabbit, on measured evidence rather than preference: on this repository it acked a full review at 09:26, was refused by the OSS limit, and then went silent for over 20 minutes on a re-request using the identical trigger — no ack, no reaction, no artifact, and a watcher on it timed out. On this PR specifically it has never produced a review object at all, only chat responses. That is a lane not serving.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 87225d726c

ℹ️ 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".

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Readiness at 87225d726c10bfab6fc4b3a9b721e9040f9ab8e8

Reviewed — Codex green at this exact head. Codex Review: Didn't find any major issues with **Reviewed commit:** 87225d726c, matching headRefOid[0:10]. Checked the trap that makes that line insufficient on its own: Codex counts only P0/P1 as major, so a green and an open P2 can coexist — there are zero ## Review finding comments on this PR, so nothing is hiding in comment form.

CodeRabbit is recorded no-gate rather than skipped: it acked a full review on this repository at 09:26, was refused by the OSS limit, then went silent for over 20 minutes on an identically-shaped re-request, and on this PR produced only chat responses — never a review object.

Programmatically tested — 21/21 checks green, mergeStateStatus: CLEAN. The contract test carries 1 control + 39 ablations, each mutating the workflow in exactly one place and required to trip exactly the assertion it targets; nine of those are new this round, covering the token split in both directions, the pinned checkout ref, and the ungated pull-request step.

Threads — 9 of 9 fetched (totalCount 9), 0 unresolved, read paginated immediately before promotion rather than from an earlier snapshot.

Tried and evaluated as a user — and what that can honestly mean here. This change is a scheduled workflow. Its runtime surface is a workflow_dispatch, and that does not exist until the workflow is on the default branch, so it cannot be exercised before merge — the no-exercisable-surface case, not a waived condition. What was exercised instead is everything that can be: the contract test drives the workflow's shape with ablations proving each assertion can fail; the scripts it calls (generate-…, guard-…, use-prod-stable-api-endpoint.sh) have their own passing tests; and the App-token change was verified against live evidence rather than documentation — vars.APP_CLIENT_ID is the pair three org workflows use, and agent-plugins@744f0e06 was authored today by the identity it mints.

The first real execution is post-merge, which this PR's own design already assumes: the narrowing switch in #3569 flips only after this workflow has run green at least once. That first scheduled run at 06:20 UTC is the outstanding verification, and #3569 should not proceed before it.

Promoting and enqueueing.

@devantler
devantler marked this pull request as ready for review September 4, 2026 10:19
@devantler
devantler added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit c45381c Sep 4, 2026
27 checks passed
@devantler
devantler deleted the claude/approved-revisions-regenerate-3552 branch September 4, 2026 10:32
@github-project-automation github-project-automation Bot moved this from 🫴 Ready to ✅ Done in 🌊 Project Board Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

ci(supply-chain): regenerate the approved-revision sets on a schedule and open the narrowing PR automatically

1 participant