Skip to content

ring_tier_for_repo is agent-agnostic but canary ring membership is per-agent — derive it from the registry #1092

Closed

Description

@don-petry

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, unused

Keep 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

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

  1. don-petry commented on Sep 8, 2026

    @don-petry
    ContributorAuthor

    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_repo agent-aware by resolving a repo's tier from that agent's rings[] in standards/canary-rings.json (single source of truth), and pass the agent at every call site — so markets becomes ring1 only for apply-repo-settings and every other agent is byte-identical to today.

    Implementation Checklist

    • scripts/lib/ring-pins.sh — change ring_tier_for_repo signature to <agent> <repo>; derive tier from canary-rings.json (expanding $host/$org_infra member tokens, falling back to the * ring). Add a registry-path resolver (honoring CANARY_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) and ring_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.
    • Update the doc comment on ring_tier_for_repo to 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.
    • AC4 point cases: apply-repo-settings markets→ring1; dev-lead markets→stable; <any-agent> TalkTerm→ring1; <any-agent> .github→ring0, .github-private→next.
    • AC2 oracle test: every agent except apply-repo-settings resolves identically to the old hardcoded logic for a representative repo set (the whole risk of the change).
    • AC4 regression: for every (agent, repo) pair the resolved tier matches an independent registry derivation — the analogue of RING_REUSABLES has drifted from canary-rings.json — 7 agents deploy verbatim, 2 with pins that do not exist #1088's registry-equality assertion.
    • test/scripts/compliance-audit/centralized-stub-pins.bats — update the ring_tier_for_repo call-site tests to pass an agent, incl. the apply-repo-settings markets→ring1 case.

    Test Command

    ./node_modules/.bin/bats test/scripts/lib/ring-pins.bats test/scripts/compliance-audit/centralized-stub-pins.bats (plus bash .dev-lead/scripts/dev-lead-lint.sh)

  2. don-petry commented on Sep 8, 2026

    @don-petry
    ContributorAuthor

    Dev-Lead: Progress (in progress — not yet durable)

    Provisional. Nothing is pushed yet; the automation posts the authoritative
    completion record (with PR number + head SHA) after the push. Treat the notes
    below as intent, not delivered results.

    Approach (intended)

    ring_tier_for_repo now takes the agent as its first argument and resolves the
    tier from that agent's rings[] in standards/canary-rings.json (expanding the
    $host/$org_infra member tokens, matching the repo by basename, falling back to
    the ring whose members contain "*"). The registry stays the single source of
    truth — standards/canary-rings.json was 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_ref passes $name; ring_vform_tier_aligned passes $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-derived ring_tier_for_repo + RING_PINS_REGISTRY path 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-end ring_canonical_ref cases.
    • 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)

    ring-pins.bats + centralized-stub-pins + emit-vform + resolve-and-drift: all green
    canary_rollout.bats: all green ; cron-timing.bats: all green
    bash .dev-lead/scripts/dev-lead-lint.sh: all checks passed
    

    Notes

    • The obvious-but-wrong fix (adding markets to a shared case arm) was avoided — the registry expresses that markets is ring1 for apply-repo-settings only; every other agent is byte-identical to today (asserted by the AC2 oracle test).
    • ring_tier_for_repo fails open to stable (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.
    • Scope respected: no release tags cut/moved, no stubs deployed, canary-rings.json untouched.
  3. don-petry commented on Sep 8, 2026

    @don-petry
    ContributorAuthor

    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.

    Review 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.

  4. added a commit that references this issue on Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug reportsdev-leadFor dev-lead agent pickup

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions