Skip to content
This repository was archived by the owner on May 13, 2026. It is now read-only.

ci: add merge gate - #107

Open
moomooskycow wants to merge 4 commits into
masterfrom
chore/merge-gate-standardization
Open

ci: add merge gate#107
moomooskycow wants to merge 4 commits into
masterfrom
chore/merge-gate-standardization

Conversation

@moomooskycow

@moomooskycow moomooskycow commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

Why This Matters

  • Problem: the repo needed a single deterministic merge gate, but the initial implementation still had a few reviewer-raised sharp edges around maintainability, failure readability, and runner assumptions.
  • Value: this PR leaves Moonbridge with one stable required-check surface that still preserves the real test matrix beneath it.
  • Why now: org policy is converging on one trustworthy merge gate per repo, and this PR is already the lane carrying that normalization work.
  • Issue: follow-up docs/maintenance hardening is tracked in [CI] Document merge-gate dependency maintenance contract #108.

Trade-offs / Risks

  • Value gained: simpler branch protection and clearer CI triage when the gate fails.
  • Cost / risk incurred: the workflow now contains a slightly richer inline Python reducer, which is more logic than a one-line bash check.
  • Why this is still the right trade: the reducer is now name-agnostic, explicit about passing states, and prints the blocked jobs directly, which makes the single-gate abstraction more trustworthy.
  • Reviewer watch-outs: confirm the workflow still fans in the intended jobs through needs, and that merge-gate stays green only when upstream deterministic jobs succeed or are intentionally skipped.

What Changed

This PR adds a stable merge-gate job and then tightens it so the gate is readable, name-agnostic, and explicit about what blocks merges.

Base Branch

graph TD
  A[Test matrix runs] --> B[Branch protection depends on multiple moving checks]
  B --> C[No single deterministic gate summarizes CI truth]
Loading

This PR

graph TD
  A[Test matrix runs] --> B[merge-gate consumes needs context]
  B --> C[success/skipped treated as passing]
  B --> D[blocked jobs printed clearly on failure]
  C --> E[one stable required check can guard merges]
Loading

Architecture / State Change

stateDiagram-v2
  [*] --> MultiCheckSurface
  MultiCheckSurface --> StableGate: add merge-gate job
  StableGate --> HardenedGate: name-agnostic reducer + explicit python3 + blocked-job output
  HardenedGate --> [*]
Loading

Why this is better:

  • the gate no longer depends on a specific upstream job name inside its reducer
  • failed jobs are surfaced directly in the red path, which speeds CI triage
  • python3 makes the runner dependency explicit instead of relying on an alias
Intent Reference

Intent Reference

Changes

Changes

  • .github/workflows/test.yml
    • adds merge-gate
    • documents the needs maintenance contract inline
    • reduces upstream results name-agnostically via ${{ toJSON(needs) }}
    • treats success and skipped as passing states
    • prints a focused blocked-job summary on failure
    • invokes python3 explicitly
Acceptance Criteria

Acceptance Criteria

  • Repo has a single deterministic merge-gate status suitable for branch protection.
  • The gate fails when any upstream deterministic job is not success or skipped.
  • Failure output identifies the blocked jobs directly.
  • All review threads are resolved before merge.
Alternatives Considered

Alternatives Considered

Option A — Keep multiple required checks

  • Upside: less workflow logic.
  • Downside: branch protection stays noisier and less portable across repos.
  • Why rejected: the org is standardizing on one deterministic gate.

Option B — Keep the first merge-gate draft unchanged

  • Upside: smaller diff.
  • Downside: it left maintainability and triage feedback unresolved.
  • Why rejected: reviewer concerns were legitimate and cheap to fix.

Option C — Harden the merge-gate in this PR

  • Upside: ships the normalized gate in a state reviewers can trust.
  • Downside: slightly more workflow logic plus a docs follow-up issue.
  • Why chosen: it closes the current lane cleanly without deferring obvious polish.
Manual QA

Manual QA

  • ruby -e 'require "yaml"; YAML.load_file(".github/workflows/test.yml"); puts "yaml ok"'
  • uv venv --python 3.11 .venv311 && uv pip install -e '.[dev]'
  • ruff check src/
  • mypy src/
  • pytest -v
  • merge-gate reducer smoke cases for success, skipped, failure, and mixed upstream states
Walkthrough

Walkthrough

  • Renderer: Terminal walkthrough
  • Artifact: PR discussion + CI evidence on this PR
  • Claim: Moonbridge now has a single stable merge gate whose result matches the upstream deterministic CI jobs and produces clearer failure output
  • Before / After scope: GitHub Actions workflow behavior for the test matrix and merge gate
  • Persistent verification: merge-gate plus the test (3.11/3.12/3.13) checks on this PR
  • Residual gap: merge-gate.needs still relies on documented manual maintenance; repo-level docs follow-up is tracked in [CI] Document merge-gate dependency maintenance contract #108
Before / After

Before / After

  • Before: Moonbridge lacked a normalized single merge gate, and the initial draft still needed reviewer-driven tightening around reducer coupling, error output, and Python invocation clarity.
  • After: the repo has a deterministic merge-gate with clearer failure messaging, explicit python3, and all open review threads resolved.

Screenshots are omitted because this is workflow-only work; the relevant proof is the GitHub Actions run and the workflow diff.

Test Coverage

Test Coverage

  • GitHub checks:
    • test (3.11)
    • test (3.12)
    • test (3.13)
    • merge-gate
    • Greptile / CodeRabbit / Cerberus review suite
  • Local verification:
    • YAML parse
    • ruff check src/
    • mypy src/
    • pytest -v
    • targeted reducer smoke cases
Merge Confidence

Merge Confidence

  • Confidence level: medium-high, rising to high once the final post-efc5b9c checks finish green.
  • Strongest evidence: all review threads are resolved, the workflow logic was exercised locally, and prior reruns on the same reducer shape were green.
  • Remaining uncertainty: the last push is small but still waiting on full GitHub Actions confirmation.
  • What could still go wrong after merge: future deterministic jobs could still be omitted from needs unless follow-up [CI] Document merge-gate dependency maintenance contract #108 lands.

Summary by CodeRabbit

  • Chores
    • Added an automated merge gate in CI that validates upstream job results before allowing merges.
    • The gate runs on each workflow execution, aggregates upstream outcomes into a concise summary, and blocks merges when any upstream job is failing, presenting a clear pass/fail status to inform merge decisions.

@coderabbitai

coderabbitai Bot commented Mar 6, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3a743664-8e49-4a99-93ba-035acb985bb0

📥 Commits

Reviewing files that changed from the base of the PR and between 78bff66 and efc5b9c.

📒 Files selected for processing (1)
  • .github/workflows/test.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/test.yml

📝 Walkthrough

Walkthrough

A new GitHub Actions job merge-gate is added to .github/workflows/test.yml; it runs after test, inspects the needs context for upstream job results, summarizes non‑passing jobs, and exits non‑zero to block merges when any blocking job failed.

Changes

Cohort / File(s) Summary
Merge Gate Workflow
.github/workflows/test.yml
Adds new merge-gate job that depends on test, iterates needs to gather upstream job statuses, prints a summary of non‑passing jobs, and exits with code 1 when any blocking job failed (otherwise exits 0).

Sequence Diagram(s)

sequenceDiagram
  participant PR as "Pull Request / Event"
  participant Runner as "GitHub Actions Runner"
  participant TestJob as "test job"
  participant MergeGate as "merge-gate job"

  PR->>Runner: trigger workflow
  Runner->>TestJob: execute tests
  TestJob-->>Runner: report status
  Runner->>MergeGate: start (needs: test)
  MergeGate->>Runner: read needs context (upstream job results)
  MergeGate->>MergeGate: aggregate non-passing jobs
  alt any failures
    MergeGate->>Runner: print red summary and exit 1
  else all pass
    MergeGate->>Runner: print green summary and exit 0
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

I hop the CI lanes, nose twitching with glee, 🐇
I tally the tests, each green leaf I see,
If one stumbles, I thump — the gate stays shut tight,
Else I wiggle my whiskers and welcome the light,
A rabbit that guards every merge through the night. ✨

🚥 Pre-merge checks | ✅ 3
✅ 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 and directly summarizes the main change: adding a merge-gate job to the CI workflow. It is specific, concise, and accurately reflects the primary objective of the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/merge-gate-standardization

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

@gemini-code-assist

Copy link
Copy Markdown

Note

Gemini is unable to generate a summary for this pull request due to the file types involved not being currently supported.

@greptile-apps

greptile-apps Bot commented Mar 6, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a merge-gate job to .github/workflows/test.yml that fans-in on all deterministic CI jobs and provides a single, stable status-check name for branch protection. The gate uses if: always() so it runs even when upstream jobs fail, evaluates results via a self-contained Python 3 stdlib script, and exits non-zero when any upstream job is neither success nor skipped.

Key points:

  • All three issues flagged in previous review rounds have been fully addressed: a maintenance comment was added above needs reminding authors to keep the fan-in list up-to-date; blocked_summary is now printed alongside the full summary to speed up CI triage; and python3 (guaranteed on all current ubuntu-latest images) is used explicitly.
  • The if: always() + needs: [test] pattern is the idiomatic approach for a fan-in gate in GitHub Actions — the gate waits for test to finish but runs regardless of its outcome, so branch protection always gets a definitive result.
  • Because test is a matrix job (Python 3.11/3.12/3.13), needs.test.result reflects the aggregated result of all variants; any individual variant failure will surface as failure here, which the gate script correctly blocks on.
  • The heredoc terminator PY dedents to column 0 after YAML block-scalar processing, and all top-level Python statements land at column 0 as well — no IndentationError risk.
  • No external actions or network calls are made in the gate step, so the 5-minute timeout is generous and appropriate.

Confidence Score: 5/5

  • This PR is safe to merge — it adds a self-contained gate job with no side effects and all prior review feedback has been incorporated.
  • The change is additive (one new job), uses only stdlib Python, follows idiomatic GitHub Actions fan-in patterns, and all previously identified issues have been resolved across the commit history. No logic, security, or correctness issues were found.
  • No files require special attention.

Important Files Changed

Filename Overview
.github/workflows/test.yml Adds a merge-gate job that fans-in on test (matrix), runs with if: always(), and uses a Python 3 stdlib script to surface blocked jobs and exit non-zero on failure. All previously flagged issues (maintenance comment, blocked_summary surfacing, python3 invocation) are addressed.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[push / pull_request trigger] --> B[test job\nmatrix: 3.11 / 3.12 / 3.13]
    B -->|always\nneeds: test| C[merge-gate job]
    C --> D{parse needs context\nvia toJSON}
    D --> E{any job NOT in\nsuccess or skipped?}
    E -->|yes| F["print: merge-gate red —\nfailed: blocked_summary | all: summary\nexit 1"]
    E -->|no| G["print: merge-gate green\nexit 0"]
    F --> H[Branch protection blocks merge]
    G --> I[Branch protection allows merge]
Loading

Last reviewed commit: efc5b9c

Comment thread .github/workflows/test.yml
@github-actions

github-actions Bot commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

⚠️ Cerberus Verdict: WARN

Summary: 5/6 reviewers passed. 1 warned (Maintainability).

Review Scope: 1 files changed, +35 / -0 lines
Reviewer Breakdown: 6 total | 5 pass | 1 warn | 0 fail | 0 skip
Findings: 0 critical | 1 major | 2 minor | 1 info

Wave Summary

  • Wave 1: 3 reviewers | 3 pass | 0 warn | 0 fail | 0 skip | 0 findings
  • Wave 2: 3 reviewers | 2 pass | 1 warn | 0 fail | 0 skip | 4 findings

Fix Order

  1. 🟠 Manual synchronization of merge gate dependencies (maintenance-burden) at .github/workflows/test.yml:31 (Maintainability)
    Fix: While this is a common GHA pattern to satisfy 'single required check' policies, consider adding a prominent warning in the contributor documentation (e.g., CONTRIBUTING.md) or investigating automation to dynamically list jobs if the CI com…
  2. 🟡 Inline script increases workflow maintenance overhead (cognitive-complexity) at .github/workflows/test.yml:39 (Maintainability)
    Fix: For a simple 'all-passed' check, the needs context can often be evaluated using native GHA expressions (e.g., ${{ !contains(needs.*.result, 'failure') }}), though the current Python approach provides better diagnostic logging. Use Pyth…
  3. 🟡 Dependency chain skip-bypass risk (partial-failure-handling) at .github/workflows/test.yml:44 (Resilience)
    Fix: Ensure all jobs in the workflow are explicitly listed in the 'needs' section of the merge-gate job to prevent skipped states from masking upstream failures.

