Skip to content

feat: implement issue #688 — canary #668 increment 6: SUSPECT→PRE_EXISTING auto-downgrade (decide_suspect_downgrade) — closes #668#689

Merged
don-petry merged 3 commits into
mainfrom
dev-lead/issue-688-20260712-2335
Jul 13, 2026
Merged

feat: implement issue #688 — canary #668 increment 6: SUSPECT→PRE_EXISTING auto-downgrade (decide_suspect_downgrade) — closes #668#689
don-petry merged 3 commits into
mainfrom
dev-lead/issue-688-20260712-2335

Conversation

@don-petry

@don-petry don-petry commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Closes #688

Implemented by dev-lead agent. Please review.

Summary by CodeRabbit

  • New Features

    • Added automatic evaluation for eligible SUSPECT failures, allowing them to be reclassified as PRE_EXISTING when candidate failure rates are comparable to the baseline.
    • Added baseline sample safeguards and configurable rate margins.
    • Added downgrade status, rate comparisons, and sample counts to evaluation results and blocker issue details.
    • Auto-downgraded results can proceed through promotion when permitted.
  • Bug Fixes

    • Improved blocker issue classification and messaging for downgraded failures, distinguishing them from reliability and correctness issues.

…STING auto-downgrade (decide_suspect_downgrade) — closes #668
@don-petry don-petry requested a review from a team as a code owner July 13, 2026 00:00
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@don-petry, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fe948797-e8b7-4014-8fb0-7a460edf1f02

📥 Commits

Reviewing files that changed from the base of the PR and between 255c947 and 35794ed.

📒 Files selected for processing (3)
  • scripts/canary-rollout.sh
  • scripts/lib/canary-rollout.sh
  • tests/canary_rollout.bats
📝 Walkthrough

Walkthrough

Adds opt-in SUSPECT auto-downgrade based on candidate and baseline failure rates, with thin-baseline safeguards. The resulting downgrade state flows through evaluation, promotion, blocker issue generation, and comprehensive Bats coverage.

Changes

SUSPECT auto-downgrade

Layer / File(s) Summary
Downgrade decision contract
scripts/lib/canary-rollout.sh, standards/canary-rings.json, tests/canary_rollout.bats
Adds decide_suspect_downgrade, configures the dev-lead workload-timeout class, and tests margin, sample, boundary, and default behavior.
Rate measurement and triage override
scripts/canary-rollout.sh
Identifies opted-in suspect classes, computes candidate and baseline class rates, and changes eligible SUSPECT results to PRE_EXISTING.
Command output and blocker reporting
scripts/canary-rollout.sh
Propagates downgrade fields through evaluation, promotion, and issue synchronization, and renders downgrade rate comparisons in blocker bodies.
End-to-end downgrade validation
tests/canary_rollout.bats
Tests successful and held downgrades, disabled opt-in behavior, promotion, thin baselines, and sync-issues output.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CanaryRollout
  participant DecisionHelper
  participant BlockerIssue
  CanaryRollout->>CanaryRollout: classify failure as SUSPECT
  CanaryRollout->>CanaryRollout: compute candidate and baseline rates
  CanaryRollout->>DecisionHelper: decide_suspect_downgrade
  DecisionHelper-->>CanaryRollout: DOWNGRADE or HOLD
  CanaryRollout->>BlockerIssue: render downgrade status and rate metrics
Loading

Possibly related PRs

Suggested labels: needs-human-review

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and matches the PR’s main change: SUSPECT→PRE_EXISTING auto-downgrade for canary #668.
Linked Issues check ✅ Passed The changes implement the pure downgrade helper, orchestrator wiring, registry opt-in, blocker sync, and tests described in #688.
Out of Scope Changes check ✅ Passed The summary shows only changes needed for the auto-downgrade feature and its tests; no unrelated scope is evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev-lead/issue-688-20260712-2335

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — review-changes (no-changes)

No changes were needed for this PR.

@don-petry don-petry enabled auto-merge (squash) July 13, 2026 00:01

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements a data-driven auto-downgrade mechanism (SUSPECT to PRE_EXISTING) for canary rollouts, specifically opting in the dev-lead workload-timeout failure class. It introduces new helper functions to evaluate candidate-vs-baseline failure rates, updates the orchestrator's state evaluation and blocker reporting, and adds comprehensive test coverage. The review feedback focuses on improving the robustness of the Bash scripts, including guarding against syntax errors from empty command substitutions or unassigned variables, using jq's optional operator to prevent null-indexing errors, handling trailing newlines and carriage returns during TSV parsing, and declaring local variables properly.

Comment thread scripts/canary-rollout.sh Outdated
Comment thread scripts/canary-rollout.sh Outdated
Comment thread scripts/canary-rollout.sh
Comment thread scripts/canary-rollout.sh Outdated
Comment thread scripts/canary-rollout.sh Outdated
@don-petry don-petry disabled auto-merge July 13, 2026 00:05
donpetry-bot
donpetry-bot previously approved these changes Jul 13, 2026

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: MEDIUM
Reviewed commit: 8798c1ca49445e71fcd3aaed1023dfce9e7ba4da
Review mode: triage-approved (single reviewer)

Summary

Implements #668 increment 6: data-driven SUSPECT→PRE_EXISTING auto-downgrade. Pure core (decide_suspect_downgrade) with tiny-n guard and inclusive base+margin boundary; orchestrator applies it only for opted-in suspect classes after classify_failure returns SUSPECT (failure-class variant only); registry opt-in scoped to dev-lead workload-timeout; sync-issues renders a report-only PRE_EXISTING blocker with the rate table and no needs-human. 12 new bats cover every branch incl. regression guard for un-flagged classes. All substantive CI green. The 5 unresolved gemini bot threads were each verified against the code: false positives or hardening-only; dev-lead no-changes disposition concurs.

Linked issue analysis

Closes #688 (final increment of the #668 series). All five scope items delivered:

  1. Pure coredecide_suspect_downgrade <cand> <base> <base_sample> <knobs_json> in scripts/lib/canary-rollout.sh: HOLD on thin baseline (base_sample < min_baseline_sample, checked first), DOWNGRADE when cand <= base + margin (inclusive), else HOLD. Non-numeric/absent knobs sanitized to 0. Side-effect-free; classify_failure signature untouched.
  2. Orchestrator — downgrade evaluated only when triage=SUSPECT && mix_shift="-" (correctness-variant SUSPECT excluded, per design). Candidate window (since cut) vs trailing baseline window (SOAK_WINDOW_DAYS/baseline_window_days, before cut), rates via round_div(matched*1000/executed), reusing _run_json/_run_signature/benign_match. Emits the required ::notice:: with the rate comparison.
  3. Registryauto_downgrade {min_baseline_sample:10, margin_permille:100} on dev-lead workload-timeout only; em-dash escapes preserved. Scope-guard bats asserts exactly one opted-in class org-wide.
  4. sync-issues — auto-downgraded blocker renders the PRE_EXISTING note + candidate-vs-baseline rate table, no needs-human; bats asserts the label is absent from issue ops.
  5. Tests — unit (DOWNGRADE / worse / tiny-n / boundary / min-boundary / empty-knobs) + orchestrator (downgrade, worse-stays-SUSPECT, thin-baseline, no-opt-in regression guard, --allow-pre-existing advance, sync-issues blocker). Acceptance criteria met: lib pure, shellcheck clean, bats green under Canary-rollout Tests.

Findings

No blocking findings. The 5 unresolved review threads (gemini-code-assist bot) were independently verified against the shipped code:

  1. HIGH, scripts/canary-rollout.sh:326 (empty $(jq) → arithmetic syntax error): already mitigated — the substitution carries || echo 0, and jq '[…]|length' cannot emit empty output on success. False positive.
  2. HIGH, scripts/canary-rollout.sh:767 (empty $ce/$be → unary-operator error): not reachable — _suspect_class_counts unconditionally echoes "$matched $executed" (both initialized to 0); an upstream failure aborts at the read under set -euo pipefail rather than yielding empty variables.
  3. MEDIUM (jq null-indexing on .auto_downgrade): guarded by select(.auto_downgrade != null) plus // 0 defaults, on a repo-controlled registry. Hardening-only.
  4. MEDIUM (TSV last-line/CRLF handling): the orchestrator loop already uses || [ -n "$dwf" ] and strips CRs; the inner loop consumes jq-generated output (newline-terminated, no CRs). Hardening-only.
  5. LOW (local count): references a variable that does not exist in the shipped code.

The dev-lead review-changes pass concluded no-changes for these threads; this review concurs with that disposition.

Minor non-blocking observation: the downgrade loop breaks on the first opted-in class whose rates compare no-worse, while the SUSPECT verdict itself is not attributed per-class — acceptable at current scope (exactly one opted-in class, enforced by the scope-guard test); worth revisiting if more classes opt in.

Secret scanning: the run_secret_scanning MCP tool was not available in this run; the gitleaks CI check passed. No secrets, credentials, or auth-surface changes in the diff.

CI status

All substantive checks SUCCESS at 8798c1c: ShellCheck, Lint, Agent Security Scan, Secret scan (gitleaks), Canary-rollout Tests (lint + bats, incl. the 12 new tests), CodeQL (actions), SonarCloud quality gate (0 new issues, 0 hotspots), npm audit, CodeRabbit status. Ecosystem audits (pip/cargo/go/pnpm) SKIPPED — not applicable. Two QUEUED jobs are this auto-review pipeline's own dispatch. mergeable=MERGEABLE; mergeStateStatus=BLOCKED pending this review.


Reviewed automatically by the PR-review agent (single-reviewer mode: fable 5). Reply if you need a human review.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 13, 2026
@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — fix-reviews (applied)

Changes committed and pushed.

@don-petry don-petry dismissed stale reviews from coderabbitai[bot] and donpetry-bot via 255c947 July 13, 2026 00:09
@don-petry don-petry enabled auto-merge (squash) July 13, 2026 00:09
@don-petry don-petry disabled auto-merge July 13, 2026 00:10
@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — waiting on PR blockers (intent: review-changes)

PR: #689
No changes were committed, but the PR still has blocking checks or reviews (failing or cancelled checks, or changes-requested reviews). The retry cron will re-attempt automatically. Next attempt after: 2026-07-13T00:40:39Z

@don-petry

Copy link
Copy Markdown
Contributor Author

Note

@don-petry I reviewed this PR and no code changes were needed, but it still has blocking checks or reviews (failing or cancelled checks, or changes-requested reviews), so I cannot mark it done yet. I'll re-check automatically.
Next attempt after: 2026-07-13T00:40:39Z

@don-petry don-petry enabled auto-merge (squash) July 13, 2026 00:10
@donpetry-bot

Copy link
Copy Markdown
Contributor

CI checks on this PR are still running. Once they complete, re-mention @donpetry-bot to trigger a fresh review.

Posted by the donpetry-bot PR-review cascade.

@donpetry-bot

Copy link
Copy Markdown
Contributor

@donpetry-bot I'm on it — starting a fresh review now. Results will appear in a few minutes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/canary-rollout.sh (1)

1216-1230: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove needs-human when an existing blocker is downgraded.

A blocker first created as SUSPECT retains needs-human: the update path at Line 1251 only conditionally adds labels and never removes old ones. Its body then says “no human needed,” but routing remains incorrect. Remove the label for updated PRE_EXISTING blockers and cover this state transition.

Proposed fix
-          { [ "$triage" = "REGRESSION" ] || [ "$triage" = "SUSPECT" ]; } && gh issue edit "$num" --repo "$ISSUE_REPO" --add-label needs-human >/dev/null 2>&1 || true
+          if [ "$triage" = "REGRESSION" ] || [ "$triage" = "SUSPECT" ]; then
+            gh issue edit "$num" --repo "$ISSUE_REPO" --add-label needs-human >/dev/null 2>&1 || true
+          else
+            gh issue edit "$num" --repo "$ISSUE_REPO" --remove-label needs-human >/dev/null 2>&1 || true
+          fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/canary-rollout.sh` around lines 1216 - 1230, The blocker update flow
around _issue_find and the PRE_EXISTING state must remove the existing
needs-human label when a blocker is downgraded and the body indicates no human
action is required. Update the label-handling logic near the referenced update
path to explicitly remove needs-human for this transition, while preserving
conditional label additions for other states, and add coverage for the
SUSPECT-to-downgraded PRE_EXISTING transition.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/canary-rollout.sh`:
- Around line 328-337: Update the baseline signature handling around
_run_signature and the matched-count loop so failed signature lookups are
distinguished from genuine non-matches instead of being skipped. Track
incomplete baseline class evidence when an executed baseline run cannot be read,
and propagate that state to the rollout decision so it returns HOLD rather than
calculating a potentially understated dg_base_rate or downgrading SUSPECT.
- Around line 291-303: Preserve the matched suspect failure class and run
attribution through triage instead of reducing it to the aggregate cum_suspect
flag. Update the downgrade flow around _suspect_downgrade_patterns and
decide_suspect_downgrade so a candidate is downgraded only when every blocking
failure is covered by an eligible class whose comparison passes; unrelated
failures must keep it blocked. Add a regression test covering a mixed
baseline-like timeout and unrelated failed run.

---

Outside diff comments:
In `@scripts/canary-rollout.sh`:
- Around line 1216-1230: The blocker update flow around _issue_find and the
PRE_EXISTING state must remove the existing needs-human label when a blocker is
downgraded and the body indicates no human action is required. Update the
label-handling logic near the referenced update path to explicitly remove
needs-human for this transition, while preserving conditional label additions
for other states, and add coverage for the SUSPECT-to-downgraded PRE_EXISTING
transition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 911522a9-a166-4020-8332-1243ca0c48d8

📥 Commits

Reviewing files that changed from the base of the PR and between 3e1767a and 255c947.

📒 Files selected for processing (4)
  • scripts/canary-rollout.sh
  • scripts/lib/canary-rollout.sh
  • standards/canary-rings.json
  • tests/canary_rollout.bats

Comment thread scripts/canary-rollout.sh
Comment thread scripts/canary-rollout.sh
@don-petry don-petry disabled auto-merge July 13, 2026 00:15
@donpetry-bot

donpetry-bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor
Superseded by automated re-review at 255c947e61b6afee219d4269be877dd98ae15ec0 — click to expand prior review.

Review — fix requested (cycle 1/3)

The automated review identified the following issues. Please address each one:

Findings to fix

Automated review — NEEDS HUMAN REVIEW

Risk: MEDIUM
Reviewed commit: 255c947e61b6afee219d4269be877dd98ae15ec0
Cascade: triage → deep (triage: haiku 4.5 → deep: opus 4.8 + duck: o4-mini → audit: fable 5)

Summary

PR #689 adds the #668-inc6 SUSPECT->PRE_EXISTING auto-downgrade to the canary promotion gate (4 files, +351/-8, comprehensive bats coverage, all CI green: ShellCheck, Lint, bats, SonarCloud, CodeQL, gitleaks). Gemini's null-safety concern is already resolved in the committed code (the jq uses optional ? operators plus a select(.auto_downgrade? != null) guard). However CodeRabbit posted CHANGES_REQUESTED against the CURRENT head (255c947, not stale) with a valid Major functional bug that I confirmed in the source, so this cannot be auto-approved. Downstream impact: (none). Secret scanning MCP tool not available in this environment; no secrets present in the diff (shell logic + JSON config + tests).

Findings

  • MAJOR: sync-issues UPDATE path never removes the needs-human label. A blocker first opened as SUSPECT (which adds needs-human) and later auto-downgraded to PRE_EXISTING keeps needs-human even though the rendered body says 'no human needed' -> incorrect routing. The add-label guard [ triage = REGRESSION ] || [ triage = SUSPECT ] only adds; the else/PRE_EXISTING branch must remove it. Confirmed against head 255c947. This SUSPECT->downgraded-PRE_EXISTING update transition is also untested (the bats test only exercises the fresh create path). (scripts/canary-rollout.sh:1251)
  • MAJOR: Downgrade decision is made per-suspect-class but the block-clear appears to key off the aggregate triage, so a candidate can be auto-downgraded to PRE_EXISTING (advancing with --allow-pre-existing, no human) when only ONE matched suspect class is rate-comparable while OTHER unrelated blocking failures remain. This could let a genuine regression through the human gate. Recommend requiring that every blocking failure be covered by an eligible, passing class before downgrading, and add a mixed baseline-timeout + unrelated-failure regression test (CodeRabbit inline, lines 291-303). (scripts/canary-rollout.sh:297)
  • MINOR: _suspect_class_counts skips failures whose _run_signature lookup returns empty, conflating an unreadable baseline run with a genuine non-match. When baseline evidence is incomplete this understates dg_base_rate; safer to treat incomplete baseline class evidence as HOLD rather than compute a potentially skewed baseline (CodeRabbit inline, lines 328-337). (scripts/canary-rollout.sh:328)
  • INFO: reviewDecision=CHANGES_REQUESTED and mergeStateStatus=BLOCKED from coderabbitai at the current head SHA (255c947, submitted 2026-07-13T00:15:23Z) — not a stale review against a prior commit. The gate is genuinely failing on an unresolved actionable review. (n/a)
  • INFO: Gemini medium-priority null-safety flag on .auto_downgrade access is already addressed: _suspect_downgrade_patterns uses .auto_downgrade?.min_baseline_sample? // 0 etc. plus select(.auto_downgrade? != null), and decide_suspect_downgrade sanitizes knobs. No action needed. (scripts/canary-rollout.sh:16)

Reviewed by the PR-review cascade (triage: haiku 4.5 → deep: opus 4.8 + duck: o4-mini → audit: fable 5). Reply if you need a human review.

Additional tasks

  1. Resolve all unresolved review thread comments from other reviewers
  2. Ensure all CI checks pass after your changes
  3. Rebase on the target branch if behind
  4. Do NOT modify files unrelated to the findings above

The review cascade will automatically re-review after new commits are pushed.

@donpetry-bot

donpetry-bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor
Superseded by automated re-review at 35794ed845fff1b2cc6b1b4c4e30be59d529f2b8 — click to expand prior review.

Review — fix requested (cycle 2/3)

The automated review identified the following issues. Please address each one:

Findings to fix

Automated review — NEEDS HUMAN REVIEW

Risk: MEDIUM
Reviewed commit: 255c947e61b6afee219d4269be877dd98ae15ec0
Cascade: triage → deep (triage: haiku 4.5 → deep: opus 4.8 + duck: o4-mini → audit: fable 5)

Summary

PR #689 adds the SUSPECT->PRE_EXISTING canary auto-downgrade (decide_suspect_downgrade) with strong pure-function + orchestrator bats coverage, and CI is fully green (ShellCheck, CodeQL, gitleaks, SonarCloud all pass). The triage escalation signal (gemini null-safety on .auto_downgrade) is RESOLVED: the code uses select(.auto_downgrade? != null) and .auto_downgrade?.min_baseline_sample? // 0 optional operators and has tests. However the PR is BLOCKED with an unresolved coderabbitai CHANGES_REQUESTED carrying a valid Major functional-correctness bug plus two logic concerns, so it cannot be approved. Risk is MEDIUM (non-trivial reliability-gate logic, no auth/secrets/crypto); escalating on my own findings without needing the Tier-3 security audit. Downstream impact: (none). Secret scanning MCP tool not exposed in this environment (gitleaks CI passed).

Findings

  • MAJOR: needs-human not removed on auto-downgrade. When an existing blocker first opened as SUSPECT (labelled needs-human) is later re-evaluated and auto-downgraded to PRE_EXISTING, the issue-update path only conditionally ADDS needs-human for REGRESSION/SUSPECT and never removes it for PRE_EXISTING. The rendered body then says 'no human needed' while the needs-human label persists, defeating the core purpose of the feature (auto-clearing the human). Add an else-branch removing needs-human on the downgraded transition and cover the SUSPECT->PRE_EXISTING update case. Confirmed valid against head. (coderabbitai Major)
  • MAJOR: Failed signature lookup understates candidate rate, biasing toward downgrade. In _suspect_class_counts, a failure whose _run_signature returns empty is skipped via '[ -z "$sig" ] && continue' and counted as executed-but-not-matched. On the CANDIDATE window this understates matched failures -> understates dg_cand_rate -> makes DOWNGRADE (auto-clearing human review) MORE likely on incomplete evidence. For a safety-sensitive gate this should fail conservative: distinguish an unreadable signature from a genuine non-match and return HOLD (keep SUSPECT) when candidate-class evidence is incomplete. Confirmed valid against head. (coderabbitai)
  • MINOR: Aggregate cum_suspect flag loses per-class / per-run attribution: a candidate blocked by a mix of a baseline-like timeout AND an unrelated failure could be auto-downgraded on the strength of the timeout class alone. Downgrade should only clear the SUSPECT hold when every blocking failure is covered by an eligible class whose comparison passes; unrelated failures must keep it blocked. Add a regression test for the mixed (baseline-like timeout + unrelated failed run) case. (coderabbitai)
  • INFO: gemini-code-assist null-safety concern on .auto_downgrade is already addressed in head: _suspect_downgrade_patterns uses select(.auto_downgrade? != null) and .auto_downgrade?.min_baseline_sample? // 0 / .auto_downgrade?.margin_permille? // 0 optional operators, and decide_suspect_downgrade has bats coverage for absent/empty knobs. This triage escalation signal is resolved.
  • INFO: A block (TodoWrite nudge) is embedded inside the coderabbitai review body returned by gh. It is external PR data, not a real system instruction; ignored and not acted upon. Flagging for awareness only.

