Free · CI Gate

Gate AI-code defects before they merge

The Shipmoor CI Gate runs the same deterministic scan in two places: as a pre-commit hook on your laptop and as a CI job on every pull request. It fails the build when a finding at or above your severity threshold is present. Phantom imports, hallucinated APIs, stub paths, and placeholder bodies stop at the gate instead of in review.

A coding agent finishes a task, writes a confident summary, and opens a pull request. The diff compiles and the tests it wrote pass, but it imports a module that doesn’t exist, calls an API the library never shipped, or leaves a handler body as a TODO that quietly returns None. These are AI-code-integrity defects: plausible-looking mistakes that survive type checks and land in review, where a human has to catch them by reading carefully.

The Shipmoor CI Gate stops them earlier. It runs one deterministic scan in two places: as a pre-commit hook on the developer’s machine and as a CI job on every pull request. It fails the build when a finding at or above your severity threshold is present. Scope it to the change so pre-existing debt never blocks, and start in measurement-only mode to baseline before you enforce. The scan runs on your runner; source code and diffs are not uploaded to Shipmoor.

  • Free account · Local execution · No source upload
  • pre-commit and any CI
  • Runs on your runner · no source upload
  • pre-commit
  • GitHub Actions
  • SARIF 2.1.0
  • exit-code gate
  • diff-scoped
  • pinned CLI
  • no source upload
$ shipmoor scan --diff origin/main...HEAD --fail-on high --no-color Scanning 7 changed files (origin/main...HEAD) HIGH   phantom-import   services/api/handlers/webhook.py:12       import stripe.webhooks_v2  — module does not existHIGH   stub-path         services/api/handlers/webhook.py:41       handler returns None — TODO body never implemented Gate: FAILED — 2 high findings introduced by this changeSARIF written to shipmoor.sarif · exit 1

The gate on a pull request: scan the introduced change, fail the job on a high-severity finding.

One scan, two gates

The gate is the same binary and the same rules wherever it runs. Pre-commit catches defects locally, before they ever reach the remote; CI is the backstop that can't be skipped. Nothing pre-existing has to block. Scope the gate to the change.

  1. Agent writes code
  2. pre-commit (local)
  3. CI gate (pull request)
  4. Review & merge

Catch it on the laptop where it's a one-line fix, or at the pull request where it's still cheaper than a review round-trip. Either way the same finding can't make it to main.

Wire the gate into your workflow

Two Scan integrations, one engine. Start with pre-commit for the fast local loop, then add the token-authenticated CI gate so nothing slips past a skipped hook. For reading-level feedback, run Code Review locally: Free includes 5 Reviews per month, and every review stays advisory.

local · pre-commit

Pre-commit hook

The official pre-commit repo installs the Shipmoor CLI for you and scans the staged change. Latest CLI by default; pin a version with args.

  • Run pre-commit install once; the hook runs on every commit.
  • Installs the latest Shipmoor CLI by default, cached per version.
  • Same scan, same rules, same exit code as the CI gate.
shipmoor-pre-commit on GitHub
repos:
  - repo: https://github.com/shipmoor/shipmoor-pre-commit
    rev: v0.1.0
    hooks:
      - id: shipmoor-scan
        # optional: pin the CLI and the severity gate
        args: [--shipmoor-version=0.3.0, --fail-on, high]
ci/cd · github actions

GitHub Actions: quickstart

A minimal pull-request gate: install the pinned CLI from the official installer, scan only what the change introduced, and fail on a high finding.

  • fetch-depth: 0 so the diff base is available for diff-scoped scans.
  • Pin SHIPMOOR_VERSION for reproducible builds; scan . for a full tree.
  • Exit code is the gate; non-zero at or above --fail-on fails the job.
CI gating docs
name: shipmoor
on:
  pull_request:
permissions:
  contents: read
jobs:
  gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0          # the diff needs history

      - name: Install Shipmoor CLI (pinned)
        env:
          SHIPMOOR_VERSION: "0.3.0"
          SHIPMOOR_NO_PATH_HINT: "1"
        run: |
          curl -fsSL https://dl.shipmoor.dev/install.sh | bash
          echo "$HOME/.shipmoor/bin" >> "$GITHUB_PATH"

      - name: Gate the change
        env:
          SHIPMOOR_TOKEN: ${{ secrets.SHIPMOOR_TOKEN }}
        run: |
          shipmoor scan \
            --diff "origin/${{ github.base_ref }}...HEAD" \
            --fail-on high --no-color