Hotspots

Reviewer Overview

(click to expand)
  • ⚠️ Maintainability (craft) | WARN | 2 findings | wave Wave 2 | conf 0.90 | runtime 2m 28s | model gemini-3-flash-preview
  • Architecture (atlas) | PASS | 0 findings | wave Wave 2 | conf 0.95 | runtime 1m 37s | model claude-haiku-4.5
  • Resilience (fuse) | PASS | 2 findings | wave Wave 2 | conf 0.90 | runtime 2m 27s | model gemini-3-flash-preview
  • Security (guard) | PASS | 0 findings | wave Wave 1 | conf 1.00 | runtime 24s | model grok-4.1-fast
  • Testing (proof) | PASS | 0 findings | wave Wave 1 | conf 1.00 | runtime 52s | model kimi-k2.5
  • Correctness (trace) | PASS | 0 findings | wave Wave 1 | conf 0.95 | runtime 4m 22s | model kimi-k2.5

Key Findings

(show less)
  • 🟠 Manual synchronization of merge gate dependencies (maintenance-burden) at .github/workflows/test.yml:31 (Maintainability)

    Details

    Description: The merge-gate job requires manual updates to its needs list whenever new CI jobs are added to the workflow. If a developer adds a mandatory check (e.g., security scanning, linting) but forgets to update this list, the 'merge-gate' will provide a false sense of security by passing even if the new job fails. This creates a friction point where CI logic is decoupled from its enforcement gate.
    Suggestion: While this is a common GHA pattern to satisfy 'single required check' policies, consider adding a prominent warning in the contributor documentation (e.g., CONTRIBUTING.md) or investigating automation to dynamically list jobs if the CI complexity grows beyond a few jobs.

  • 🟡 Dependency chain skip-bypass risk (partial-failure-handling) at .github/workflows/test.yml:44 (Resilience)

    Details

    Description: The merge-gate script treats a 'skipped' job result as a passing state. This creates a resilience gap: if a required upstream job fails, its downstream dependencies are 'skipped'. If only the downstream job is tracked by the merge-gate, the gate will erroneously report success.
    Suggestion: Ensure all jobs in the workflow are explicitly listed in the 'needs' section of the merge-gate job to prevent skipped states from masking upstream failures.

  • 🟡 Inline script increases workflow maintenance overhead (cognitive-complexity) at .github/workflows/test.yml:39 (Maintainability)

    Details

    Description: Embedding a 20-line Python script inside a YAML block increases the cognitive load for maintaining the CI pipeline. While functional and robust for status reporting, it is harder to lint or test independently than a dedicated script or a composite action.
    Suggestion: For a simple 'all-passed' check, the needs context can often be evaluated using native GHA expressions (e.g., ${{ !contains(needs.*.result, 'failure') }}), though the current Python approach provides better diagnostic logging. Use Python when the reporting complexity justifies the heredoc overhead.

  • 🔵 Missing timeout configuration (timeout-configuration) at .github/workflows/test.yml:29 (Resilience)

    Details

    Description: The merge-gate job does not specify a timeout-minutes property. A hang in the runner or execution environment could consume runner minutes unnecessarily.
    Suggestion: Add timeout-minutes: 5 to the merge-gate job.

Reviewer details (click to expand)

⚠️ Maintainability (craft) — WARN

  • Confidence: 0.90
  • Wave: Wave 2
  • Model: gemini-3-flash-preview
  • Runtime: 2m 28s
  • Summary: The addition of a centralized merge-gate job introduces a manual synchronization requirement that acts as a maintenance 'landmine' for future CI changes.

