diff --git a/standards/canary-rings.json b/standards/canary-rings.json index 8d414bd4e..476fb267f 100644 --- a/standards/canary-rings.json +++ b/standards/canary-rings.json @@ -857,13 +857,70 @@ } } } - } - }, - "unmanaged": { + }, "pr-auto-review": { "host": "petry-projects/.github", "reusable": ".github/workflows/pr-auto-review-reusable.yml", - "reason": "Frozen-major @v2 review-dispatcher infra \u2014 not a canary-promoted agent." + "run_workflow": "PR Auto-Review \u2014 Ready Check", + "rings": [ + { + "channel": "next", + "order": 0, + "members": [ + "petry-projects/.github-private" + ] + }, + { + "channel": "ring0", + "order": 1, + "members": [ + "petry-projects/.github" + ] + }, + { + "channel": "ring1", + "order": 2, + "members": [ + "petry-projects/TalkTerm", + "petry-projects/bmad-bgreat-suite" + ] + }, + { + "channel": "stable", + "order": 3, + "members": [ + "*" + ] + } + ], + "gate": { + "_standard": "petry-projects/.github#548 \u2014 graduated dwell/sample gate over a per-candidate cumulative window. These are registry-configurable per-transition knobs; the values below are the #548 defaults.", + "baseline_window_days": 14, + "baseline_spike_cap_multiple": 3, + "benign_failure_classes": [], + "control": { + "allow_pre_existing": false + }, + "_benign_note": "benign_failure_classes (#1025 P2): per-reusable allowlist matched against a failed run's workflow name (`workflow` ERE) + failed-step signature (`step` ERE, matched via `gh run view`). Matches are excluded from cum_fail \u2014 but ONLY when the candidate's reusable is byte-identical to the prior channel (differs=0), so the allowlist can never mask a candidate-introduced regression. `control.allow_pre_existing`, or `promote --allow-pre-existing`, advances a BLOCKED+PRE_EXISTING frontier (never REGRESSION).", + "transitions": { + "next->ring0": { + "dwell_hours": 4, + "sample_fraction_permille": 250, + "sample_clamp_min": 3, + "sample_clamp_max": 15, + "waive_sample_if_no_caller": true + }, + "ring0->ring1": { + "dwell_hours": 8, + "waive_sample": true + }, + "ring1->stable": { + "dwell_hours": 12, + "sample_min": 1 + } + } + } } - } + }, + "unmanaged": {} } diff --git a/tests/canary_rollout.bats b/tests/canary_rollout.bats index da09ae53d..92c954a36 100644 --- a/tests/canary_rollout.bats +++ b/tests/canary_rollout.bats @@ -201,6 +201,18 @@ setup() { } # ── canary-rings.json SoT shape (rings + gate knobs) ────────────────────────── +@test "canary-rings.json: pr-auto-review onboarded to canary; unmanaged block emptied" { + run jq -e '.agents["pr-auto-review"].host == "petry-projects/.github"' "$RINGS" + [ "$status" -eq 0 ] + run jq -e '.agents["pr-auto-review"].run_workflow == "PR Auto-Review — Ready Check"' "$RINGS" + [ "$status" -eq 0 ] + run bash -c "jq -r '.agents[\"pr-auto-review\"].rings | sort_by(.order) | map(.channel) | join(\",\")' '$RINGS'" + [ "$output" = "next,ring0,ring1,stable" ] + # every reusable is now a managed agent — the unmanaged block holds nothing + run jq -e '(.unmanaged // {}) | length == 0' "$RINGS" + [ "$status" -eq 0 ] +} + @test "canary-rings.json: add-to-project onboarded to full ring model (#651)" { run jq -e '.agents["add-to-project"].host == "petry-projects/.github"' "$RINGS" [ "$status" -eq 0 ]