From 5a7270afae2497a5acc53fbfe569eded7da50a05 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Sat, 18 Jul 2026 12:21:30 -0700 Subject: [PATCH 1/2] publish-plan: annotate a non-publishing pin push; codify fail-loud gates (#354) The push gate matches github.actor against a hard-coded bot allowlist. The allowlist is correct today, but if the App is renamed, replaced, or reinstalled under a new slug the comparison quietly evaluates false, publish is skipped, and the run stays green. The weekly schedule keeps publishing, so the only symptom is lost release timeliness - easy to miss for months, and it defeats the tracker's same-day purpose. Option 1 from the issue (minimal): a main pin push by an unrecognized actor now emits a ::warning:: instead of falling through silently. The publish decision is unchanged for every case - simulated across the matrix (codegen/dependabot/renamed-App/human x main/develop, plus schedule and dispatch): identical publish= results, the warning firing only on the silent-failure path. Also codifies the transferable rule as WORKFLOW.md D8.4: an identity allowlist used as a gate fails loud. It notes where an annotation is optional (the merge-bot's failure is self-announcing - bot PRs visibly pile up) and names the run-time identity resolution (GET /app) as the escalation if an allowlist proves fragile. Co-Authored-By: Claude Opus 4.8 (1M context) --- WORKFLOW.md | 1 + catalog/snippets/workflows/publish-plan-task.yml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/WORKFLOW.md b/WORKFLOW.md index 2431bb3b..9d2312a0 100644 --- a/WORKFLOW.md +++ b/WORKFLOW.md @@ -196,6 +196,7 @@ The required behaviors, organized by domain. Each is a **MUST**, stated as input - **D8.1 Merge-bot.** Output: enables auto-merge on `opened`/`reopened` for **every** Dependabot tier including semver-major (the required checks are the gate, not the bump magnitude); dispatches `--squash`/`--merge` by the PR's base ref; disables on a maintainer-pushed `synchronize`; concurrency keyed on the **PR number**, not `github.ref`. *Prevents: two PRs colliding in auto-merge.* - **D8.2 CodeGen and Dependabot.** Output: codegen runs as a matrix over both branches and is deterministic from an external source; Dependabot targets both branches, security PRs to default. - **D8.3 Upstream-version tracker.** Output: a scheduled resolver prints a JSON `name -> version` object to a committed state file, opens a rolling per-branch bump PR naming only the moved keys, the merge-bot auto-merges it; the `main` pin push publishes via the release gate, while a `develop` pin does not auto-publish - it ships via a `develop` dispatch (prerelease) or the next promotion to `main`. The tracker's `bump-branch-prefix` + `branches` MUST match the merge-bot's hard-coded `-` head/base pairs, or auto-merge silently never fires. +- **D8.4 An identity allowlist used as a gate fails loud.** Where a gate compares `github.actor` (or a PR author) against hard-coded bot identities, the non-matching branch on an otherwise-legitimate trigger **emits a `::warning::`** rather than falling through silently. Output: a run that declines to act on an unrecognized identity is visibly annotated. *Prevents: the App being renamed, replaced, or reinstalled under a new slug, after which the comparison quietly evaluates false and the gate stops firing - a green, silent run that looks identical to a healthy one.* The masking matters most where a second path hides the loss: a weekly schedule keeps publishing, so the only symptom is release *timeliness*, easily missed for months. Where the failure is self-announcing instead (the merge-bot simply stops merging, so bot PRs visibly pile up) an annotation is optional. Resolving the identity at run time (mint an App token, read `GET /app`) removes the hard-coded string entirely and is the escalation if an allowlist proves fragile in practice. ### D9 - Style / Static (See Section 2) diff --git a/catalog/snippets/workflows/publish-plan-task.yml b/catalog/snippets/workflows/publish-plan-task.yml index 8eaef7f5..32ca14ed 100644 --- a/catalog/snippets/workflows/publish-plan-task.yml +++ b/catalog/snippets/workflows/publish-plan-task.yml @@ -74,6 +74,11 @@ jobs: # ref==main guard keeps the task self-contained even if a caller's push trigger is not main-only. if [[ "$REF" == "main" ]] && { [[ "$ACTOR" == "ptr727-codegen[bot]" ]] || [[ "$ACTOR" == "dependabot[bot]" ]]; }; then publish=true + elif [[ "$REF" == "main" ]]; then + # Fail loud: an unrecognized actor on a main pin push is either a hand edit (rare, worth seeing) + # or the App under a new identity, which would otherwise stop publishing silently - the weekly + # schedule keeps releasing, so the only symptom would be lost timeliness. + echo "::warning::Pin push on main by unrecognized actor '$ACTOR'; not publishing. If this is the codegen App under a new identity, update the allowlist in publish-plan-task.yml." fi ;; esac From 7b769975868a3903d921423f9235e830991aeea4 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Sat, 18 Jul 2026 12:24:44 -0700 Subject: [PATCH 2/2] Generalize the warning wording; the task is not pin-specific (Copilot #355) publish-plan-task is the generic publish gate every publish-release job reuses, not only the upstream-version pin flow, so "Pin push on main" would misread on any other push trigger. The message and its comment now say "Push to main ... release bot", which is accurate for every consumer. Co-Authored-By: Claude Opus 4.8 (1M context) --- catalog/snippets/workflows/publish-plan-task.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/catalog/snippets/workflows/publish-plan-task.yml b/catalog/snippets/workflows/publish-plan-task.yml index 32ca14ed..b5997d2c 100644 --- a/catalog/snippets/workflows/publish-plan-task.yml +++ b/catalog/snippets/workflows/publish-plan-task.yml @@ -75,10 +75,10 @@ jobs: if [[ "$REF" == "main" ]] && { [[ "$ACTOR" == "ptr727-codegen[bot]" ]] || [[ "$ACTOR" == "dependabot[bot]" ]]; }; then publish=true elif [[ "$REF" == "main" ]]; then - # Fail loud: an unrecognized actor on a main pin push is either a hand edit (rare, worth seeing) - # or the App under a new identity, which would otherwise stop publishing silently - the weekly - # schedule keeps releasing, so the only symptom would be lost timeliness. - echo "::warning::Pin push on main by unrecognized actor '$ACTOR'; not publishing. If this is the codegen App under a new identity, update the allowlist in publish-plan-task.yml." + # Fail loud: an unrecognized actor pushing to main is either a human commit (legitimately not + # publishing, but worth seeing) or a release bot under a new identity, which would otherwise + # stop publishing silently while a schedule keeps releasing - lost timeliness, no error. + echo "::warning::Push to main by unrecognized actor '$ACTOR'; not publishing. If this is a release bot under a new identity, update the allowlist in publish-plan-task.yml." fi ;; esac