CI gate

One HTTP call carries the whole gate: your CI pipes the diff, the status code carries the verdict.

The endpoint

POST /api/ci/gate
Authorization: Bearer <workspace token>
Body: raw output of `git diff base...HEAD`

The response status is the decision:

StatusMeaning
422At least one blocking violation (severity error). curl --fail-with-body exits non-zero → the CI job fails → the merge is gated.
200Compliant, only non-blocking violations, an empty diff — or the gate couldn't run (fail-open, see below).
401Missing or invalid token.

Response formats

By default the body is human-readable text/plain, made for CI logs. Send Accept: application/json to get a structured response instead — same status codes:

{ "status": "failure", "blocking": 1, "rulesApplied": 8, "filesAnalysed": 3,
  "violations": [ { "ruleId": "…", "path": "src/api.ts", "line": 42,
                    "severity": "error", "comment": "…",
                    "suggestion": "…" | null } ] }

// empty diff:            { "status": "empty" }
// gate couldn't run:     { "status": "unavailable", "reason": "…" }
// diff over 4 MB (413):  { "status": "oversize", "reason": "…", "limitBytes": 4194304 }

Fix suggestions

A violation may carry a suggestion: one short, concrete line on how to fix what was flagged. In the text/plain output it appears under the violation, prefixed with ↳ suggested fix; in JSON it is a suggestion field on the violation object, null when there is none (older analyses may omit the key entirely). It also shows up in the Analyses history and in repo scan reports.

Read it as a hint, not as a patch. The gate only sees the added lines of the diff — not the rest of your repository. A suggestion can therefore miss a helper you already have, a signature it never saw, or a convention that lives in another file, and be wrong even when the violation itself is correct. Nothing is auto-applied, and staying silent is the default: when the model has no confident fix it returns null rather than inventing one. Review a suggestion before acting on it, exactly as you would review the violation.

Context headers (optional)

HeaderUse
X-Normik-RepoRepository label shown in the Analyses history (e.g. acme/shop).
X-Normik-ShaHead commit SHA of the checked diff.
X-Normik-PRPull-request number.

Fail-open, by design

If no AI provider is configured, or the model call fails (invalid key, quota, provider outage), the gate returns 200 with an explanation instead of blocking. A quality gate should catch bad code — never hold your deliveries hostage to a third-party incident. Blocking is reserved for actual error-severity violations.

The generated snippets extend that guarantee to the client side: they capture the HTTP status code instead of relying on curl --fail, and fail the job on 422 only. An unreachable host, a timeout or a 5xx leaves the pipeline green with an explanation in the log.

GitHub Actions — with PR comments

The generated snippet (Settings → CI integration) requests JSON, posts a violation summary as a PR comment using the workflow's own GITHUB_TOKEN (permissions: pull-requests: write), and exits 1 only on a 422. No app, no webhook — the comment comes from your pipeline, not from a bot we operate.

GitLab CI — with optional MR notes

Same call from a merge_request_event job with GIT_DEPTH: "0" and a fetch of the target branch. The generated snippet also posts the violation report as a merge-request note — but that part is opt-in, and here is why: GitLab's built-in CI_JOB_TOKEN is not allowed to write MR notes, so no snippet can post a comment with the credentials a pipeline already has.

To turn comments on, create a project access token (or a personal access token) with the api scope and at least the Developer role, and expose it to the pipeline as a masked CI variable named NORMIK_GITLAB_TOKEN. The job then calls POST /projects/:id/merge_requests/:iid/notes with that token whenever violations are reported. Leave the variable unset and the job behaves exactly the same otherwise: the verdict stays in the job log and in the Normik dashboard.

Posting a note is best-effort in both directions: a failed comment never fails the pipeline, and the job's exit status is still decided solely by the gate's status code.

What gets analyzed

  • Only files present in the diff, and only their added lines — deleted files and binary changes are skipped.
  • Only rules whose glob scopes match at least one changed file.
  • Every run is recorded in the Analyses history with its violations and token usage.