Repository navigation
feat: implement issue #1297 — Synced stubs in ring repos carry a header naming v1-stable above a uses: line pinned to their ring — bots flag it and agents edit the stub to match #1298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
4f880bf
83a1175
70ed8af
b6f3d24
4bb767a
3fbd2c9
506ded3
4b57d18
4c421b5
0f414c9
572180f
c638177
1f70096
03b5dd3
3f0de8a
f47c49c
d6d2d69
666b6d9
a294cb9
d7ec4f4
1eea28d
ec25264
348aae4
01f4602
63444b5
4708ee3
9842d6f
9051276
8e71eb3
3ed50a2
dd65220
2269059
767380b
9bee24c
b2366d9
ad33b1b
49d3107
97df195
a7e67e7
e6653a6
8411623
b150f8d
1cbfb3a
226791d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -158,6 +158,9 @@ DEPLOYABLE_WORKFLOWS=( | |
| pr-auto-review.yml | ||
| feature-ideation.yml | ||
| pr-review.yml | ||
| idea-enhancer.yml | ||
| idea-triage.yml | ||
| initiative-planner.yml | ||
| ) | ||
|
|
||
| # Deployable workflows whose stub BODY carries a documented per-repo edit the | ||
|
|
@@ -220,6 +223,11 @@ readonly REPLACE_IF_PRESENT_WORKFLOWS=( | |
| # the sweep must never read, replace or re-pin it. | ||
| declare -A ENGINE_HOST_REPOS=( | ||
| ["pr-review.yml"]="$ENGINE_HOST_REPO" | ||
| # The central BMAD idea engines live under these names in .github-private; their | ||
| # callers here are stubs, so the sweep must not treat the engines as consumers. | ||
| ["idea-enhancer.yml"]="$ENGINE_HOST_REPO" | ||
| ["idea-triage.yml"]="$ENGINE_HOST_REPO" | ||
| ["initiative-planner.yml"]="$ENGINE_HOST_REPO" | ||
| ) | ||
|
|
||
| # is_replace_if_present_workflow <name.yml> -> 0 if the workflow deploys only over | ||
|
|
@@ -601,6 +609,85 @@ merge_guarded_surfaces() { | |
| return 0 | ||
| } | ||
|
|
||
| # stub_has_stale_stable_header <existing_content> <template> <repo> -> 0 if the stub carries a | ||
| # comment naming its agent's stable channel (`<base>/stable`, `<base>/v<M>-stable`, | ||
| # `<base> stable channel`) that the template no longer carries (#1297). Such a header | ||
| # contradicts a ring pin; the pin alone is compliant, so without this check the sweep | ||
| # would never ship the comment-only header fix to existing ring repos. In ring repos, | ||
| # even a currently stable-pinned stub must migrate its header because the in-place | ||
| # repin will change the pin to ring, leaving the header contradictory. | ||
| stub_has_stale_stable_header() { | ||
| local existing="$1" template="$2" repo="$3" base re tier | ||
| base="$(reusable_base_of "$template")" | ||
| [[ -n "$base" ]] || return 1 | ||
| re="^[[:space:]]*#.*(${base}/(v[0-9]+-)?stable|${base} stable channel)" | ||
| grep -qE "$re" <<< "$existing" || return 1 | ||
| # A stable-pinned stub's header agrees with its pin, BUT only when the repo is | ||
| # stable-tier. In ring-managed repos, a stable-pinned stub is itself an anomaly | ||
| # that the repin will fix; the header must migrate too, so don't exempt in ring. | ||
| if ring_is_ring_reusable "$base" && [[ -n "$repo" ]]; then | ||
| tier="$(ring_tier_for_repo "$base" "$repo" 2>/dev/null)" || tier="" | ||
| # Ring-managed repo with a tier: the stub should be ring-pinned, so the header | ||
| # is stale regardless of the current pin. | ||
| if [[ -n "$tier" ]] && [[ "$tier" != "stable" ]]; then | ||
| # The template may still carry the stable wording; ring renders (ring_repin_uses) | ||
| # and the header cleanup neutralize it, so the template never exempts a ring repo. | ||
| return 0 # Flag as stale | ||
| fi | ||
| fi | ||
| ! grep -qE "$re" "$template" || return 1 | ||
| # Non-ring reusable or stable-tier repo: apply the exemption (don't flag if | ||
| # the stub is stable-pinned, since the header agrees with the pin). | ||
| ! grep -qE "^[[:space:]]*uses:.*@${base}/(v[0-9]+-)?stable([[:space:]]|\$)" <<< "$existing" | ||
| } | ||
|
|
||
| # strip_stale_stable_header <base> [<template>] -> stdin minus the stale stable-channel | ||
| # header. With <template>, the stub's whole leading comment block (everything above the | ||
| # first top-level YAML key) is replaced by the template's canonical, tier-neutral header, | ||
| # so a multiline legacy bullet is never left half-deleted. The body is kept verbatim, so | ||
| # per-repo edits (e.g. workflow_run.workflows) survive and the next sweep sees no stale | ||
| # header (#1297). Without <template>, only the lines naming the stable channel are dropped. | ||
| strip_stale_stable_header() { | ||
| local base="$1" template="${2:-}" | ||
| if [[ -n "$template" && -r "$template" ]]; then | ||
| # Body comment blocks (consecutive comment lines) that name the stable channel are | ||
| # dropped whole too, so a legacy `# Pinned to the moving ... stable channel` block under | ||
| # `jobs:` does not survive and keep the detector true forever. | ||
| awk -v tpl="$template" -v base="$base" -v re="^[[:space:]]*#.*(${base}/(v[0-9]+-)?stable|${base} stable channel)" ' | ||
| BEGIN { | ||
| while ((getline l < tpl) > 0) { if (l ~ /^[^#[:space:]]/) break; hdr = hdr l "\n" } | ||
| # The template header may still name the stable channel; render it tier-neutral | ||
| # (same wording as ring_repin_uses) so cleanup never reinstalls it. | ||
| gsub(base "/(v[0-9]+-)?stable", base "/<tier>", hdr) | ||
| gsub(base " stable channel", base "/<tier> channel", hdr) | ||
| } | ||
| function flush() { if (!stale) printf "%s", blk; blk = ""; stale = 0 } | ||
| !body && /^[^#[:space:]]/ { body = 1; printf "%s", hdr } | ||
| body && /^[[:space:]]*#/ { blk = blk $0 "\n"; if ($0 ~ re) stale = 1; next } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| body { flush(); print; next } | ||
| END { if (body) flush() } | ||
| ' | ||
| return 0 | ||
| fi | ||
| grep -vE "^[[:space:]]*#.*(${base}/(v[0-9]+-)?stable|${base} stable channel)" || true | ||
|
don-petry marked this conversation as resolved.
|
||
| } | ||
|
|
||
| # Workflows an adopter may or may not have (BMAD idea pipeline). The sweep never | ||
| # seeds them; it only migrates an existing caller, re-pinning its OWN body in place. | ||
| readonly PRESENT_ONLY_WORKFLOWS=( | ||
| idea-enhancer.yml | ||
| idea-triage.yml | ||
| initiative-planner.yml | ||
| ) | ||
|
|
||
| is_present_only_workflow() { | ||
| local w="$1" p | ||
| for p in "${PRESENT_ONLY_WORKFLOWS[@]}"; do | ||
| [[ "$w" == "$p" ]] && return 0 | ||
| done | ||
| return 1 | ||
| } | ||
|
|
||
| # is_already_compliant <existing_content> <template> <repo> -> 0 if the deployed | ||
| # stub needs no re-deploy. A stub is compliant only when it is BOTH pin-compliant | ||
| # (is_pin_compliant, below) AND — when its template carries a real `secrets: inherit` | ||
|
|
@@ -615,7 +702,8 @@ merge_guarded_surfaces() { | |
| # rendered at the repo's tier channel (stub_body_matches_template): its callers | ||
| # predate the template and carry per-repo edits, which are drift. So must every stub | ||
| # in a FULL_BODY_COMPARE_REPOS repo (repo-template, #1285) other than a | ||
| # BODY_PRESERVING_WORKFLOWS one, whose per-repo body is never re-synced. | ||
| # BODY_PRESERVING_WORKFLOWS or PRESENT_ONLY_WORKFLOWS one, whose per-repo body is | ||
| # re-pinned in place and never re-synced from the template. | ||
| # | ||
| # A pin-correct stub whose guarded `on:` / `permissions:` / `concurrency:` surface | ||
| # differs from the template is ALSO drift (#1236): the audit flags it | ||
|
|
@@ -634,8 +722,10 @@ is_already_compliant() { | |
| && ! stub_has_s7635_marker <<< "$existing_content"; then | ||
| return 1 | ||
| fi | ||
| stub_has_stale_stable_header "$existing_content" "$template" "$repo" && return 1 | ||
| if is_replace_if_present_workflow "$workflow" \ | ||
| || { is_full_body_compare_repo "$repo" && ! is_body_preserving_workflow "$workflow"; }; then | ||
| || { is_full_body_compare_repo "$repo" && ! is_body_preserving_workflow "$workflow" \ | ||
| && ! is_present_only_workflow "$workflow"; }; then | ||
| stub_body_matches_template "$existing_content" "$template" "$repo" || return 1 | ||
| fi | ||
| if ! is_skipped_repo "$repo" && ! is_body_preserving_workflow "$workflow" \ | ||
|
|
@@ -887,6 +977,17 @@ deploy_repo() { | |
| repin_source="$(mktemp)"; _TMPFILES+=("$repin_source") | ||
| printf '%s\n' "$existing_content" > "$repin_source" | ||
| fi | ||
| elif is_present_only_workflow "$workflow"; then | ||
| # Optional pipeline stub: never seed; otherwise re-pin its OWN body in place. | ||
| if [[ -z "$existing_sha" ]]; then | ||
| skip "$repo/$workflow (present-only: no existing caller — never seeded)" | ||
| continue | ||
| fi | ||
| mode="repin-in-place" | ||
|
don-petry marked this conversation as resolved.
|
||
| if [[ "$DRY_RUN" != "true" ]]; then | ||
| repin_source="$(mktemp)"; _TMPFILES+=("$repin_source") | ||
| printf '%s\n' "$existing_content" > "$repin_source" | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| fi | ||
| elif is_body_preserving_workflow "$workflow"; then | ||
| # feature-ideation carries a per-repo project_context the sweep must never | ||
| # clobber. Seed the generic template ONLY when the stub is absent; when it | ||
|
|
@@ -928,6 +1029,25 @@ deploy_repo() { | |
| fi | ||
| fi | ||
|
|
||
| # Stale stable-channel header (#1297): migrate by editing the stub's OWN body, never | ||
| # by redeploying the template (that would reset per-repo edits such as | ||
| # pr-auto-review's workflow_run.workflows). Full-body-compared stubs are exempt — | ||
| # their drift is already resolved by a template redeploy. | ||
| if [[ "$DRY_RUN" != "true" && -n "$existing_sha" ]] \ | ||
| && stub_has_stale_stable_header "$existing_content" "$template" "$repo"; then | ||
| base="$(reusable_base_of "$template")" | ||
| if [[ "$repin_source" == "$template" ]] && ! is_replace_if_present_workflow "$workflow" \ | ||
| && ! { is_full_body_compare_repo "$repo" && ! is_body_preserving_workflow "$workflow"; } \ | ||
| && is_pin_compliant "$existing_content" "$template" "$repo"; then | ||
| repin_source="$(mktemp)"; _TMPFILES+=("$repin_source") | ||
| printf '%s\n' "$existing_content" > "$repin_source" | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| fi | ||
| if [[ "$repin_source" != "$template" ]]; then | ||
| strip_stale_stable_header "$base" "$template" < "$repin_source" > "$repin_source.stripped" | ||
| mv "$repin_source.stripped" "$repin_source" | ||
| fi | ||
| fi | ||
|
|
||
| # When repin_source is a copy of existing_content (meta-repo consumer or body- | ||
| # preserving repin-in-place) and the template requires the S7635 marker but the | ||
| # copy lacks it, inject the marker now. Without this, the deployed stub would | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.