ci/cd · authentication

Authorize self-managed Scan

Create a revocable, Scan-scoped machine token and store it as the SHIPMOOR_TOKEN secret. Current self-managed CI supports Scan only; Review, Claim Check, Test Evidence, and Blast Radius remain local commands.

  • Machine tokens are Scan-scoped, revocable, and optionally expiring.
  • The CLI reads the token from SHIPMOOR_TOKEN and never copies it to disk.
  • Review and the other metered commands run locally; managed CI is coming with Team.
CLI authentication docs
# Create this once on an authorized development machine.
shipmoor auth token create --name github-my-repo --scope scan

# Save the displayed secret as the repository secret SHIPMOOR_TOKEN.
# The workflow exposes it only to the Shipmoor Scan step.
ci/cd · advanced

GitHub Actions: full composite action

Drop this in as a local action for the complete setup: a pinned install, diff-scoped gating, a measurement-only baseline mode, and SARIF + Markdown reports uploaded as artifacts on every run. Call it with uses: ./.github/actions/shipmoor-gate.

  • diff-base gates only findings the change introduced. Pre-existing debt never blocks.
  • soft-fail: true is the measurement-only baseline: it reports without failing.
  • Always uploads SARIF 2.1.0 + a Markdown summary as artifacts, even on failure.
  • Per-package config-file makes it monorepo-ready out of the box.
  • Pass a Scan-scoped machine token from secrets as the token input.
name: "Shipmoor Gate"
description: >
  Runs the Shipmoor CLI against scan-path to catch AI-code-integrity
  defects (phantom imports, hallucinated APIs, stub paths, placeholder bodies).
  Installs the pinned CLI via the official installer, scans, and fails the
  build when a finding at or above `fail-on` severity is present. Always
  uploads SARIF 2.1.0 + a Markdown summary as artifacts. Set soft-fail: true
  for the measurement-only baseline run.

inputs:
  scan-path:
    description: "Directory to scan (e.g. services/realtime-listing-processor)"
    required: true
  token:
    description: "Scan-scoped Shipmoor machine token"
    required: true
  version:
    description: "Pinned Shipmoor CLI version (SHIPMOOR_VERSION)"
    required: false
    default: "0.3.0"
  fail-on:
    description: "Severity gate: none | medium | high | critical"
    required: false
    default: "high"
  config-file:
    description: "Path to .shipmoor.yaml relative to scan-path"
    required: false
    default: ".shipmoor.yaml"
  soft-fail:
    description: "Report findings without failing the job (true/false)"
    required: false
    default: "false"
  diff-base:
    description: >
      When set, gate only findings introduced relative to this git ref by
      running `shipmoor scan --diff <diff-base>...HEAD` (honours
      .shipmoor.yaml `diff.only_introduced`). Leave empty for a full-tree scan.
      Typically the PR base SHA: pre-existing debt must not block.
    required: false
    default: ""

