Skip to content

feat(sync): gate fleet fan-out on consumer canaries - #2886

Merged
stranske merged 5 commits into
mainfrom
codex/issue-2879-canary-gated-sync
Aug 1, 2026
Merged

feat(sync): gate fleet fan-out on consumer canaries#2886
stranske merged 5 commits into
mainfrom
codex/issue-2879-canary-gated-sync

Conversation

@stranske

@stranske stranske commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Source: Issue #2879

Closes #2879

Automated Status Summary

Scope

maint-68-sync-consumer-repos.yml:103-128 compiles one plan and immediately builds the full registered-repo matrix. Health 69 produces a typed, non-authorizing shadow handoff (.github/workflows/health-69-consumer-sync-shadow-evidence.yml:48-88), but no workflow uses representative consumer results as a promotion gate before the remaining fleet receives PRs. During the setup-python v7 delivery, source defects discovered in consumer review caused four 12-repo waves. This is a current sequencing defect: fleet PRs are used as the first compatibility environment.

Tasks

  • Add config/consumer_sync_canaries.json defining 2-3 representative repos and documented capability tags such as lock-heavy, custom-gate, and standard consumer.
  • Add scripts/select_consumer_sync_phase.py to consume the compiled plan, requested phase, registered repos, and prior canary evidence and return an affected repo matrix.
  • Extend .github/workflows/maint-68-sync-consumer-repos.yml with preview, canary, and promote inputs; an unfiltered normal run must start at canary.
  • Reuse scripts/build_consumer_sync_shadow_handoff.py and Health 69 artifacts to record plan ID, per-repo desired hash, affected paths, and non-authorizing preview evidence.
  • Emit a machine-readable canary result from Maint 71 containing plan ID, repo, PR, required-check state, and active-review-thread count.
  • Add a promotion guard in Maint 68 that rejects missing, failed, stale, or mixed-plan canary evidence before constructing the remaining fleet matrix.
  • Add tests/scripts/test_select_consumer_sync_phase.py and extend tests/workflows/test_sync_manifest_delivery.py for phase/matrix contracts.
  • Update docs/ops/CONSUMER_REPO_MAINTENANCE.md and docs/WORKFLOW_GUIDE.md with the no-sleep, two-run promotion sequence and emergency override.

Acceptance criteria

  • python -m pytest tests/scripts/test_select_consumer_sync_phase.py tests/workflows/test_sync_manifest_delivery.py tests/scripts/test_consumer_sync_shadow_handoff.py -q passes.
  • A default no-filter Maint 68 run at a new plan ID creates PRs only for configured canaries and writes prospective-diff evidence for every affected repo.
  • promote rejects canary evidence with a different plan ID, a failed required check, or active non-outdated review debt.
  • After all canaries are green and review-clear, promote targets only affected non-canary repos; unchanged/skipped repos never enter the matrix.
  • Deliberate-break gate: alter one canary result fixture to an older plan ID; tests/scripts/test_select_consumer_sync_phase.py::test_promotion_rejects_stale_canary_evidence must fail the promotion decision. Restore the fixture before review.

Summary by CodeRabbit

  • New Features

    • Added phased consumer synchronization with preview, canary, and promote stages.
    • Added plan-bound promotion validation using required checks, review status, and canary evidence.
    • Added configurable canary targeting, affected-repository selection, and audited emergency promotion support.
    • Added synchronization metadata, evidence reporting, and trusted pull request handling.
  • Bug Fixes

    • Improved structured response handling for automated verification.
    • Updated branch protection checks to support non-strict enforcement.
  • Documentation

    • Updated workflow and maintenance guides with phased synchronization procedures and promotion requirements.
  • Tests

    • Added coverage for phase selection, gating, evidence, trust validation, and structured responses.

@stranske stranske added agent:codex Agent-created issues from Codex agents:keepalive Use to initiate keepalive functionality with agents autofix Opt-in automated formatting & lint remediation labels Aug 1, 2026
Copilot AI review requested due to automatic review settings August 1, 2026 17:10
@stranske stranske added agent:retry Add to trigger agent retry after rate limit or pause agent:codex Agent-created issues from Codex agents:keepalive Use to initiate keepalive functionality with agents autofix Opt-in automated formatting & lint remediation labels Aug 1, 2026
@cursor

cursor Bot commented Aug 1, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 44 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a61f8312-fc9f-4643-aa61-2cbe0b228434

📥 Commits

Reviewing files that changed from the base of the PR and between 893eadf and aa42a99.

