CI/CD must pass before merge — ABSOLUTE rule, all Corone projects

No PR merges with failing CI. Every CI job must be green. No bypass. No exceptions.

User (r_goto) directive 2026-05-04, hardened after CRV-46 firefight regressed Curva’s CD bypass.


Scope

Applies to every repo under COCONRobotics-Corp org and every Corone-Inc account project hosted on this VPS or anywhere else:

ProjectRepoBranch model
CurvaCOCONRobotics-Corp/Curvadevelop (with user/corone-CRV-XX-… PRs)
Pasukuru FECOCONRobotics-Corp/pasukuru-feTBD
Pasukuru BECOCONRobotics-Corp/pasukuru-beTBD
Corone MonsterCOCONRobotics-Corp/corone-monstermain + corone-ryo + corone-julian
Kokorozashi(Xserver static)manual deploy
Passkuru LP(Xserver static)manual deploy
Future Corone projectsTBDinherits this rule

Plus Da Vinci (DVG) — but that’s NO-TOUCH read-only, no merges happen via this agent anyway. Rule still applies if/when ever lifted.


The hard rule (verbatim)

Every CI/CD test job must show GREEN before any PR can be merged.

If even one job is red/failed/cancelled, MERGE IS FORBIDDEN regardless of:

  • Who requested the merge
  • How urgent the fix is
  • Whether the failing job is “unrelated”
  • Whether it works locally
  • Whether CI is “just being flaky”
  • Whether the user explicitly says “merge it anyway”

If user requests merge while CI is red → respond:

PR has N red CI jobs. Cannot merge. Fix CI first.

Then either auto-fix the red jobs or surface them to the user for direction.


Pre-merge checklist (every PR, every project)

Run through this before acting on any merge request:

[ ] All CI jobs status == "success"
    (not "pending", not "failure", not "cancelled", not "skipped-but-should-have-run")
[ ] Branch up-to-date with target (no behind-by-N)
[ ] PR body has Jira ID
[ ] Workspace attribution in PR body or Jira comment
    ("Requested by Ryo" or "Requested by Julian")
[ ] No secrets in commit diff
[ ] Explicit human approval THIS specific commit SHA
    (Ryo/eduson510 OR Julian/time7676 — both valid signoff)

If any box unchecked → merge blocked.

To check CI status fast:

gh pr checks <PR_NUMBER>

Returns table with pass / fail / pending per job.


Required infrastructure (every Corone repo)

For this rule to be enforceable, every Corone repo MUST have:

  1. Branch protection on default branch (and any deploy-target branch like develop, staging):
    • Require status checks before merge
    • Require branches to be up to date before merging
    • Required status checks: ALL CI jobs that run on PRs must be selected
  2. CI workflow triggered on push and pull_request to all deploy-target branches.
  3. CD workflow that either:
    • Runs only on workflow_run after CI succeeds, OR
    • Has a ci-gate job that uses gh api to verify CI green for the same SHA before deploy proceeds (CRV-54 pattern, see .github/workflows/cd.yml in COCONRobotics-Corp/Curva).
  4. No workflow_dispatch on deploy workflows without ci-gate.
  5. No [skip ci] / --skip-ci / --no-verify on commits to deploy-target branches.

If any of these are missing → file a ticket like CRV-54 and harden before any further feature work.


CRV-54 reference pattern (for future Corone repos)

The cd.yml ci-gate job from Curva (COCONRobotics-Corp/Curva PR #1045):

on:
  workflow_run:
    workflows: [CI]
    branches: [main, develop]
    types: [completed]
  workflow_dispatch:
    inputs:
      reason:
        description: "Why dispatch manually? (audit trail)"
        required: true
        type: string
 
jobs:
  ci-gate:
    if: github.event_name == 'workflow_dispatch'
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
    steps:
      - name: Verify CI passed for SHA ${{ github.sha }}
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          REPO: ${{ github.repository }}
          SHA: ${{ github.sha }}
        run: |
          set -euo pipefail
          RUN_JSON=$(gh api -H "Accept: application/vnd.github+json" \
            "/repos/${REPO}/actions/workflows/ci.yml/runs?head_sha=${SHA}&per_page=1" \
            --jq '.workflow_runs[0] // empty')
          if [ -z "$RUN_JSON" ]; then
            echo "::error::No CI run found for SHA $SHA"
            exit 1
          fi
          STATUS=$(echo "$RUN_JSON" | jq -r '.status')
          CONCLUSION=$(echo "$RUN_JSON" | jq -r '.conclusion')
          if [ "$STATUS" != "completed" ] || [ "$CONCLUSION" != "success" ]; then
            echo "::error::CI did not succeed for SHA $SHA"
            exit 1
          fi
 
  build-and-deploy:
    needs: [ci-gate]
    if: |
      always() &&
      (
        (github.event_name == 'workflow_run' && !contains(fromJSON('["failure", "cancelled"]'), github.event.workflow_run.conclusion)) ||
        (github.event_name == 'workflow_dispatch' && needs.ci-gate.result == 'success')
      )
    # ... rest of deploy ...

Use this pattern in any new deploy workflow on any Corone repo.


Anti-patterns (FORBIDDEN)

  • “Just merge it, the test failure is unrelated.” → NO. Fix the test or open a tracking ticket and explicitly approve the deviation, with sign-off recorded.
  • “It worked locally, CI is just slow/flaky.” → NO. Flakiness is a separate ticket. Re-run CI to green; do not merge red.
  • “We need to ship this hotfix now, CI takes too long.” → NO. Use the ci-gate-aware emergency workflow with documented reason input. Never bypass.
  • “I’ll merge and fix CI in the next commit.” → NO. Develop branch must always be deployable.
  • “Lint is just style, doesn’t matter.” → NO. Lint is a hard gate. Style errors signal process failures (review skipped, hooks bypassed).
  • Adding [skip ci] to deploy commits. → Forbidden by branch protection if configured correctly.
  • Using GitHub admin override to merge. → Forbidden except for the single exception path below.

Single exception path

GitHub repository admin override is allowed ONLY when ALL of the following are true:

  1. Documented in an incident ticket within 24 hours of the override.
  2. Explicit r_goto sign-off (either Ryo/eduson510 or Julian/time7676 persona).
  3. Reason matches: “production down, CI infrastructure broken, customer impact”.
  4. A follow-up ticket exists to root-cause + harden CI within 7 days.

That’s it. Single, narrow, audited path. Anything else → forbidden.


How this rule was hardened (CRV-54 history)

  • 2026-05-03 → 2026-05-04: CRV-46 emergency redis pecl + EB recovery work on Curva.
  • During CRV-46, PR #1027 added unconditional workflow_dispatch to cd.yml. Eight manual production deploys ran through it on 2026-05-04. CI was also returning false-greens because paths-filter excluded .ebextensions/, .platform/, .github/workflows/, etc.
  • 2026-05-04: CRV-54 created (Julian persona scoping ticket).
  • 2026-05-04: PR #1045 opened (Ryo + Julian commits) — restored mandatory CI gate, expanded paths-filter, version unification, action upgrades, ci-light deletion, eb-ops consolidation, package audit, README documenting hard rule + anti-patterns.
  • 2026-05-04: After PR #1045 lint fixes (commits 0e7e7ba + 3513baa), user (r_goto) made this an absolute rule for ALL Corone projects, not just Curva.

Cross-references

  • Memory: corone-cicd-must-pass-before-merge-absolute (canonical persistent record)
  • Memory: crv-54-pr-1045-cicd-hardening (origin PR, ci-gate pattern)
  • Memory: curva-merge-approval-personas (Ryo OR Julian both valid signoff)
  • Memory: absolute-jira-id-comment-attribution-corone-keb (companion rule on git push workflow)
  • File: /var/www/CLAUDE.md (global CLAUDE prompt — has a section pointing here)
  • File: /var/www/corone.monster/CLAUDE.md and /var/www/kebahagiaan.corone.monster/CLAUDE.md (per-workspace cross-refs)
  • File: .github/workflows/README.md in COCONRobotics-Corp/Curva (concrete reference implementation)