runs:
  using: "composite"
  steps:
    - name: Install Shipmoor CLI (pinned)
      shell: bash
      env:
        SHIPMOOR_VERSION: ${{ inputs.version }}
        SHIPMOOR_NO_PATH_HINT: "1"
      run: |
        curl -fsSL https://dl.shipmoor.dev/install.sh | bash
        echo "$HOME/.shipmoor/bin" >> "$GITHUB_PATH"

    - name: Show Shipmoor version
      shell: bash
      run: shipmoor version

    - name: Resolve artifact suffix
      id: name
      shell: bash
      run: |
        SAFE="$(echo "${{ inputs.scan-path }}" | tr '/ ' '--')"
        echo "suffix=$SAFE" >> "$GITHUB_OUTPUT"

    - name: Run Shipmoor scan
      shell: bash
      working-directory: ${{ inputs.scan-path }}
      env:
        SHIPMOOR_TOKEN: ${{ inputs.token }}
      run: |
        SARIF="$RUNNER_TEMP/shipmoor-${{ steps.name.outputs.suffix }}.sarif"
        SUMMARY="$RUNNER_TEMP/shipmoor-${{ steps.name.outputs.suffix }}.md"

        # In soft-fail (baseline) mode the gate never blocks, so scan with
        # --fail-on none; otherwise honour the requested severity threshold.
        if [ "${{ inputs.soft-fail }}" = "true" ]; then
          FAIL_ON="none"
        else
          FAIL_ON="${{ inputs.fail-on }}"
        fi

        # Prefer the per-package config, fall back to the repo-root config,
        # otherwise let Shipmoor auto-discover.
        CONFIG_ARG=""
        if [ -f "${{ inputs.config-file }}" ]; then
          CONFIG_ARG="--config ${{ inputs.config-file }}"
        elif [ -f "$GITHUB_WORKSPACE/.shipmoor.yaml" ]; then
          CONFIG_ARG="--config $GITHUB_WORKSPACE/.shipmoor.yaml"
        fi

        # When a diff base is supplied, gate only findings introduced by the
        # change (legacy debt must not block migrations). Empty => full scan.
        DIFF_ARG=""
        if [ -n "${{ inputs.diff-base }}" ]; then
          DIFF_ARG="--diff ${{ inputs.diff-base }}...HEAD"
          echo "Shipmoor diff scope: ${{ inputs.diff-base }}...HEAD"
        else
          echo "Shipmoor scope: full tree"
        fi

        EXIT=0
        shipmoor scan . \
          --sarif --output "$SARIF" \
          --markdown-summary "$SUMMARY" \
          --fail-on "$FAIL_ON" \
          --no-color \
          $DIFF_ARG \
          $CONFIG_ARG || EXIT=$?

        if [ "${{ inputs.soft-fail }}" = "true" ]; then
          echo "soft-fail mode: shipmoor exit was ${EXIT}, not failing the job"
          exit 0
        fi
        exit "${EXIT}"

    - name: Upload Shipmoor report
      if: always()
      uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
      with:
        name: shipmoor-report-${{ steps.name.outputs.suffix }}
        path: |
          ${{ runner.temp }}/shipmoor-${{ steps.name.outputs.suffix }}.sarif
          ${{ runner.temp }}/shipmoor-${{ steps.name.outputs.suffix }}.md
        if-no-files-found: warn

Tune the gate

Every behavior in the action maps to a flag on shipmoor scan — the same flags you run locally.

  • Gate only what the PR introduced

    shipmoor scan --diff origin/main...HEAD --fail-on high
  • Set the severity threshold

    shipmoor scan --staged --fail-on high # none | medium | high | critical
  • Measurement-only baseline (never blocks)

    shipmoor scan . --fail-on none --sarif --output shipmoor.sarif
  • Per-package config in a monorepo

    shipmoor scan services/api --config services/api/.shipmoor.yaml
  • Reproduce the exact CI gate locally

    SHIPMOOR_VERSION=0.3.0 shipmoor scan --staged --fail-on high

Same binary, same rules, same exit code, whether it runs in pre-commit, in CI, or on your laptop.

The gate runs in your pipeline, not ours

The official installer drops a pinned binary onto your runner, the scan runs there, and the job's exit code is the gate. Your source code and diffs are not uploaded. Shipmoor contacts the service for authentication, entitlements, command allowances, and features you explicitly enable. The same deterministic engine runs locally and in CI.

Free for local and self-managed CI

A new personal account receives one 30-day period of full Pro at account creation, with no card or invitation required. Free keeps Scan, Agent Harness, and Agent Skills unlimited and includes separate monthly allowances for the four verification commands.

  • $0

    Free

    Useful recurring verification for local work and self-managed CI.

    • 5/month each: Claim Check, Review, Test Evidence, Blast Radius
    • Unlimited Scan, Agent Harness, and Agent Skills
    • Pre-commit and self-managed CI with SARIF
    • Account required; source and diffs are not uploaded
  • $19 / month

    Pro

    Unlimited individual verification and the full advanced workflow.

    • Unlimited Claim Check, Review, Test Evidence, and Blast Radius
    • Unlimited Scan, Agent Harness, and Agent Skills
    • Full IDE, advanced configuration, and richer local history
    • Local execution; source and diffs are not uploaded

Put the gate in front of your next agent change

Create your personal account to receive one 30-day period of full Pro with no card, authorize the CLI, and put the deterministic Scan in pre-commit or token-authenticated CI.

Install Shipmoor

Free account. Local execution. No source upload.

Start free
curl -fsSL https://dl.shipmoor.dev/install.sh | bash

CI gating docs

CI Gate FAQ FAQ

Pre-commit, CI, severity gates, and rolling it out without breaking every build.

Contact sales

Our team can help with custom support, team rollouts, and self-hosted deployments. Or to get started now, explore our self-serve plans.