Skip to content

feat(canary): differs-aware benign classes (version_independent) — #668 increment 1#672

Merged
don-petry merged 6 commits into
mainfrom
feat/668-version-independent-benign-classes
Jul 12, 2026
Merged

feat(canary): differs-aware benign classes (version_independent) — #668 increment 1#672
don-petry merged 6 commits into
mainfrom
feat/668-version-independent-benign-classes

Conversation

@don-petry

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

Copy link
Copy Markdown
Contributor

What

First increment of #668 (correctness-aware canary gate): resolve the differs=1 chronic-false-block by letting a benign failure class declare "version_independent": true.

Today the #548 gate disables the entire benign_failure_classes allowlist whenever the candidate changed the reusable (differs=1) — correct for classes a candidate could cause, but it means an actively-developed agent (dev-lead is differs=1 almost always) gets blocked as REGRESSION by inherently environmental failures:

How

  • _benign_patterns <agent> <differs>: at differs=1, emit only classes marked version_independent: true; at differs=0, all classes (unchanged). Every unmarked class still disables at differs=1, so the allowlist still cannot mask a candidate-introduced regression.
  • _cumulative_health takes differs (was apply_benign) and always applies the filtered allowlist; _frontier_state passes it through. Pure core (lib/canary-rollout.sh) untouched.
  • Registry:
    • dev-lead dependabot-context-dispatchversion_independent: true. Airtight: the failure mechanism (no Actions secrets in a Dependabot context) fires before any step of the candidate runs.
    • dev-lead fix-review-git-push-permission → deliberately NOT flagged: a candidate could change push behaviour, so it stays differs=0-only. (The Canary blocker: dev-lead next->ring0 (cum_fail=3, REGRESSION) #664 timeout class is likewise NOT eligible — a timeout can be a real perf regression; that's the SUSPECT-triage follow-up in the Enhancement: canary gate should assess correctness, not just run-reliability #668 design.)
    • ci-failure-analyst's benign classes emptied — they were a verbatim clone of dev-lead's (onboarding copy), inert because their workflow: "Dev-Lead Agent" regex can never match a ci-failure-analyst run.
    • _benign_note updated fleet-wide (via jq -a, em-dashes preserved) documenting the flag and the bar for using it (provable from the failure mechanism, not merely likely).

Tests

bats tests/canary_rollout.bats106/106 pass (11 new):

  • filter unit tests (differs=0 → all, differs=1 → flagged-only, default arg),
  • end-to-end gate verdicts with gh/git stubs: differs=1 + failures matching the version_independent class → PROMOTE (benign=80 excluded); differs=1 + failures matching the unflagged push class → still BLOCKED+REGRESSION,
  • registry-shape assertions.

Behaviour change surface

Zero for any agent without version_independent classes (13 of 14 agents). For dev-lead, exactly the #864 failure shape stops counting as REGRESSION at differs=1. Rollout of this engine change is just a merge — the engine runs from main, not via channel tags.

Design context: full write-up on #668.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J4z5uYVjwdyQjh2wFZxkut

Summary by CodeRabbit

  • Bug Fixes

    • Improved canary health checks to prevent candidate-related regressions from being hidden by benign-failure allowlists.
    • Preserved exclusions for failures confirmed to be independent of the candidate version.
    • Strengthened promotion and rollback operations using consistent API-based tag updates.
  • Documentation

    • Clarified canary failure-handling rules and updated agent-specific benign-failure classifications.
  • Tests

    • Expanded coverage for canary differences, failure filtering, cross-repository tag resolution, and promotion gating.

…usion (#668 increment 1)

The #548 gate disables the entire benign_failure_classes allowlist whenever
the candidate changed the reusable (differs=1). For an actively-developed
agent (dev-lead: differs=1 almost always) this chronically false-blocks
promotion on failures that are inherently environmental — #864 (Dependabot-
context startup failures held dev-lead 6 days) and #664 (exit-124 workload
timeouts needed a human override).

Fix: a benign class may declare "version_independent": true, meaning the
failure occurs before/independent of the candidate's own code and can NEVER
be candidate-caused. Such classes stay excluded from cum_fail even at
differs=1; every unmarked class still disables, preserving the invariant
that the allowlist cannot mask a candidate-introduced regression.

- scripts/canary-rollout.sh: _benign_patterns is differs-aware (jq filter);
  _cumulative_health takes differs instead of apply_benign; _frontier_state
  always applies the (filtered) allowlist.
- standards/canary-rings.json: dev-lead dependabot-context-dispatch marked
  version_independent (fails at secrets evaluation, before any candidate
  code); fix-review-git-push-permission deliberately NOT marked (a candidate
  could change push behaviour). ci-failure-analyst's cloned dev-lead classes
  removed (inert: workflow regex 'Dev-Lead Agent' can never match its runs).
  _benign_note updated fleet-wide.
- tests/canary_rollout.bats: filter unit tests + end-to-end gate verdicts
  (version_independent match at differs=1 -> PROMOTE with benign=80;
  non-flagged match at differs=1 -> still BLOCKED+REGRESSION) + registry
  shape tests. 106/106 pass.

Part of #668 (correctness-aware gate design); does NOT change behaviour for
any agent without version_independent classes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J4z5uYVjwdyQjh2wFZxkut
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Canary gating and validation

Layer / File(s) Summary
Differs-aware health gating
scripts/canary-rollout.sh, standards/canary-rings.json
Benign failure allowlists now retain only version_independent classes when the reusable differs; ring configuration documents and sets the corresponding flags.
Cross-repository resolution and mutation stubs
tests/canary_rollout.bats
Tests use gh for cross-repository tag, reusable, and host mutation operations while rejecting local Git tag and push paths.
Differs-aware regression coverage
package.json, tests/canary_rollout.bats
Adds the Bats dependency and tests allowlist filtering, null safety, promotion of version-independent failures, and blocking of candidate-specific failures.

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

Sequence Diagram(s)

sequenceDiagram
  participant FrontierState
  participant ReusableDiffers
  participant CumulativeHealth
  participant BenignPatterns
  participant GhAPI
  FrontierState->>ReusableDiffers: Determine reusable difference
  FrontierState->>CumulativeHealth: Evaluate health with differs
  CumulativeHealth->>BenignPatterns: Request filtered allowlist
  BenignPatterns-->>CumulativeHealth: Return eligible benign classes
  CumulativeHealth->>GhAPI: Read workflow failure evidence
  GhAPI-->>CumulativeHealth: Return runs and failure signatures
  CumulativeHealth-->>FrontierState: Return cumulative health status
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 clearly summarizes the main change: differs-aware benign classes with version_independent handling for canary gating.
Docstring Coverage ✅ Passed Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/668-version-independent-benign-classes

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.

@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 introduces a differs-aware benign failure class mechanism to the canary rollout process, allowing failures marked as version_independent to be excluded from the cumulative failure count even when the candidate changed the reusable workflow. The feedback suggests using the optional/try operator ? in jq to prevent potential fatal indexing errors when accessing agent gate properties, and utilizing $BATS_TEST_TMPDIR during temporary directory creation in tests to ensure proper isolation and cleanup.

Comment thread scripts/canary-rollout.sh
Comment thread tests/canary_rollout.bats Outdated
@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — review-changes (applied)

Changes committed and pushed.

@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — review-changes (applied)

Changes committed and pushed.

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

Copy link
Copy Markdown
Contributor Author

Dev-Lead — review-changes (applied)

Changes committed and pushed.

@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — review-changes (applied)

Changes committed and pushed.

@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — fix-bot-comment (no-changes)

Agent reasoning
- ✅ 0 Accepted issues  
- ✅ 0 Security Hotspots
- ✅ 0.0% Coverage on New Code
- ✅ 0.0% Duplication on New Code
**PR State:**
- All CI checks passing (20/20 completed successfully, none failed)
- No Tier 1 blockers (no failed checks, no "CHANGES_REQUESTED" reviews)
- Working tree clean
The PR introduces a differs-aware benign failure class mechanism with `version_independent` flag support. The SonarCloud analysis found no violations, security hotspots, or code quality issues in the changes. There are no actionable items from the bot comment.
**Files changed:** None (no fixes needed)

@don-petry don-petry marked this pull request as ready for review July 12, 2026 13:23
@don-petry don-petry requested a review from a team as a code owner July 12, 2026 13:23
@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.

@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — fix-bot-comment (no-changes)

Agent reasoning
Issues addressed: 0
- No actionable code issues in bot comment (usage notification only)
CI Status: All passing
Blockers: None
Files changed: None (no code fixes needed)
```
The PR is in a clean state. No code changes are required to address this comment.

@don-petry don-petry enabled auto-merge (squash) July 12, 2026 13:24

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

Caution

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

⚠️ Outside diff range comments (1)
standards/canary-rings.json (1)

66-66: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Misleading reason for a deliberately-unflagged class.

fix-review-git-push-permission is intentionally not marked version_independent: true (a candidate can change push behavior, per the test at Line 1331), yet its reason still asserts it is a "version-independent benign failure". This contradicts the design rationale and could lead a maintainer to add the flag, which would let the class mask a candidate-introduced regression at differs=1. Recommend dropping the "version-independent" wording and stating why the flag is withheld.

📝 Proposed wording fix
-            "reason": "fix-review git push denied by branch protection / missing write scope; version-independent benign failure, not a candidate regression."
+            "reason": "fix-review git push denied by branch protection / missing write scope; NOT marked version_independent because a candidate change to push behavior could legitimately cause this, so it must stay disabled at differs=1."
🤖 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 `@standards/canary-rings.json` at line 66, Update the reason for the
fix-review-git-push-permission entry to remove the incorrect
“version-independent” characterization and explicitly state that it remains
unflagged because a candidate can change push behavior and must not mask
regressions at differs=1, as covered by the existing test.
🤖 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.

Outside diff comments:
In `@standards/canary-rings.json`:
- Line 66: Update the reason for the fix-review-git-push-permission entry to
remove the incorrect “version-independent” characterization and explicitly state
that it remains unflagged because a candidate can change push behavior and must
not mask regressions at differs=1, as covered by the existing test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ac350be2-ff13-4d63-9f6b-bf6d8550a8bb

📥 Commits

Reviewing files that changed from the base of the PR and between 0dee71d and 3a50454.

⛔ Files ignored due to path filters (34)
  • node_modules/.bin/bats is excluded by !**/node_modules/**
  • node_modules/.package-lock.json is excluded by !**/node_modules/**
  • node_modules/bats/LICENSE.md is excluded by !**/node_modules/**
  • node_modules/bats/README.md is excluded by !**/node_modules/**
  • node_modules/bats/bin/bats is excluded by !**/node_modules/**
  • node_modules/bats/install.sh is excluded by !**/node_modules/**
  • node_modules/bats/lib/bats-core/common.bash is excluded by !**/node_modules/**
  • node_modules/bats/lib/bats-core/formatter.bash is excluded by !**/node_modules/**
  • node_modules/bats/lib/bats-core/preprocessing.bash is excluded by !**/node_modules/**
  • node_modules/bats/lib/bats-core/semaphore.bash is excluded by !**/node_modules/**
  • node_modules/bats/lib/bats-core/test_functions.bash is excluded by !**/node_modules/**
  • node_modules/bats/lib/bats-core/tracing.bash is excluded by !**/node_modules/**
  • node_modules/bats/lib/bats-core/validator.bash is excluded by !**/node_modules/**
  • node_modules/bats/lib/bats-core/warnings.bash is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats-exec-file is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats-exec-suite is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats-exec-test is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats-format-cat is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats-format-junit is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats-format-pretty is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats-format-tap is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats-format-tap13 is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats-gather-tests is excluded by !**/node_modules/**
  • node_modules/bats/libexec/bats-core/bats-preprocess is excluded by !**/node_modules/**
  • node_modules/bats/man/Makefile is excluded by !**/node_modules/**
  • node_modules/bats/man/README.md is excluded by !**/node_modules/**
  • node_modules/bats/man/bats.1 is excluded by !**/node_modules/**
  • node_modules/bats/man/bats.1.ronn is excluded by !**/node_modules/**
  • node_modules/bats/man/bats.7 is excluded by !**/node_modules/**
  • node_modules/bats/man/bats.7.ronn is excluded by !**/node_modules/**
  • node_modules/bats/package.json is excluded by !**/node_modules/**
  • node_modules/bats/uninstall.sh is excluded by !**/node_modules/**
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • package.json
  • scripts/canary-rollout.sh
  • standards/canary-rings.json
  • tests/canary_rollout.bats

@don-petry don-petry disabled auto-merge July 12, 2026 13:28
@sonarqubecloud

Copy link
Copy Markdown

@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — fix-bot-comment (no-changes)

Agent reasoning
Issues addressed: 0
- No issues found by quality gate
Files changed: N/A
Skipped (informational): 0
```
The PR is ready for merge.

@don-petry don-petry enabled auto-merge (squash) July 12, 2026 13:33
@don-petry don-petry disabled auto-merge July 12, 2026 14:09
@don-petry

Copy link
Copy Markdown
Contributor Author

Dev-Lead — waiting on PR blockers (intent: fix-reviews)

PR: #672
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-12T14:42:43Z

@don-petry don-petry enabled auto-merge (squash) July 12, 2026 14:12

@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: 193a654f750abfa9894e8c13ed684bf9276a4ee5
Cascade: triage → deep (triage: haiku 4.5 → deep: opus 4.8 + duck: o4-mini → audit: fable 5)

Summary

Differs-aware canary gate change: at differs=1 the benign-failure allowlist narrows to classes marked version_independent (only dev-lead's Dependabot-context class, which provably fails before candidate code runs), preserving the #548/#1025 invariant that the allowlist cannot mask a candidate-introduced regression. The two triage signals dissolve on inspection: the Gemini jq null-index finding is already fixed at head (optional operators present; verified no 'Cannot index null' crash; new null-safety tests added), and the 6000+ line diff is entirely vendored node_modules/bats. All CI gates are green (ShellCheck, CodeQL, gitleaks, SonarCloud, npm audit, AgentShield). Approving at MEDIUM. Downstream impact: (none).

Findings

  • INFO: Gemini's flagged fatal jq 'Cannot index null' risk at _benign_patterns is already resolved at head: expression is .agents[$a]?.gate?.benign_failure_classes? // [] with all optional operators. Empirically verified no crash for absent-agent, null-gate, and absent-agents-key inputs (all rc=0). Dedicated null-safety bats tests added. (scripts/canary-rollout.sh:276)
  • MAJOR: Full node_modules/bats tree (~6000 lines) is committed and node_modules is not in .gitignore, yet CI installs bats via apt-get install -y bats (lint.yml) — so the vendored dependency is unused dead weight in the repo. Recommend gitignoring node_modules and removing the vendored tree (keep package.json/package-lock.json only if a documented npm-based path exists). (node_modules/bats)
  • MINOR: tests/canary_rollout.bats (106 tests incl. the 11 new ones) is not listed in lint.yml's bats job, so it does not gate merge in CI — the new logic is only validated locally. Pre-existing gap (file predates this PR), but worth wiring in given this PR expands its coverage of a security-relevant gate. (.github/workflows/lint.yml)
  • INFO: Change loosens the canary auto-promotion gate but stays within the documented safety bar: only classes that fail before/independent of candidate code (version_independent:true) are excluded at differs=1; the fix-review push class and #664 timeout class are deliberately NOT flagged. run_secret_scanning MCP tool was not available; gitleaks CI check passed. (standards/canary-rings.json)

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.

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.

2 participants