Reviewed by the PR-review cascade (triage: haiku 4.5 → deep: opus 4.8 + duck: o4-mini → audit: fable 5). Reply if you need a human review.

Additional tasks

  1. Resolve all unresolved review thread comments from other reviewers
  2. Ensure all CI checks pass after your changes
  3. Rebase on the target branch if behind
  4. Do NOT modify files unrelated to the findings above

The review cascade will automatically re-review after new commits are pushed.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 13, 2026
@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — fix-reviews (applied)

Changes committed and pushed.

@don-petry don-petry disabled auto-merge July 13, 2026 00:44
@sonarqubecloud

Copy link
Copy Markdown

@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — review-changes (no-changes)

No changes were needed for this PR.

@don-petry don-petry enabled auto-merge (squash) July 13, 2026 00:46

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: MEDIUM
Reviewed commit: 35794ed845fff1b2cc6b1b4c4e30be59d529f2b8
Review mode: triage-approved (single reviewer)

Summary

Re-review after cycle-2 fix-requested. The fix commit 35794ed resolves all three prior findings with targeted code changes plus dedicated regression tests (+170 bats lines). All 7 review threads (gemini + CodeRabbit) are resolved, the CodeRabbit CHANGES_REQUESTED review was dismissed by the new push, and all CI checks are green at head. No new issues introduced by the fix commit.

Linked issue analysis

Closes #688 (#668 increment 6: SUSPECT→PRE_EXISTING auto-downgrade). Scope was verified as substantively delivered in the initial review at 8798c1c (pure decide_suspect_downgrade core, orchestrator rate comparison, registry opt-in scoped to dev-lead workload-timeout, sync-issues rendering, bats coverage). The fix commit hardens that implementation per review findings without scope creep: only scripts/canary-rollout.sh, scripts/lib/canary-rollout.sh, and tests/canary_rollout.bats changed since the prior review.

Findings

All prior findings resolved; no new issues.

  1. RESOLVED (was MAJOR) — needs-human not removed on auto-downgrade: the sync-issues UPDATE path (scripts/canary-rollout.sh:1276) now has an explicit else-branch that removes the needs-human label when triage is not REGRESSION/SUSPECT. New bats test exercises the SUSPECT→downgraded-PRE_EXISTING UPDATE transition and asserts label removal.
  2. RESOLVED (was MAJOR) — failed signature lookup understated rates, biasing toward downgrade: _run_signature now distinguishes a lookup failure (exit 1, cached via a sentinel) from a genuine empty signature; _suspect_class_counts emits an incomplete count; the orchestrator skips DOWNGRADE (keeps SUSPECT/HOLD) when incomplete>0 on either the candidate or baseline window — fail-closed on both windows, stricter than the finding required. New bats test simulates a gh run view failure and asserts the gate stays SUSPECT.
  3. RESOLVED (was MINOR) — aggregate cum_suspect lost per-failure attribution: _cumulative_health/_failure_suspect now count suspect-matched failures, and downgrade is only attempted when cum_suspect >= cum_fail (every blocking failure attributed to an opted-in suspect class); classify_failure updated accordingly. New mixed-failure bats test (workload-timeout + unrelated failure) asserts NOT downgraded.

Verified shell details of the fix: the || [ -n ... ] && continue guard has correct left-associative precedence, and the errexit-sensitive gh run view failure path is wrapped in || { }. ShellCheck passed in CI.

Secret scanning: run_secret_scanning MCP tool not available in this run; gitleaks CI check passed. No secrets, auth, or credential surfaces in the diff.

CI status

All checks green at 35794ed: ShellCheck, Lint, Agent Security Scan, Secret scan (gitleaks), Canary-rollout Tests (lint + bats incl. the new regression tests), CodeQL (actions), SonarCloud quality gate (0 new issues, 0 hotspots), npm audit, AgentShield, CodeRabbit status SUCCESS. Ecosystem audits (pip/cargo/go/pnpm) SKIPPED — not applicable. mergeable=MERGEABLE; mergeStateStatus=BLOCKED pending this review.


Reviewed automatically by the PR-review agent (single-reviewer mode: fable 5). Reply if you need a human review.

@don-petry don-petry merged commit e58bc97 into main Jul 13, 2026
23 checks passed
@don-petry don-petry deleted the dev-lead/issue-688-20260712-2335 branch July 13, 2026 00:49

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: MEDIUM
Reviewed commit: 35794ed845fff1b2cc6b1b4c4e30be59d529f2b8
Review mode: triage-approved (single reviewer)

Summary

Increment 6 of #668: data-driven SUSPECT→PRE_EXISTING auto-downgrade. Adds a pure decide_suspect_downgrade core (lib), orchestrator rate-comparison wiring gated per suspect class via a new auto_downgrade registry knob (dev-lead workload-timeout only), needs-human label removal on downgrade in sync-issues, and 15 new bats tests covering boundaries, tiny-n guard, mixed-failure attribution, fail-closed incomplete evidence, and increment-2 regression guards. All CI green, no unresolved threads, implementation matches the issue #688 spec exactly.

Linked issue analysis

Closes #688 (increment 6 of the #668 series). Every scope item is delivered: (1) pure decide_suspect_downgrade in scripts/lib/canary-rollout.sh with the tiny-n guard (HOLD when base_sample < min_baseline_sample) and inclusive no-worse threshold (cand <= base + margin); (2) orchestrator applies the downgrade only AFTER classify_failure returns SUSPECT, only for classes carrying auto_downgrade, with two extra fail-closed guards beyond the spec (all failures must be suspect-attributed, and any failed signature lookup HOLDs); (3) registry opt-in for dev-lead workload-timeout only, with a scope-guard test enforcing exactly one flagged class; (4) sync-issues renders the rate-comparison table in PRE_EXISTING blockers and removes stale needs-human labels on the update path; (5) bats cover every acceptance case including the boundary at exactly base+margin and the class-without-auto_downgrade byte-identical guard.

Findings

No blocking findings.

Verified specifically:

  • _run_signature now returns exit 1 on lookup failure (with a $'\x01' cache sentinel). All pre-existing callers remain safe under set -euo pipefail: _failure_benign/_failure_suspect are only invoked in if/&& condition contexts (set -e suppressed; empty sig falls through to the existing fail-closed return 1), _blocker_evidence is called with || true, and the new _suspect_class_counts explicitly captures the exit code to count incomplete evidence.
  • classify_failure's changed comparison ([ "$suspect" -gt 0 ]) is safe: the third arg defaults to 0, and prior 0|1 callers behave identically (3-arg signature back-compatible).
  • The '[ cmi -gt 0 ] || [ bei -gt 0 ] && continue' guard is correct shell (left-associative; continue runs iff either count is nonzero) and does not trip set -e.
  • jq filter interpolation of $before/$since uses only internally-derived ISO timestamps (tag dates from the org-controlled host repo), not untrusted input.
  • Secret scan: run_secret_scanning MCP tool unavailable in this session; gitleaks CI check is green and the diff contains only test-stub placeholders.

Non-blocking observation: in _frontier_state the downgrade loop breaks on the first class returning DOWNGRADE and re-triages the whole verdict PRE_EXISTING. If multiple suspect classes ever carry auto_downgrade and a candidate has failures across classes, a no-worse class could clear a hold that a worse class should keep. Currently unreachable — the registry has exactly one flagged class and a scope-guard bats test enforces that — but worth revisiting before flagging additional classes.

CI status

All required checks green at 35794ed: ShellCheck ✓, Lint and bats ✓, CodeQL ✓, SonarCloud quality gate ✓, Secret scan (gitleaks) ✓, Agent Security Scan ✓, AgentShield ✓, npm audit ✓ (language-specific audits skipped as not applicable). CodeRabbit state SUCCESS; its earlier changes-requested review was dismissed after the dev-lead fix-reviews cycle applied the requested changes.


Reviewed automatically by the PR-review agent (single-reviewer mode: fable 5). Reply if you need a human review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

canary #668 increment 6: SUSPECT→PRE_EXISTING auto-downgrade (decide_suspect_downgrade) — closes #668

2 participants