📒 Files selected for processing (10)
  • .github/scripts/__tests__/sync-pr-merge-contract.test.js
  • .github/scripts/sync_pr_merge_contract.js
  • .github/workflows/maint-68-sync-consumer-repos.yml
  • .github/workflows/maint-71-merge-sync-prs.yml
  • config/consumer_sync_canaries.json
  • docs/WORKFLOW_GUIDE.md
  • docs/ops/CONSUMER_REPO_MAINTENANCE.md
  • scripts/select_consumer_sync_phase.py
  • tests/scripts/test_select_consumer_sync_phase.py
  • tests/workflows/test_sync_manifest_delivery.py
📝 Walkthrough

Walkthrough

Maint 68 now supports plan-bound preview, canary, and promote phases. Maint 71 publishes canary evidence with check and review-thread status. The PR also updates branch protection enforcement and structured response parsing.

Changes

Consumer synchronization phases

Layer / File(s) Summary
Phase selection and promotion validation
scripts/select_consumer_sync_phase.py, config/consumer_sync_canaries.json, tests/scripts/test_select_consumer_sync_phase.py
Validates plans, canary configuration, repository subsets, and promotion evidence. Returns phase-specific selections and prospective diffs.
Plan-bound Maint 68 fan-out
.github/workflows/maint-68-sync-consumer-repos.yml, tests/workflows/test_sync_manifest_delivery.py
Adds phase inputs, phase-specific matrices, preview gating, plan outputs, combined artifacts, and plan and phase metadata.
Trusted PR processing and canary evidence
.github/workflows/maint-71-merge-sync-prs.yml, .github/scripts/sync_pr_merge_contract.js, .github/scripts/__tests__/sync-pr-merge-contract.test.js
Restricts sync PRs to trusted actors and publishes plan-bound evidence with required-check and review-thread status.
Workflow contracts and operating procedure
docs/WORKFLOW_GUIDE.md, docs/ops/CONSUMER_REPO_MAINTENANCE.md
Documents preview, canary, and promote execution, evidence requirements, affected-repository targeting, and emergency promotion.

Branch protection policy controls

Layer / File(s) Summary
Branch protection enforcement and validation
tools/enforce_gate_branch_protection.py, .github/config/required-contexts.json, .github/workflows/health-40-repo-selfcheck.yml, .github/workflows/health-44-gate-branch-protection.yml, tests/tools/test_enforce_gate_branch_protection.py
Uses the summary context and supports optional non-strict branch protection with conflicting-flag validation.

Structured verifier responses

Layer / File(s) Summary
Provider response normalization
scripts/langchain/pr_verifier.py, tests/scripts/test_pr_verifier_structured_output.py
Normalizes list and non-string provider content, preserves normalized results, and handles schema-repair responses.

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

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant Maint68
  participant PhaseSelector
  participant Maint71
  participant ConsumerRepos
  Operator->>Maint68: dispatch preview or canary phase
  Maint68->>PhaseSelector: compile plan and select repositories
  PhaseSelector-->>Maint68: return plan-bound selection
  Maint68->>ConsumerRepos: create selected sync PRs
  Maint71->>ConsumerRepos: inspect checks and review threads
  Maint71-->>Operator: publish canary evidence
  Operator->>Maint68: dispatch promote with canary evidence
  Maint68->>PhaseSelector: validate matching plan and evidence
  PhaseSelector-->>Maint68: return non-canary selection
  Maint68->>ConsumerRepos: create remaining sync PRs
Loading

Possibly related issues

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The pull request also changes branch-protection defaults, LLM response parsing, and removes testgen_gate.py, which are unrelated to [#2879]. Remove unrelated branch-protection, LLM parsing, and testgen_gate.py changes, or link issues that explicitly require them.
Docstring Coverage ⚠️ Warning Docstring coverage is 18.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: gating consumer synchronization fan-out on canary validation.
Linked Issues check ✅ Passed The changes implement the phase flow, representative canaries, plan-bound evidence gates, affected-repository selection, tests, and documentation required by [#2879].
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/issue-2879-canary-gated-sync

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

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Runner dispatch state for codex on PR #2886. Do not edit.

Comment thread .github/workflows/maint-68-sync-consumer-repos.yml Fixed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7610a0cece

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/maint-68-sync-consumer-repos.yml
Comment thread scripts/select_consumer_sync_phase.py

Copilot AI 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.

Pull request overview

Adds a phase-aware, plan-bound gating mechanism to consumer sync fan-out so Maint 68 defaults to canary-only PR creation and requires explicit, plan-matching canary evidence (from Maint 71) before promoting to the rest of the fleet.

Changes:

  • Introduces preview / canary / promote phases and a canary config to gate Maint 68’s repo matrix selection.
  • Adds scripts/select_consumer_sync_phase.py plus tests to enforce plan-bound promotion and canary evidence requirements.
  • Extends Maint 71 to emit machine-readable canary evidence (including required-check state and review-thread debt), and updates ops/docs to describe the new two-run flow.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/workflows/test_sync_manifest_delivery.py Adds workflow-level contract tests for phase defaults/options, gating behavior, and presence of canary config + evidence fields.
tests/scripts/test_select_consumer_sync_phase.py Adds unit coverage for phase selection behavior and promotion rejection conditions.
scripts/select_consumer_sync_phase.py Implements the phase selector that produces the repo matrix plus auditable selection/prospective evidence.
docs/WORKFLOW_GUIDE.md Updates workflow catalog entries to document canary-gated sync + canary evidence emission.
docs/ops/CONSUMER_REPO_MAINTENANCE.md Documents the operational two-run sequence (Maint 68 canary → Maint 71 evidence → Maint 68 promote).
config/consumer_sync_canaries.json Adds the canonical 2–3 repo canary set and capability tags.
.github/workflows/maint-71-merge-sync-prs.yml Emits sync-canary-evidence.json (plan-bound) including required-check state + active review thread count.
.github/workflows/maint-68-sync-consumer-repos.yml Adds phase inputs, uses the selector to build a phase-gated matrix, records plan/selection artifacts, and annotates PR metadata with plan/phase.
Suppressed comments (1)

scripts/select_consumer_sync_phase.py:157

  • In promote, the selector currently returns every non-canary repo from registered_repos (even if the plan results in no changes for that repo). This conflicts with the acceptance criteria in the PR description (unchanged/skipped repos should never enter the matrix). Implementing "affected-only" selection likely requires incorporating per-repo prospective-diff/shadow evidence rather than just the global plan entries/removals.
            raise PhaseSelectionError("promotion_rejected:" + ",".join(reasons))
        selected = [repo for repo in registered_repos if repo not in canary_repos]
    return {

Comment thread scripts/select_consumer_sync_phase.py Outdated
Comment thread docs/ops/CONSUMER_REPO_MAINTENANCE.md
Comment thread .github/workflows/maint-71-merge-sync-prs.yml
@stranske

stranske commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Runner dispatch state for autofix on PR #2886. Do not edit.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Autofix updated these files:

  • tests/workflows/test_sync_manifest_delivery.py

@agents-workflows-bot
agents-workflows-bot Bot temporarily deployed to agent-high-privilege August 1, 2026 17:19 Inactive

@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: 6

Caution

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

⚠️ Outside diff range comments (2)
tests/scripts/test_select_consumer_sync_phase.py (1)

1-106: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Fix Black formatting to unblock CI.

The Selftest CI pipeline reports: "Black formatting check failed... Run 'black --line-length 100 tests/scripts/test_select_consumer_sync_phase.py' to format this file." Run Black on this file before merge.

🤖 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 `@tests/scripts/test_select_consumer_sync_phase.py` around lines 1 - 106, Run
Black with a line length of 100 on the test file containing the select_phase
tests, ensuring formatting passes CI without changing test behavior.

Source: Pipeline failures

.github/workflows/maint-68-sync-consumer-repos.yml (1)

1-1: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Stale artifact name break and missing regression coverage share one root cause. The "prepare" job's upload-artifact step was renamed to sync-plan-and-prospective-diffs, but the "sync" job's own download step was not updated to match, and no test asserts producer/consumer artifact-name consistency within the workflow.

  • .github/workflows/maint-68-sync-consumer-repos.yml#L152-159: update the "sync" job's "Download manifest" step (currently name: sync-manifest, shown at lines 280-283) to name: sync-plan-and-prospective-diffs so it matches the renamed upload.
  • tests/workflows/test_sync_manifest_delivery.py#L212-230: add an assertion that parses jobs.sync.steps' download-artifact with.name and confirms it equals jobs.prepare.steps' upload-artifact with.name, so a future rename desync fails CI immediately.
🤖 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 @.github/workflows/maint-68-sync-consumer-repos.yml at line 1, Update the
sync job’s “Download manifest” step to use the artifact name produced by the
prepare job’s renamed upload step, `sync-plan-and-prospective-diffs`, instead of
`sync-manifest`. In `test_sync_manifest_delivery.py`, add regression coverage
that compares the sync job’s download-artifact `with.name` against the prepare
job’s upload-artifact `with.name` and fails when they differ.
🤖 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 @.github/workflows/maint-68-sync-consumer-repos.yml:
- Around line 148-150: Update the “Build phase-gated repo matrix” step to emit
the resolved phase output as phase=$phase, then remove the separate “Record
selected phase” step and its duplicate inputs.phase default expression. Preserve
the existing phase output contract consumed by the sync job and consumer PR
environment.
- Around line 152-159: Update the sync job’s artifact download step to request
the sync-plan-and-prospective-diffs artifact instead of sync-manifest, matching
the artifact name uploaded by the prepare job.
- Around line 122-146: Update the “Build phase-gated repo matrix” step to expose
inputs.repos through a REPOS_INPUT environment variable, then use "$REPOS_INPUT"
for both the non-empty check and repos assignment instead of interpolating
inputs.repos directly in the shell. Leave the constrained inputs.phase handling
unchanged.

In @.github/workflows/maint-71-merge-sync-prs.yml:
- Around line 267-300: Validate the pull request author against the configured
trusted sync-bot identity in both selectActiveSyncPr and classifySyncPrChecks.
Reject any sync/workflows-* PR whose pr.user does not match before selecting it,
merging it, or recording canary evidence, while preserving the existing behavior
for authorized sync PRs.

In `@tests/scripts/test_select_consumer_sync_phase.py`:
- Around line 58-86: Add a promotion test alongside
test_promotion_rejects_stale_canary_evidence that removes one canary row from
otherwise valid green_evidence, calls select_phase with phase="promote" and the
existing registered repositories and canaries, and asserts PhaseSelectionError
matches "missing_canary_evidence".

In `@tests/workflows/test_sync_manifest_delivery.py`:
- Around line 212-230: Extend
test_sync_fanout_is_canary_gated_and_promotion_is_plan_bound to inspect
jobs.sync.steps and jobs.prepare.steps, locate the download-artifact and
upload-artifact steps respectively, and assert the consumer’s with.name equals
the producer’s with.name. Replace reliance on the broad source substring check
with this direct artifact-name consistency assertion while preserving the
existing workflow phase checks.

---

Outside diff comments:
In @.github/workflows/maint-68-sync-consumer-repos.yml:
- Line 1: Update the sync job’s “Download manifest” step to use the artifact
name produced by the prepare job’s renamed upload step,
`sync-plan-and-prospective-diffs`, instead of `sync-manifest`. In
`test_sync_manifest_delivery.py`, add regression coverage that compares the sync
job’s download-artifact `with.name` against the prepare job’s upload-artifact
`with.name` and fails when they differ.

In `@tests/scripts/test_select_consumer_sync_phase.py`:
- Around line 1-106: Run Black with a line length of 100 on the test file
containing the select_phase tests, ensuring formatting passes CI without
changing test behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b7f2d8d9-5b45-4c9e-b1c4-cf18b4968872

📥 Commits

Reviewing files that changed from the base of the PR and between 79db012 and 7610a0c.

📒 Files selected for processing (8)
  • .github/workflows/maint-68-sync-consumer-repos.yml
  • .github/workflows/maint-71-merge-sync-prs.yml
  • config/consumer_sync_canaries.json
  • docs/WORKFLOW_GUIDE.md
  • docs/ops/CONSUMER_REPO_MAINTENANCE.md
  • scripts/select_consumer_sync_phase.py
  • tests/scripts/test_select_consumer_sync_phase.py
  • tests/workflows/test_sync_manifest_delivery.py

Comment thread .github/workflows/maint-68-sync-consumer-repos.yml
Comment thread .github/workflows/maint-68-sync-consumer-repos.yml Outdated
Comment thread .github/workflows/maint-68-sync-consumer-repos.yml
Comment thread .github/workflows/maint-71-merge-sync-prs.yml
Comment thread tests/scripts/test_select_consumer_sync_phase.py
Comment thread tests/workflows/test_sync_manifest_delivery.py

@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)
tests/scripts/test_select_consumer_sync_phase.py (1)

41-46: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert exact repository coverage for prospective diffs.

len(result["prospective_diffs"]) == len(REGISTERED) can pass when one repository is omitted and another is duplicated. Assert that the prospective-diff repository set equals REGISTERED.

Proposed test assertion
     assert len(result["prospective_diffs"]) == len(REGISTERED)
+    assert {
+        item["repo"] for item in result["prospective_diffs"]
+    } == set(REGISTERED)

As per path instructions, **/*.py files must prioritize correctness, error handling, and test coverage.

🤖 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 `@tests/scripts/test_select_consumer_sync_phase.py` around lines 41 - 46,
Strengthen the assertions in the select_phase test by verifying that the
repository set in result["prospective_diffs"] exactly matches REGISTERED, rather
than relying only on equal lengths. Retain the existing assertions and compare
repository identifiers from the prospective diffs to REGISTERED so omissions and
duplicates cannot pass.

Source: Path instructions

🤖 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 `@tests/scripts/test_select_consumer_sync_phase.py`:
- Around line 41-46: Strengthen the assertions in the select_phase test by
verifying that the repository set in result["prospective_diffs"] exactly matches
REGISTERED, rather than relying only on equal lengths. Retain the existing
assertions and compare repository identifiers from the prospective diffs to
REGISTERED so omissions and duplicates cannot pass.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6df960f3-741e-47c6-ab79-36b82fb1105b

📥 Commits

Reviewing files that changed from the base of the PR and between 7610a0c and 30f856d.

📒 Files selected for processing (2)
  • tests/scripts/test_select_consumer_sync_phase.py
  • tests/workflows/test_sync_manifest_delivery.py

@agents-workflows-bot

agents-workflows-bot Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

🤖 Bot Comment Handler

  • Agent: codex
  • Bot comments to address: 15

The agent has been assigned to this PR to address the bot review comments.

Instructions for agent

  1. Implement suggested fixes that improve the code
  2. Skip suggestions that don't apply (note why in your response)

The bot comment handler workflow has prepared context in the artifacts.

@stranske

stranske commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Resolved the current review set in 5ad595e. The Maint 68 matrix now routes manual repos through an environment variable, preserves explicit filtered manual runs while validating canaries against the full registry, emits one resolved phase output, and uses one upload/download artifact name. Promotion rejects missing or duplicate canary evidence; Maint 71 fails closed on review-thread pagination and only accepts trusted sync actors. Updated the promotion wording to match all non-canary fan-out. Validation: 17 focused pytest cases, 15 Node contract tests, Ruff check/format, YAML parsing, and git diff --check all passed.

@stranske
stranske temporarily deployed to agent-high-privilege August 1, 2026 18:29 — with GitHub Actions Inactive

@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: 5

Caution

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

⚠️ Outside diff range comments (1)
scripts/select_consumer_sync_phase.py (1)

131-166: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject repository subsets during promote.

When a manual run supplies repos, Maint 68 passes it as --selected-repos for every phase. This code then builds the promotion matrix from that subset. A green promotion can therefore sync only one non-canary repository, while the documented contract requires all registered non-canary repositories.

Reject selected_repos for promote, unless a separate audited override defines scoped promotion behavior. Add a regression test for this rejection.

Proposed fix
     canary_repos = [item["repo"] for item in canaries]
     if selected_repos is not None and not set(selected_repos) <= set(registered_repos):
         raise PhaseSelectionError("selected_repos_must_be_registered")
+    if phase == "promote" and selected_repos is not None:
+        raise PhaseSelectionError("selected_repos_not_allowed_for_promote")
     target_repos = selected_repos if selected_repos is not None else registered_repos
🤖 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/select_consumer_sync_phase.py` around lines 131 - 166, Update the
promotion branch in the phase-selection function to reject any non-None
selected_repos before constructing the promotion matrix, using the existing
PhaseSelectionError mechanism and a clear rejection reason. Preserve subset
handling for preview and canary, while ensuring promote always targets all
registered non-canary repositories. Add a regression test covering promote with
selected_repos.
🤖 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/langchain/pr_verifier.py`:
- Around line 682-692: Update the structured repair normalization around the
content-handling logic and its empty check so a list containing a valid text
block returns the concatenated text even when it is empty, while lists without
text blocks retain existing behavior; then treat the normalized empty string as
None before JSON serialization. Add a regression test covering a repair response
such as [{"type": "text", "text": ""}] and verify it is handled as empty rather
than parsed as JSON.

In `@tests/scripts/test_pr_verifier_structured_output.py`:
- Around line 86-166: Add a test alongside the existing ComparisonRunner
structured-response tests that sets response.content directly to
_valid_payload() rather than a list of content blocks, then runs
ComparisonRunner.run_single and asserts a PASS verdict plus JSON-normalized
raw_content. Ensure this exercises the fallback coercion path in the verifier
and documents the changed behavior.

In `@tests/scripts/test_select_consumer_sync_phase.py`:
- Around line 122-131: Add a test alongside
test_filtered_manual_canary_run_preserves_requested_repositories that passes an
unregistered repository in selected_repos, asserts select_phase raises
PhaseSelectionError, and retains the existing accepted-selection test unchanged.

In `@tools/enforce_gate_branch_protection.py`:
- Around line 32-39: Update the --context argument help text to document
“summary” as the default instead of “Gate / gate”, while preserving the existing
DEFAULT_CONTEXTS value and argument behavior.
- Line 821: The snapshot generation in tools/enforce_gate_branch_protection.py
must use desired_strict for both desired.strict assignments, including the
assignment near line 821, so --allow-non-strict reports strict as false. Add a
--snapshot --allow-non-strict test in
tests/tools/test_enforce_gate_branch_protection.py covering the relevant
snapshot flow and assert snapshot["desired"]["strict"] is False.

---

Outside diff comments:
In `@scripts/select_consumer_sync_phase.py`:
- Around line 131-166: Update the promotion branch in the phase-selection
function to reject any non-None selected_repos before constructing the promotion
matrix, using the existing PhaseSelectionError mechanism and a clear rejection
reason. Preserve subset handling for preview and canary, while ensuring promote
always targets all registered non-canary repositories. Add a regression test
covering promote with selected_repos.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 74e3882f-1151-445e-a12a-a2f6d82cea7e

📥 Commits

Reviewing files that changed from the base of the PR and between 30f856d and 5ad595e.

📒 Files selected for processing (16)
  • .github/config/required-contexts.json
  • .github/scripts/__tests__/sync-pr-merge-contract.test.js
  • .github/scripts/sync_pr_merge_contract.js
  • .github/workflows/health-40-repo-selfcheck.yml
  • .github/workflows/health-44-gate-branch-protection.yml
  • .github/workflows/maint-68-sync-consumer-repos.yml
  • .github/workflows/maint-71-merge-sync-prs.yml
  • docs/ops/CONSUMER_REPO_MAINTENANCE.md
  • scripts/langchain/pr_verifier.py
  • scripts/select_consumer_sync_phase.py
  • testgen_gate.py
  • tests/scripts/test_pr_verifier_structured_output.py
  • tests/scripts/test_select_consumer_sync_phase.py
  • tests/tools/test_enforce_gate_branch_protection.py
  • tests/workflows/test_sync_manifest_delivery.py
  • tools/enforce_gate_branch_protection.py
💤 Files with no reviewable changes (1)
  • testgen_gate.py

Comment thread scripts/langchain/pr_verifier.py
Comment thread tests/scripts/test_pr_verifier_structured_output.py
Comment thread tests/scripts/test_select_consumer_sync_phase.py
Comment thread tools/enforce_gate_branch_protection.py
Comment thread tools/enforce_gate_branch_protection.py
@agents-workflows-bot
agents-workflows-bot Bot temporarily deployed to agent-high-privilege August 1, 2026 18:34 Inactive
codex-automation and others added 3 commits August 1, 2026 13:42
select_phase rejects a --selected-repos set that is not a subset of the
registry, but nothing exercised that branch, so a manual filtered run could
have become a fan-out escape hatch without failing a test.

Co-authored-by: Cursor <cursoragent@cursor.com>
@stranske

stranske commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Closer review-recovery: the branch was rebased onto a stale main, which is why the review went sideways

The structural problem, first. Before this round the branch's merge-base with main was 79db0126 (the #2885 merge, 16:46:40Z) — 7 ahead, 3 behind. The 18:28:39Z rebase replayed the already-merged commits of #2889, #2888, and #2887 onto that older base, so GitHub attributed their files to this PR. The diff showed 18 files, +769/-182, including scripts/langchain/pr_verifier.py, tests/scripts/test_pr_verifier_structured_output.py, tools/enforce_gate_branch_protection.py, tests/tools/test_enforce_gate_branch_protection.py, .github/config/required-contexts.json, health-40-repo-selfcheck.yml, health-44-gate-branch-protection.yml, and the testgen_gate.py deletion — none of which this PR authored.

That is what sent CodeRabbit's 18:33Z review off-target: four of its five findings were against code that had already merged.

Rebased onto current main (2e3e5b34). Git skipped the three duplicates (skipped previously applied commit 857fabd1 / de9f8926 / 8e314abd). The PR is now 5 ahead, 0 behind, and the diff is this PR's real scope — 10 files: the two Maint workflows, the two sync-PR-merge-contract files, select_consumer_sync_phase.py and its tests, the canary config, the delivery test, and two docs.

The one in-scope finding is fixed, not waved off. CodeRabbit was right that select_phase's selected_repos_must_be_registered guard had no test: every existing case passed a valid subset, so nothing pinned the boundary. Given that this PR's entire purpose is gating fan-out, an untested registry check is exactly the guard you do not want unpinned — a manual --selected-repos run could have become a fan-out escape hatch.

aa42a994 adds test_manual_selection_cannot_target_an_unregistered_repository, mixing one unregistered repo into an otherwise valid selection and asserting the whole selection is rejected rather than silently filtered.

Deliberate-break demonstration — deleting the two-line guard from select_consumer_sync_phase.py:

E       Failed: DID NOT RAISE <class 'scripts.select_consumer_sync_phase.PhaseSelectionError'>
FAILED tests/scripts/test_select_consumer_sync_phase.py::test_manual_selection_cannot_target_an_unregistered_repository
1 failed, 8 passed

Only the new test failed. Source restored → 9 passed.

Validation: tests/scripts/test_select_consumer_sync_phase.py 9 passed; tests/workflows/test_sync_manifest_delivery.py 9 passed; node --test .github/scripts/__tests__/sync-pr-merge-contract.test.js 15 pass / 0 fail; ruff check and ruff format --check clean; git diff --check clean.

The four out-of-scope findings were not discarded. They are real defects in merged main code, and dropping them off this diff would have buried them. They are fixed in follow-up #2890:

Finding Origin Disposition in #2890
pr_verifier.py:692 — textless repair reply treated as a repair #2887 Fixed; the repair path now returns None for no-text-block or blank replies while the parse path keeps its json.dumps fallback
test_pr_verifier_structured_output.py:166 — no coverage for the JSON fallback #2887 Test added
enforce_gate_branch_protection.py:821 — snapshot serializes strict: True under --allow-non-strict #2888 Fixed at both remaining sites, with a regression test
enforce_gate_branch_protection.py:39--context help still says Gate / gate #2888 Help corrected to summary

Resolving the two still-open threads here on that basis: their files are no longer in this PR's diff, and the fixes are in #2890.

@stranske
stranske merged commit 40dd4e8 into main Aug 1, 2026
51 checks passed
@stranske
stranske deleted the codex/issue-2879-canary-gated-sync branch August 1, 2026 18:51
@stranske stranske added the verify:compare Compare multiple LLM evaluations label Aug 1, 2026
@stranske
stranske temporarily deployed to agent-high-privilege August 1, 2026 18:52 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Provider Comparison Report

Provider Summary

Provider Model Verdict Confidence Summary
openai gpt-5.6-terra PASS 84% The change set addresses the required staged consumer-sync flow: it adds documented canary configuration, phase-selection logic, Maint 68 preview/canary/promote handling, promotion validation based...
anthropic claude-sonnet-5 CONCERNS 50% The PR adds the required new files (config/consumer_sync_canaries.json, scripts/select_consumer_sync_phase.py, tests/scripts/test_select_consumer_sync_phase.py) and extends maint-68 and maint-71 wo...
📋 Full Provider Details (click to expand)

openai

  • Model: gpt-5.6-terra
  • Verdict: PASS
  • Confidence: 84%
  • Scores:
    • Correctness: 9.0/10
    • Completeness: 9.0/10
    • Quality: 8.0/10
    • Testing: 8.0/10
    • Risks: 2.0/10
  • Summary: The change set addresses the required staged consumer-sync flow: it adds documented canary configuration, phase-selection logic, Maint 68 preview/canary/promote handling, promotion validation based on canary evidence, and Maint 71 machine-readable canary-result output. The phase selector and workflow contracts are covered by new/extended tests, including stale-plan promotion rejection. The documented two-run promotion procedure and emergency override are also included. The implementation appears to ensure default unfiltered runs are canary-only and promotion selects only affected non-canary repositories after complete matching green/review-clear evidence.

anthropic

  • Model: claude-sonnet-5
  • Verdict: CONCERNS
  • Confidence: 50%
  • Scores:
    • Correctness: 6.0/10
    • Completeness: 6.0/10
    • Quality: 6.0/10
    • Testing: 6.0/10
    • Risks: 6.0/10
  • Summary: The PR adds the required new files (config/consumer_sync_canaries.json, scripts/select_consumer_sync_phase.py, tests/scripts/test_select_consumer_sync_phase.py) and extends maint-68 and maint-71 workflows plus test_sync_manifest_delivery.py and documentation, aligning structurally with nearly all listed tasks. CI (pr-00-gate, pr-11-ci-smoke, selftest-ci) shows passing/success results on the merge commit, which is a positive signal for basic correctness. However, the diff provided to this verifier was truncated, preventing direct confirmation of the core promotion-gate logic (rejecting missing/failed/stale/mixed-plan canary evidence) and the specific deliberate-break test scenario mandated by the acceptance criteria. Documentation additions to WORKFLOW_GUIDE.md are minimal relative to the stated requirement. Given the criticality of the gating logic to the feature's purpose and the inability to fully verify it from the available diff, this is flagged as CONCERNS rather than an unqualified PASS, pending fuller visibility into the implementation.
  • Concerns:
    • The full diff was truncated in the verifier context, so the actual logic of select_consumer_sync_phase.py (plan-ID staleness detection, mixed-plan rejection, required-check/review-thread evaluation) cannot be directly audited from the provided material — only file-level change sizes are visible.
    • docs/WORKFLOW_GUIDE.md only received a +2/-2 line change, which appears thin for documenting 'the no-sleep, two-run promotion sequence and emergency override' called out in the task list; most of the documentation burden appears to fall on CONSUMER_REPO_MAINTENANCE.md (+20 lines), which may or may not fully cover the two-run sequence and override procedure.
    • scripts/build_consumer_sync_shadow_handoff.py is not shown as modified in the diff, so it's unclear whether the 'reuse' task for recording plan ID/desired-hash/affected-paths evidence is wired into the new workflow phases or simply invoked unchanged — this is plausible but unverifiable here.
    • The deliberate-break acceptance criterion (test_promotion_rejects_stale_canary_evidence) cannot be confirmed to exist with that exact name/behavior from the summarized diff; its presence is inferred only from the 151-line test file addition.
    • tests/scripts/test_consumer_sync_shadow_handoff.py referenced in the required pytest invocation is not part of this diff (presumably pre-existing from Health 69), so its continued passing state after this PR's changes to maint-68/71 is not independently verified here.

Agreement

  • No clear areas of agreement.

Disagreement

Dimension openai anthropic
Verdict PASS CONCERNS
Correctness 9.0/10 6.0/10
Completeness 9.0/10 6.0/10
Quality 8.0/10 6.0/10
Testing 8.0/10 6.0/10
Risks 2.0/10 6.0/10

Unique Insights

  • openai: The change set addresses the required staged consumer-sync flow: it adds documented canary configuration, phase-selection logic, Maint 68 preview/canary/promote handling, promotion validation based on canary evidence, and Maint 71 machine-readable canary-result output. The phase selector and work...
  • anthropic: The full diff was truncated in the verifier context, so the actual logic of select_consumer_sync_phase.py (plan-ID staleness detection, mixed-plan rejection, required-check/review-thread evaluation) cannot be directly audited from the provided material — only file-level change sizes are visible.; docs/WORKFLOW_GUIDE.md only received a +2/-2 line change, which appears thin for documenting 'the no-sleep, two-run promotion sequence and emergency override' called out in the task list; most of the documentation burden appears to fall on CONSUMER_REPO_MAINTENANCE.md (+20 lines), which may or may not fully cover the two-run sequence and override procedure.; scripts/build_consumer_sync_shadow_handoff.py is not shown as modified in the diff, so it's unclear whether the 'reuse' task for recording plan ID/desired-hash/affected-paths evidence is wired into the new workflow phases or simply invoked unchanged — this is plausible but unverifiable here.; The deliberate-break acceptance criterion (test_promotion_rejects_stale_canary_evidence) cannot be confirmed to exist with that exact name/behavior from the summarized diff; its presence is inferred only from the 151-line test file addition.; tests/scripts/test_consumer_sync_shadow_handoff.py referenced in the required pytest invocation is not part of this diff (presumably pre-existing from Health 69), so its continued passing state after this PR's changes to maint-68/71 is not independently verified here.

🔍 LangSmith Traces

@stranske

stranske commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Closer verifier disposition — anthropic CONCERNS audited as truncated-diff false positive

Report: Provider Comparison on merged head (2026-08-01T18:59:03Z): openai PASS 84% / anthropic CONCERNS 50%.

Anthropic claims checked against live main / squash scope (not the truncated verifier payload):

  1. "Diff truncated; cannot audit promotion-gate logic in select_consumer_sync_phase.py" — verifier tooling limitation, not a code gap. On main the selector and tests are present and complete: plan-ID staleness / mixed-plan rejection / required-check+review evaluation are covered by tests/scripts/test_select_consumer_sync_phase.py (including the unregistered-selection guard added in aa42a994). Pre-merge Gate on the merge commit was SUCCESS.

  2. "WORKFLOW_GUIDE.md only +2/-2; thin for the two-run promotion sequence" — the promotion procedure and emergency override live in docs/CONSUMER_REPO_MAINTENANCE.md (+20 in this PR) by design; WORKFLOW_GUIDE only got the cross-link. That matches the issue task split, not an incomplete doc surface.

Disposition: no bounded follow-up from this CONCERNS report. Source issue #2879 is already closed. Residual review-residue fixes remain on open follow-up #2890 (independent of this verdict).

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

Labels

agent:codex Agent-created issues from Codex agent:retry Add to trigger agent retry after rate limit or pause agents:keepalive Use to initiate keepalive functionality with agents autofix:patch autofix Opt-in automated formatting & lint remediation verify:compare Compare multiple LLM evaluations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Dependency/Sync] Gate fleet fan-out on source validation and consumer canaries

4 participants