Skip to content

Publication-contract validator is unenforced on the merge-queue route #2940

Description

@devantler

🤖 Generated by the Agentic Engineer

Evidence

validateProductionRoutes checks that each merge-queue production job invokes the checked deploy
action, but it never checks the publication-contract validator step itself on that route.

On the merge-queue path (.github/workflows/ci.yaml):

  • deploy-prod declares needs: [changes, validate-eks-authorization]
  • the DR-signing validator (go run ./scripts/validate-dr-signing …) runs as a step inside the
    changes job
    , not in a dedicated gate job
  • validateProductionRoutes contains no reference to validatorCommand, requireEnforcedStep on
    that step, or runBlockIsSimpleSequence

So the changes job can succeed with the validator neutered, and deploy-prod proceeds.

The direct-push route in cd.yaml is protected against exactly this by validateDRWorkflow: the
validator step there must execute the command, must be neither skipped nor failure-suppressed, must be
a straight-line sequence, and (as of #2939) must carry no shell override at any scope. None of that
applies to the ci.yaml occurrence.

Why it matters

The contract's own premise is that the guarantee is only as strong as its weakest route, and the
merge-queue route is the normal path to production — direct-push is the exception. Every bypass
already pinned for cd.yaml is reachable here: echo-ing the command, set +e, continue-on-error,
if: false, a here-doc, or defaults.run.shell: bash {0} plus a trailing true.

Not exploitable by an outside contributor — it needs a workflow edit — so this is defence-in-depth of a
gate that already exists, not a live exposure.

Expected behaviour

The ci.yaml validator step is held to the same enforcement rules as the cd.yaml one, and
deploy-prod is proven to depend on the job that carries it.

Acceptance criteria

  1. validateProductionRoutes (or a peer) locates the validator step on the merge-queue route by the
    same validatorCommand equality used for cd.yaml, and fails when it is absent.
  2. That step is required to be enforced — not skipped, not continue-on-error, straight-line, and no
    shell override at workflow / job / step scope.
  3. deploy-prod is required to needs the job that actually carries the validator, so moving the step
    to an unrequired job fails the check.
  4. One ablation per rule, each failing a disjoint test, each build-checked.
  5. The shipped ci.yaml passes unchanged.

Size

Small–medium. The rules exist; this reuses requireEnforcedStep / refuseShellOverride /
runBlockIsSimpleSequence against a second route rather than writing new logic.

Found while fixing a CodeRabbit P1 on #2939; deliberately kept out of that PR to keep it one concern.

Activity

  1. moved this to 📥 Backlog in 🌊 Project Boardon Aug 4, 2026
  2. coderabbitai commented on Aug 4, 2026

    @coderabbitai
    Contributor
    🔗 Related PRs

    #2330 - ci: heal prod when a merge-group deploy fails (restore main) [merged]
    #2622 - fix(ci): heal cancelled merge-group deploys [merged]
    #2753 - fix(aws): allow EKS version discovery [merged]
    #2825 - fix(ci): queue prod deploys instead of dropping the pending one [merged]
    #2930 - feat(ci): verify published evidence before promoting the prod digest [merged]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  3. self-assigned this
    on Aug 4, 2026
  4. added a commit that references this issue on Aug 4, 2026
    f0f3b9a
  5. moved this from 📥 Backlog to 👀 In Review in 🌊 Project Boardon Aug 4, 2026
  6. moved this from 👀 In Review to ✅ Done in 🌊 Project Boardon Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Fields

No fields configured for Security.

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions