Repository navigation
ring_tier_for_repo is agent-agnostic but canary ring membership is per-agent — derive it from the registry #1092
Description
Summary
ring_tier_for_repo in scripts/lib/ring-pins.sh returns one ring tier per repo, for every agent. Ring membership in standards/canary-rings.json is defined per agent. The two disagree, and the function is a hardcoded case statement that cannot express the registry's shape.
This is the same class of defect as #1088 (RING_REUSABLES drifting from the registry), in the same file, and it should be fixed the same way — by deriving from the registry instead of maintaining a parallel list.
The mismatch
ring_tier_for_repo() {
case "$1" in
.github-private) printf 'next' ;;
.github) printf 'ring0' ;;
TalkTerm | bmad-bgreat-suite) printf 'ring1' ;;
*) printf 'stable' ;;
esac
}Registry ring1 membership, per agent:
apply-repo-settings: markets, TalkTerm, bmad-bgreat-suite
all 15 other agents: TalkTerm, bmad-bgreat-suite
markets is ring1 for apply-repo-settings only and stable for every other agent. A single agent-agnostic function cannot represent that, so today it returns stable for markets universally — wrong for apply-repo-settings.
Why the obvious fix is wrong
Adding markets to the ring1 case arm would move markets to ring1 for all sixteen agents, silently repinning its dev-lead, agent-shield, pr-auto-review and other stubs from the stable channel onto ring1 channels. That turns markets into a canary for the entire fleet — a behaviour change nobody requested, affecting a repo that is currently working.
Do not add markets to the case statement.
AC1 — make the tier lookup agent-aware, derived from the registry
ring_tier_for_repo takes the agent as a parameter and resolves the tier by finding the repo in that agent's rings[] in standards/canary-rings.json, falling back to the ring whose members contain "*" (today: stable).
ring_canonical_ref already has the agent in hand and simply does not pass it down:
ring_canonical_ref() {
local name="$1" repo="$2" major="${3:-}"
local tier; tier="$(ring_tier_for_repo "$repo")" # ← agent `$name` is available, unusedKeep the registry the single source of truth, exactly as #1088 did for RING_REUSABLES.
AC2 — no behaviour change for the other fifteen agents
After the change, every agent except apply-repo-settings must resolve identically to today for every repo. markets moves to ring1 only for apply-repo-settings. Assert this explicitly — it is the whole risk of the change.
AC3 — audit the call sites
Update every caller of ring_tier_for_repo to pass the agent. Any call site that genuinely has no agent in scope needs a documented decision rather than a silent default; flag it in the PR rather than guessing.
AC4 — coverage
ring_tier_for_repo apply-repo-settings markets→ring1ring_tier_for_repo dev-lead markets→stablering_tier_for_repo <any-agent> TalkTerm→ring1ring_tier_for_repo <any-agent> .github→ring0,.github-private→next- A regression test asserting the resolved tier for every (agent, repo) pair matches the registry — the analogue of RING_REUSABLES has drifted from canary-rings.json — 7 agents deploy verbatim, 2 with pins that do not exist #1088's "RING_REUSABLES equals the registry" assertion.
Why this matters now
It blocks Step A of #1045. Maintainer decision (2026-09-07): standards/canary-rings.json is authoritative, so markets is ring1 for apply-repo-settings. Until this lands, deploying the apply-repo-settings stub to markets computes @apply-repo-settings/v1-stable, which does not exist — and since #1088 the deploy correctly refuses it. TalkTerm and bmad-bgreat-suite are unaffected and can take the stub today.
Scope
scripts/lib/ring-pins.sh and its bats suite, plus call-site updates. Do not create, move or cut release tags. Do not deploy stubs. Do not edit standards/canary-rings.json — it is the source of truth being conformed to.
Filed from a manual compliance sweep. Related: #1088 (same file, same class), #1045 (the rollout this blocks), #1065 (v-scoped channel tag bootstrap).
Activity
don-petry commentedon Sep 8, 2026
Dev-Lead Implementation Plan
Issue: #1092 — ring_tier_for_repo is agent-agnostic but canary ring membership is per-agent — derive it from the registry
Scope
Make
ring_tier_for_repoagent-aware by resolving a repo's tier from that agent'srings[]instandards/canary-rings.json(single source of truth), and pass the agent at every call site — somarketsbecomesring1only forapply-repo-settingsand every other agent is byte-identical to today.Implementation Checklist
scripts/lib/ring-pins.sh— changering_tier_for_reposignature to<agent> <repo>; derive tier fromcanary-rings.json(expanding$host/$org_inframember tokens, falling back to the*ring). Add a registry-path resolver (honoringCANARY_RINGS, defaulting relative to the lib).scripts/lib/ring-pins.sh— update the two internal callers to pass the agent:ring_canonical_ref(has$name) andring_vform_tier_aligned(has$base).scripts/compliance-audit.sh:1680— pass$chan(agent) to the message-building call.scripts/pinned-version-report.sh— move the tier lookup into the per-agent loop and pass$agent.ring_tier_for_repoto describe the registry-derived, agent-aware behavior.Tests to Write (TDD — before implementation)
test/scripts/lib/ring-pins.bats— rewrite the "maps each tier" test to be agent-aware.apply-repo-settings markets→ring1;dev-lead markets→stable;<any-agent> TalkTerm→ring1;<any-agent> .github→ring0,.github-private→next.apply-repo-settingsresolves identically to the old hardcoded logic for a representative repo set (the whole risk of the change).test/scripts/compliance-audit/centralized-stub-pins.bats— update thering_tier_for_repocall-site tests to pass an agent, incl. theapply-repo-settings markets→ring1case.Test Command
./node_modules/.bin/bats test/scripts/lib/ring-pins.bats test/scripts/compliance-audit/centralized-stub-pins.bats(plusbash .dev-lead/scripts/dev-lead-lint.sh)don-petry commentedon Sep 8, 2026
Dev-Lead: Progress (in progress — not yet durable)
Approach (intended)
ring_tier_for_reponow takes the agent as its first argument and resolves thetier from that agent's
rings[]instandards/canary-rings.json(expanding the$host/$org_inframember tokens, matching the repo by basename, falling back tothe ring whose members contain
"*"). The registry stays the single source oftruth —
standards/canary-rings.jsonwas not edited.AC3 — call-site audit (all callers had the agent in scope; none needed a defaulted decision)
scripts/lib/ring-pins.sh—ring_canonical_refpasses$name;ring_vform_tier_alignedpasses$base.scripts/compliance-audit.sh— the drift-message builder passes$chan(the reusable basename = agent).scripts/pinned-version-report.sh— the tier lookup moved into the per-agent loop and passes$agent(was computed once per repo, which is the exact per-agent bug).Files touched (this workspace, not yet pushed)
scripts/lib/ring-pins.sh: agent-aware, registry-derivedring_tier_for_repo+RING_PINS_REGISTRYpath resolver; two internal callers updated.scripts/compliance-audit.sh: pass agent at the one call site.scripts/pinned-version-report.sh: per-(agent,repo) tier resolution.test/scripts/lib/ring-pins.bats: AC2 no-behaviour-change oracle, AC4 point cases + full registry-conformance regression, end-to-endring_canonical_refcases.test/scripts/compliance-audit/centralized-stub-pins.bats: tier tests made agent-aware incl. the apply-repo-settings/markets case.Local check output (provisional — not the pushed run)
Notes
marketsto a shared case arm) was avoided — the registry expresses thatmarketsis ring1 forapply-repo-settingsonly; every other agent is byte-identical to today (asserted by the AC2 oracle test).ring_tier_for_repofails open tostable(the*-ring tier) on an unknown agent or unreadable registry, consistent with the existing fail-open helpers in this lib and the documented wildcard fallback.canary-rings.jsonuntouched.don-petry commentedon Sep 8, 2026
Dev-Lead: Implementation Complete — PR #1096
The implementation for issue #1092 is durable: commits are pushed and a pull request is open. This record is backed by verifiable artifacts you can confirm in one click.
1a2811d4bf3209a9e1fb195921f896cd30f31590Review happens on the PR. Acceptance criteria and test results are evidenced by the PR's diff and CI checks — not asserted here. If a later stage fails, this claim is retracted in place.
feat: implement issue #1092 — ring_tier_for_repo is agent-agnostic bu…