Findings

  • 🟠 Manual synchronization of merge gate dependencies (maintenance-burden) at .github/workflows/test.yml:31
    • The merge-gate job requires manual updates to its needs list whenever new CI jobs are added to the workflow. If a developer adds a mandatory check (e.g., security scanning, linting) but forgets to update this list, the 'merge-gate' will provide a false sense of security by passing even if the new job fails. This creates a friction point where CI logic is decoupled from its enforcement gate.
    • Suggestion: While this is a common GHA pattern to satisfy 'single required check' policies, consider adding a prominent warning in the contributor documentation (e.g., CONTRIBUTING.md) or investigating automation to dynamically list jobs if the CI complexity grows beyond a few jobs.
  • 🟡 Inline script increases workflow maintenance overhead (cognitive-complexity) at .github/workflows/test.yml:39
    • Embedding a 20-line Python script inside a YAML block increases the cognitive load for maintaining the CI pipeline. While functional and robust for status reporting, it is harder to lint or test independently than a dedicated script or a composite action.
    • Suggestion: For a simple 'all-passed' check, the needs context can often be evaluated using native GHA expressions (e.g., ${{ !contains(needs.*.result, 'failure') }}), though the current Python approach provides better diagnostic logging. Use Python when the reporting complexity justifies the heredoc overhead.

✅ Resilience (fuse) — PASS

  • Confidence: 0.90
  • Wave: Wave 2
  • Model: gemini-3-flash-preview
  • Runtime: 2m 27s
  • Summary: The merge gate is correctly implemented with a fail-closed script, though it introduces a maintenance burden to keep the 'needs' list synchronized with the full CI suite.

Findings

  • 🟡 Dependency chain skip-bypass risk (partial-failure-handling) at .github/workflows/test.yml:44
    • The merge-gate script treats a 'skipped' job result as a passing state. This creates a resilience gap: if a required upstream job fails, its downstream dependencies are 'skipped'. If only the downstream job is tracked by the merge-gate, the gate will erroneously report success.
    • Suggestion: Ensure all jobs in the workflow are explicitly listed in the 'needs' section of the merge-gate job to prevent skipped states from masking upstream failures.
  • 🔵 Missing timeout configuration (timeout-configuration) at .github/workflows/test.yml:29
    • The merge-gate job does not specify a timeout-minutes property. A hang in the runner or execution environment could consume runner minutes unnecessarily.
    • Suggestion: Add timeout-minutes: 5 to the merge-gate job.

Cerberus (master) | Run #22915197202 | Override policy pr_author | Fail on verdict true | Override command: /cerberus override sha=483f7b88f17f (reason required)

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

Cerberus inline comments for 34000e920567

  • Inline comments posted: 1/1
  • Canonical report: verdict report
  • Cerberus verdict: WARN (6 reviewers. Failures: 0, warnings: 1, skipped: 0.)

Comment thread .github/workflows/test.yml Outdated
@kaylee-mistystep

Copy link
Copy Markdown
Contributor

Review feedback triage

Refreshed every live feedback surface against commit 483f7b8.

  • Critical: none.
  • In-scope fixed now:
    1. Greptile thread (risk, low) — added the maintenance note above merge-gate.needs.
    2. Cerberus major (risk, high) — removed the hardcoded needs.test.result coupling and now aggregate all needs results generically.
    3. Cerberus minor (risk, medium) — treat skipped as a neutral/passing upstream state while still failing any other non-passing result.
  • Follow-up: none worth spinning out; the remaining maintenance concern is now documented inline and in the PR body.
  • Declined: none.

Local verification on 483f7b8:

  • python3 -c 'import yaml; yaml.safe_load(open(".github/workflows/test.yml")); print("workflow yaml parses")'
  • uv venv --python 3.11 .venv311 && . .venv311/bin/activate && uv pip install -e '.[dev]' && ruff check src/ && mypy src/ && pytest -v253 passed
  • targeted merge-gate smoke payloads for success, skipped, failure, and mixed upstream jobs all produced the expected pass/fail behavior

Required checks are rerunning for this push; I’ll only merge once the new merge-gate + test matrix come back green and no fresh bot findings land.

Comment thread .github/workflows/test.yml

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

Cerberus inline comments for 483f7b88f17f

  • Inline comments posted: 1/1
  • Canonical report: verdict report
  • Cerberus verdict: WARN (6 reviewers. Failures: 0, warnings: 1, skipped: 0.)

Comment thread .github/workflows/test.yml
Comment thread .github/workflows/test.yml Outdated
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants