Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 29 additions & 19 deletions scripts/compliance-audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1563,31 +1563,36 @@ check_centralized_workflow_stubs() {
decoded=$(echo "$content" | base64 -d 2>/dev/null || echo "")
[ -z "$decoded" ] && continue

# Compliant if a non-comment `uses:` line pins the reusable at the canonical
# channel or — transitionally, during the #482 migration — an accepted legacy
# @vN ref. stub_pin_acceptable anchors to start-of-line so a `# uses: …`
# comment never satisfies the check. For RING reusables, also accept the
# major-scoped `<name>/v<M>-<tier>` form (major-scoped-channels epic #657 F3)
# for any major M whose tier matches the repo — backward-compatible with the
# bare-tier pins the fleet still carries; the wrong tier stays drift.
if stub_pin_acceptable "$decoded" "$reusable" "$canonical" "$legacy" \
|| { [ "$is_ring" = 1 ] && ring_major_form_acceptable "$decoded" "$reusable" "$chan" "$repo"; }; then
# Compliance rule (major-scoped-channels epic #657 F5, #861):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Audit/deploy pin mismatch 🐞 Bug ≡ Correctness

check_centralized_workflow_stubs() now accepts only major-scoped v<M>-<tier> pins for RING stubs,
so a bare <agent>/<tier> pin is always flagged as drift. deploy-standard-workflows.sh still treats
bare-tier pins as compliant when ring_host_current_major() finds no releases, so the deploy sweep
may never remediate the audit finding for pre-release agents.
Agent Prompt
### Issue description
`scripts/compliance-audit.sh` now *unconditionally* flags bare `<agent>/<tier>` pins for RING reusables by removing the `stub_pin_acceptable` path for RING entries and requiring `ring_major_form_acceptable`. However, `scripts/deploy-standard-workflows.sh` explicitly keeps bare-tier stubs compliant when the reusable has **no release** (no current major), meaning the deploy sweep will not converge the fleet with the audit.

### Issue Context
- The shared ring model (`scripts/lib/ring-pins.sh`) defines `ring_host_current_major()` which returns empty when there are no release tags, and callers should fall back to bare-tier pins.
- Deploy logic already implements and tests this no-release grace.

### Fix Focus Areas
- scripts/compliance-audit.sh[1566-1576]

### Implementation guidance
Update `check_centralized_workflow_stubs()` so RING pin compliance is **release-aware**, matching deploy behavior:
1. Determine the current major for the reusable (via `ring_host_current_major` or an equivalent gh_api-based helper, ideally cached per agent for performance).
2. If there is **no current major** (empty), allow bare-tier pins (e.g., via `stub_pin_acceptable` using the computed bare canonical + legacy) the same way deploy considers them compliant.
3. If there **is** a current major, require the `v<M>-<tier>` form (current behavior).

Add/adjust integration tests to cover the no-release audit behavior (mirroring `emit-vform.bats`’s `unset GH_RELEASE_REFS` scenario) so audit+deploy cannot regress into disagreement again.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

# RING reusables MUST pin the major-scoped `<name>/v<M>-<tier>` form — any
# major M whose tier matches the repo (ring_major_form_acceptable). A bare
# `<name>/<tier>` pin is now drift: the fleet has migrated onto the v-form,
# so the bare grace (stub_pin_acceptable) no longer applies to the rings.
# Non-RING reusables (fixed-pin entries) keep the exact/legacy match via
# stub_pin_acceptable.
# Both helpers anchor to start-of-line so a `# uses: …` comment never counts;
# a v-form pinned to the WRONG tier stays drift.
if { [ "$is_ring" = 1 ] && ring_major_form_acceptable "$decoded" "$reusable" "$chan" "$repo"; } \
|| { [ "$is_ring" != 1 ] && stub_pin_acceptable "$decoded" "$reusable" "$canonical" "$legacy"; }; then
continue
fi
Comment on lines +1575 to 1578

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The new ring-only acceptance path unconditionally rejects bare <base>/<tier> pins, but deploy logic still treats bare pins as compliant when an agent has no release major yet. This creates an audit/deploy contract split where audit reports unavoidable drift that deploy will not fix. Align this check with deploy by allowing bare-tier ring pins when no current major exists for that reusable host. [api mismatch]

Severity Level: Major ⚠️
- ⚠️ Compliance audit flags unfixable drift for pre-release ring stubs.
- ⚠️ Deploy sweep leaves some pre-release stubs unrepinned.
- ⚠️ Engineers see recurring non-actionable ci-workflows findings.
Steps of Reproduction ✅
1. Run the org-wide audit script `scripts/compliance-audit.sh` (entrypoint at lines 1–40)
so that it iterates repositories and invokes `check_centralized_workflow_stubs "$repo"`
from the main loop at `scripts/compliance-audit.sh:2854-2860`.

2. In some repo `R`, ensure a centralized ring-managed stub (e.g.
`.github/workflows/auto-rebase.yml`) is pinned to a bare-tier channel such as `uses:
petry-projects/.github/.github/workflows/auto-rebase-reusable.yml@auto-rebase/stable`,
which matches the channel model wired through `centralized[]` and `ring_canonical_ref` in
`check_centralized_workflow_stubs` at `scripts/compliance-audit.sh:1490-1521`.

3. When `check_centralized_workflow_stubs` runs for `R`, it sets `is_ring=1` and
`chan="${reusable%-reusable}"`, then loads the stub into `decoded` (lines 59–65) and
evaluates the new condition at `scripts/compliance-audit.sh:1567-1577`: the RING path
calls `ring_major_form_acceptable "$decoded" "$reusable" "$chan" "$repo"`, which only
accepts `uses:` refs in major-scoped `<base>/v<M>-<tier>` form (implementation at
`scripts/compliance-audit.sh:1470-1483`), so a bare `auto-rebase/stable` pin is rejected
and the `if` falls through, producing a `non-stub-*.yml` drift finding even though the
stub matches the bare-tier ring channel.

4. Deploy the same stub via `scripts/deploy-standard-workflows.sh`: `is_already_compliant`
at `scripts/deploy-standard-workflows.sh:287-323` computes
`major="$(ring_host_current_major "$host" "$base")"` and, when the agent has no current
major (`major` empty), executes the `if [[ -z "$major" ]]` block at lines 55–62 to treat
any bare-tier ref from `ring_accepted_refs "$base" "$repo"` (defined in
`scripts/lib/ring-pins.sh:46-63`) as compliant, so the deploy sweep leaves the bare-tier
stub unchanged while the audit continues to flag it as non-compliant.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** scripts/compliance-audit.sh
**Line:** 1575:1578
**Comment:**
	*Api Mismatch: The new ring-only acceptance path unconditionally rejects bare `<base>/<tier>` pins, but deploy logic still treats bare pins as compliant when an agent has no release major yet. This creates an audit/deploy contract split where audit reports unavoidable drift that deploy will not fix. Align this check with deploy by allowing bare-tier ring pins when no current major exists for that reusable host.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎


# Determine why it's non-compliant for a more actionable message.
local esc_reusable why
# Determine why it's non-compliant for a more actionable message. For RING
# reusables the expected pin is the major-scoped v-form `<name>/v<M>-<tier>`
# (major-agnostic on tier, #657 F5); non-ring keeps the fixed canonical ref.
local esc_reusable why expected_pin="$canonical"
esc_reusable=$(escape_ere "$reusable")
[ "$is_ring" = 1 ] && expected_pin="${chan}/v<M>-$(ring_tier_for_repo "$repo")"
if echo "$decoded" | grep -qE "^[[:space:]]*uses:[[:space:]]*petry-projects/\\.github/\\.github/workflows/${esc_reusable}\\.yml@"; then
why="references the reusable but is not pinned to \`@${canonical}\` (org standard)"
why="references the reusable but is not pinned to the major-scoped channel \`@${expected_pin}\` (org standard — a bare \`@${canonical}\` tier pin is drift)"
elif echo "$decoded" | grep -qF "petry-projects/.github/.github/workflows/${reusable}"; then
why="references the reusable but the \`uses:\` line does not match the canonical stub"
else
why="is an inline copy instead of a thin caller stub — re-sync from \`standards/workflows/${wf}\`"
fi

add_finding "$repo" "ci-workflows" "non-stub-$wf" "error" \
"Centralized workflow \`$wf\` $why. Replace with the canonical stub from \`standards/workflows/${wf}\` which delegates to \`petry-projects/.github/.github/workflows/${reusable}.yml@${canonical}\`." \
"Centralized workflow \`$wf\` $why. Replace with the canonical stub from \`standards/workflows/${wf}\` which delegates to \`petry-projects/.github/.github/workflows/${reusable}.yml@${expected_pin}\`." \
"standards/ci-standards.md#centralization-tiers"
done
}
Expand Down Expand Up @@ -1627,26 +1632,31 @@ check_dev_lead_stub() {
# channel `next` and per-ring channels `ring0`, `ring1`, … A frozen `@vX.Y.Z`
# or `@<sha>` is NOT a channel and is intentionally rejected — callers pin the
# moving channel so rollout/rollback is a single central tag move.
if ! printf '%s\n' "$decoded" | grep -qE "^[[:space:]]*uses:[[:space:]]*petry-projects/\\.github-private/\\.github/workflows/dev-lead-reusable\\.yml@dev-lead/(stable|next|ring[0-9]+)([[:space:]]|$)"; then
# Major-scoped channels (#657 F5, #861): the channel MUST carry the `v<M>-`
# major prefix — a bare `dev-lead/stable` is now drift; only `dev-lead/v<M>-<tier>`
# (any major) is accepted, mirroring the RING reusables above.
if ! printf '%s\n' "$decoded" | grep -qE "^[[:space:]]*uses:[[:space:]]*petry-projects/\\.github-private/\\.github/workflows/dev-lead-reusable\\.yml@dev-lead/v[0-9]+-(stable|next|ring[0-9]+)([[:space:]]|$)"; then
add_finding "$repo" "ci-workflows" "dev-lead-stub-pin" "error" \
"The \`dev-lead.yml\` caller stub must pin a \`dev-lead\` channel tag — \`petry-projects/.github-private/.github/workflows/dev-lead-reusable.yml@dev-lead/<channel>\` where <channel> is \`stable\` (default), \`next\`, or \`ring<N>\`. Re-sync from \`standards/workflows/dev-lead.yml\`." \
"The \`dev-lead.yml\` caller stub must pin a major-scoped \`dev-lead\` channel tag — \`petry-projects/.github-private/.github/workflows/dev-lead-reusable.yml@dev-lead/v<M>-<channel>\` where <channel> is \`stable\` (default), \`next\`, or \`ring<N>\` (a bare \`dev-lead/<channel>\` tier pin is drift). Re-sync from \`standards/workflows/dev-lead.yml\`." \
"standards/ci-standards.md#dev-lead-agent"
Comment on lines +1638 to 1641

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: This check now requires dev-lead/v<M>-<channel> unconditionally, but deployment still emits/accepts bare dev-lead/<channel> when no release major exists. That makes dev-lead.yml impossible to satisfy in no-release states and creates recurring compliance failures. Gate the strict v-form requirement on detected current major (or mirror deploy’s no-major fallback). [api mismatch]

Severity Level: Major ⚠️
- ⚠️ Dev-lead stubs can fail compliance before first release.
- ⚠️ Deploy and audit disagree on dev-lead pin format.
- ⚠️ Teams face persistent dev-lead ci-workflows findings pre-release.
Steps of Reproduction ✅
1. Run `scripts/compliance-audit.sh` so that the main audit loop at
`scripts/compliance-audit.sh:2835-2860` evaluates each repository and calls
`check_dev_lead_stub "$repo"` for non-meta repos (definition at
`scripts/compliance-audit.sh:1615-1780`).

2. In a non-meta repo that has adopted dev-lead, ensure `.github/workflows/dev-lead.yml`
is pinned to the legacy bare-tier channel that deployment emits when no release major
exists, e.g. `uses:
petry-projects/.github-private/.github/workflows/dev-lead-reusable.yml@dev-lead/stable`;
this is what `emit_ref_for` in `scripts/deploy-standard-workflows.sh:103-115` produces
when `ring_host_current_major` returns an empty major for dev-lead.

3. `check_dev_lead_stub` fetches and decodes the stub (lines 124–128) and then evaluates
the new pin rule at `scripts/compliance-audit.sh:136-142`: the `grep -qE` condition only
accepts `uses:` lines of the form `@dev-lead/v[0-9]+-(stable|next|ring[0-9]+)`, so any
bare `dev-lead/<channel>` pin is rejected and `add_finding` is invoked, yielding a
`ci-workflows/dev-lead-stub-pin` error that cannot be resolved while deployment continues
to emit bare-tier pins in no-major states.

4. If a maintainer attempts to satisfy the audit by pinning `@dev-lead/vM-stable` before
any `dev-lead/vM.*` tags exist on the host (`petry-projects/.github-private`), the next
run of `scripts/deploy-standard-workflows.sh` treats that v-form as drift:
`is_already_compliant` (lines 287–323) calls `ring_host_current_major`, sees `major`
empty, and only accepts bare-tier refs from `ring_accepted_refs`, so the deploy sweep
rewrites the stub back to `dev-lead/<channel>`, causing an oscillation where dev-lead.yml
cannot simultaneously pass both deploy and audit in a no-release-major state.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** scripts/compliance-audit.sh
**Line:** 1638:1641
**Comment:**
	*Api Mismatch: This check now requires `dev-lead/v<M>-<channel>` unconditionally, but deployment still emits/accepts bare `dev-lead/<channel>` when no release major exists. That makes `dev-lead.yml` impossible to satisfy in no-release states and creates recurring compliance failures. Gate the strict v-form requirement on detected current major (or mirror deploy’s no-major fallback).

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

fi

# 2) agent_ref must be threaded through to pin the same channel inside the
# reusable's own script/prompt checkout (prevents split-brain on promotion).
# Same channel set as the uses: pin above; should match the uses: channel.
uses_channel=$(printf '%s\n' "$decoded" | sed -nE 's#^[[:space:]]*uses:[[:space:]]*petry-projects/\.github-private/\.github/workflows/dev-lead-reusable\.yml@dev-lead/(stable|next|ring[0-9]+)([[:space:]]|$).*#\1#p')
# Same major-scoped channel form as the uses: pin above; must match the uses:
# channel exactly — including its major, so a `v3-stable` uses pin with a
# `v2-stable` agent_ref is caught.
uses_channel=$(printf '%s\n' "$decoded" | sed -nE 's#^[[:space:]]*uses:[[:space:]]*petry-projects/\.github-private/\.github/workflows/dev-lead-reusable\.yml@dev-lead/(v[0-9]+-(stable|next|ring[0-9]+))([[:space:]]|$).*#\1#p')
if [ -n "$uses_channel" ]; then
if ! printf '%s\n' "$decoded" | grep -qE "^[[:space:]]*agent_ref:[[:space:]]*dev-lead/$uses_channel([[:space:]]|$)"; then
add_finding "$repo" "ci-workflows" "dev-lead-stub-agent-ref" "error" \
"The \`dev-lead.yml\` caller stub must pass \`with: agent_ref: dev-lead/$uses_channel\` to match the pinned channel \`$uses_channel\`. Re-sync from \`standards/workflows/dev-lead.yml\`." \
"standards/ci-standards.md#dev-lead-agent"
fi
else
if ! printf '%s\n' "$decoded" | grep -qE "^[[:space:]]*agent_ref:[[:space:]]*dev-lead/(stable|next|ring[0-9]+)([[:space:]]|$)"; then
if ! printf '%s\n' "$decoded" | grep -qE "^[[:space:]]*agent_ref:[[:space:]]*dev-lead/v[0-9]+-(stable|next|ring[0-9]+)([[:space:]]|$)"; then
add_finding "$repo" "ci-workflows" "dev-lead-stub-agent-ref" "error" \
"The \`dev-lead.yml\` caller stub must pass \`with: agent_ref: dev-lead/<channel>\` (\`stable\`, \`next\`, or \`ring<N>\`) so the reusable checks out its own scripts/prompts from the same channel. Re-sync from \`standards/workflows/dev-lead.yml\`." \
"The \`dev-lead.yml\` caller stub must pass \`with: agent_ref: dev-lead/v<M>-<channel>\` (\`stable\`, \`next\`, or \`ring<N>\`) so the reusable checks out its own scripts/prompts from the same major-scoped channel. Re-sync from \`standards/workflows/dev-lead.yml\`." \
"standards/ci-standards.md#dev-lead-agent"
fi
fi
Expand Down
31 changes: 21 additions & 10 deletions scripts/deploy-standard-workflows.sh
Original file line number Diff line number Diff line change
Expand Up @@ -302,16 +302,27 @@ is_already_compliant() {
if [[ "$prefix" =~ /([a-z0-9-]+)-reusable\.yml$ ]]; then
base="${BASH_REMATCH[1]}"
# Only treat it as ring-managed if the reusable is on the ring model AND the
# template pins a channel tag (not a frozen @vX / SHA).
if ring_is_ring_reusable "$base" && [[ "$ref_after" =~ ^${base}/(stable|next|ring[0-9]+)$ ]]; then
local ref
while IFS= read -r ref; do
[[ -n "$ref" ]] && grep -qF "${prefix}@${ref}" <<< "$existing_content" && return 0
done < <(ring_accepted_refs "$base" "$repo")
# Transition to major-scoped channels (#657 F5): a stub already pinned to the
# repo's tier-correct `v<M>-<tier>` form (any major) is compliant too, so the
# sweep accepts both the bare and the v-form during migration. A WRONG-tier
# v-form is not accepted here and stays drift.
# template pins a channel tag (not a frozen @vX / SHA). The template ref may be
# the bare `<base>/<tier>` OR the major-scoped `<base>/v<M>-<tier>` form (#657
# F5) — post-migration the templates pin the v-form, so both must enter here.
if ring_is_ring_reusable "$base" && [[ "$ref_after" =~ ^${base}/(v[0-9]+-)?(stable|next|ring[0-9]+)$ ]]; then
# Major-scoped channels (#657 F5, #861): the bare-tier grace is now
# major-AWARE. A bare `<base>/<tier>` stub stays compliant only while the
# agent has NO release (no current major line) — the pre-release fleet has
# nothing to major-scope to yet. Once the agent has a release, a bare stub is
# drift and must migrate to the tier's `v<M>-<tier>` form.
local host major
host="$(cut -d/ -f1-2 <<< "$prefix")"
major="$(ring_host_current_major "$host" "$base")"
if [[ -z "$major" ]]; then
local ref
while IFS= read -r ref; do
[[ -n "$ref" ]] && grep -qF "${prefix}@${ref}" <<< "$existing_content" && return 0
done < <(ring_accepted_refs "$base" "$repo")
Comment on lines +315 to +321

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The bare-tier grace is keyed only on major being empty, but major is also empty when ring_host_current_major fails to query tags (it returns empty on API failure). That means transient GH/API errors can incorrectly mark bare stubs as compliant and skip required re-pins. Differentiate “no releases found” from “lookup failed” (e.g., by status code/explicit sentinel) before allowing this grace path. [incorrect condition logic]

Severity Level: Major ⚠️
- ⚠️ Major-scoped migration can silently skip some stubs.
- ⚠️ Bare-tier pins may persist after releases due to API errors.
- ⚠️ Drift detection during deploy becomes sensitive to transient failures.
Steps of Reproduction ✅
1. Run `scripts/deploy-standard-workflows.sh` to perform a standards deployment sweep; for
each centralized workflow, the deploy logic calls `is_already_compliant
"$existing_content" "$template" "$repo"` defined at
`scripts/deploy-standard-workflows.sh:287-323` to decide whether the stub in a target repo
needs re-pinning.

2. For a ring-managed reusable whose template `uses:` line points at a channel ref
(matched by the `[[ "$ref_after" =~ ^${base}/(v[0-9]+-)?(stable|next|ring[0-9]+)$ ]]`
condition at `scripts/deploy-standard-workflows.sh:42-50`), control enters the ring
branch, and `host="$(cut -d/ -f1-2 <<< "$prefix")"` and `major="$(ring_host_current_major
"$host" "$base")"` are computed at lines 55–57.

3. `ring_host_current_major` is implemented in `scripts/lib/ring-pins.sh:14-29` and calls
`gh api "repos/${host}/git/matching-refs/tags/${base}/v" --jq '.[]?.ref'`; on any
GitHub/API failure (network, auth, or rate limiting) the `gh api` command exits non-zero,
the `|| { ...; return 0; }` handler runs, it prints a warning and returns success with no
output, so `major` is set to the empty string exactly as in the legitimate “no tags exist
yet” case.

4. When `major` is empty due to this lookup failure, the `if [[ -z "$major" ]]` block at
`scripts/deploy-standard-workflows.sh:58-62` executes the bare-tier grace path: it
iterates over `ring_accepted_refs "$base" "$repo"` (defined in
`scripts/lib/ring-pins.sh:46-63`) and returns 0 as soon as it finds a matching bare-tier
ref in `existing_content`, marking the stub “already compliant” and skipping the intended
re-pin to a `v<M>-<tier>` channel even though the agent actually has published releases
and should be migrated.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** scripts/deploy-standard-workflows.sh
**Line:** 315:321
**Comment:**
	*Incorrect Condition Logic: The bare-tier grace is keyed only on `major` being empty, but `major` is also empty when `ring_host_current_major` fails to query tags (it returns empty on API failure). That means transient GH/API errors can incorrectly mark bare stubs as compliant and skip required re-pins. Differentiate “no releases found” from “lookup failed” (e.g., by status code/explicit sentinel) before allowing this grace path.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

fi
# A stub already pinned to the repo's tier-correct `v<M>-<tier>` form (any
# major) is compliant regardless of release state. A WRONG-tier v-form — or a
# bare stub once the agent has a release — is not accepted here and stays drift.
local existing_ref
existing_ref=$(grep -oE "@${base}/[^[:space:]\"']+" <<< "$existing_content" | head -1)
existing_ref="${existing_ref#@}"
Expand Down
62 changes: 54 additions & 8 deletions test/scripts/compliance-audit/centralized-stub-pins.bats
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
# @vN ref — so the audit neither flags nor reverts a stub mid-migration. A SHA
# pin, a commented-out line, or the wrong reusable must NOT satisfy the check.
#
# #657 F5 (#861): the RING acceptance in check_centralized_workflow_stubs now
# REQUIRES the major-scoped v<M>-<tier> form — a bare <agent>/<tier> pin on a
# stable-tier repo is drift, and only a tier-correct v<M>-<tier> (any major) is
# accepted. The pure helpers stub_pin_acceptable (bare) / ring_major_form_acceptable
# (v-form) are unchanged and still unit-tested below; the integration section at
# the end exercises the whole-check decision through a mocked gh_api.
#
# The script is sourced in an isolated subshell (its `main` is guarded, so
# sourcing only defines functions) and the real helper is exercised directly.

Expand Down Expand Up @@ -150,16 +157,55 @@ maj_accept() {
[ "$status" -ne 0 ]
}

@test "transition: bare <tier> stays clean while v-form is added (#657 F3)" {
# The two acceptance paths together: a stable-tier repo is clean whether it
# pins the bare tier (legacy) OR the v-form of its tier, but a v-form on the
# wrong tier is drift. Mirrors the issue's audit acceptance cases.
accept " uses: $R@agent-shield/stable" "$C" "$L" # bare → clean
# ---------------------------------------------------------------------------
# #657 F5 (#861) — integration: check_centralized_workflow_stubs now REQUIRES the
# v<M>-<tier> form for RING reusables. A bare <agent>/<tier> pin on a stable-tier
# repo is drift (FLAGGED); a tier-correct v<M>-<tier> (any major) is accepted; a
# wrong-tier v-form is still drift. These exercise the whole check by sourcing the
# script and mocking gh_api so the pin decision surfaces as a FINDING, not just a
# pure-helper return.
# ---------------------------------------------------------------------------
stub_check() { # <workflow-yaml> — run the check for a stable-tier repo (markets)
FIXTURE_B64=$(printf '%s' "$1" | base64 | tr -d '\n'); export FIXTURE_B64
run bash -c '
source "$1" >/dev/null 2>&1
ORG=petry-projects
gh_api() {
case "$1" in
*/contents/.github/workflows) printf "agent-shield.yml\n" ;;
*/contents/.github/workflows/agent-shield.yml) printf "%s" "$FIXTURE_B64" ;;
esac
}
add_finding() { printf "FLAGGED:%s\n" "$3"; }
check_centralized_workflow_stubs markets
' _ "$SCRIPT"
}

@test "F5 integration: a bare <agent>/<tier> RING pin is FLAGGED on a stable repo (#861)" {
stub_check "jobs:
agent-shield:
uses: $R@agent-shield/stable
secrets: inherit"
[ "$status" -eq 0 ]
maj_accept "agent-shield-reusable" " uses: $R@agent-shield/v1-stable" "markets" # v-form → clean
echo "$output" | grep -q 'FLAGGED:non-stub-agent-shield.yml'
}

@test "F5 integration: a tier-correct v<M>-<tier> RING pin is accepted (no finding) (#861)" {
stub_check "jobs:
agent-shield:
uses: $R@agent-shield/v3-stable
secrets: inherit"
[ "$status" -eq 0 ]
maj_accept "agent-shield-reusable" " uses: $R@agent-shield/v1-ring0" "markets" # wrong tier → drift
[ "$status" -ne 0 ]
[ -z "$output" ]
}

@test "F5 integration: a wrong-tier v<M> RING pin is FLAGGED (#861)" {
stub_check "jobs:
agent-shield:
uses: $R@agent-shield/v3-ring0
secrets: inherit"
[ "$status" -eq 0 ]
echo "$output" | grep -q 'FLAGGED:non-stub-agent-shield.yml'
}

# ---------------------------------------------------------------------------
Expand Down
Loading
Loading