Skip to content

feat(code-review): surface code review as PR/MR gate check#885

Merged
alex-alecu merged 42 commits intomainfrom
feat/review-as-pr-gate
Mar 9, 2026
Merged

feat(code-review): surface code review as PR/MR gate check#885
alex-alecu merged 42 commits intomainfrom
feat/review-as-pr-gate

Conversation

@alex-alecu
Copy link
Contributor

@alex-alecu alex-alecu commented Mar 6, 2026

Summary

  • Cloud code reviews now create a visible status check on GitHub PRs (via Check Runs API) and GitLab MRs (via Commit Status API), so review results appear directly in the PR checks tab rather than only as comments and reactions.
  • Users can configure these as required checks in GitHub branch protection rules or GitLab merge request approval rules, turning code review into a merge gate.
  • Check status tracks the full review lifecycle: queued -> in progress -> success/failure/cancelled, with a details link pointing back to the Kilo review dashboard for full logs.
  • Adds a strict_gate option to the code review agent config — when enabled, the check will fail if the review finds blocking issues (requires future cloud agent callback extension). Defaults to system-failure-only mode.
  • Lite (read-only) GitHub App installations are excluded since they lack the checks:write permission.
  • The entire feature is gated behind a PostHog feature flag (code-review-pr-gate), evaluated per-user. When disabled or missing, no check runs are created, gateThreshold is forced to off, and the UI hides the gate config. In dev environments the flag is overridden to true via process.env.NODE_ENV === 'development'.

Note: The KiloConnect GitHub App needs checks:write permission added in its settings. Existing installations will be prompted to accept the new permission. Until then, check creation silently fails and reviews proceed normally.

Verification

  • pnpm typecheck — passes across all 28 workspace projects
  • Reviewed all 8 commits for correctness, consistent patterns, and no regressions

Visual Changes

Demo alex-alecu/stories-canvas#16

Screenshot 2026-03-06 at 17 26 25 Check "PR Gate Threshold"

Reviewer Notes

  • The GitHub App permission upgrade (checks:write) is a manual admin step. Until it's done, the new check run creation calls will fail non-blocking — reviews still work exactly as before.
  • The strict_gate config option is added but not yet active at the cloud agent level. It's wired into the schema so the UI and backend are ready when the agent starts reporting a gate_result in its completion callback.
  • The status callback handler now fetches the platform integration once and reuses it for both the gate check update and the existing reaction/footer logic (was previously fetched twice for terminal states).
  • Feature flag: code-review-pr-gate — evaluated per-user via PostHog. When the flag is off, undefined, or PostHog errors, the system defaults to disabled (no check runs, gateThreshold forced to off, UI dropdown hidden). The flag is checked at 6 callsites: GitHub/GitLab webhook handlers (check run creation), review payload preparation (gateThreshold), retrigger (gate recreation), and config routers (UI visibility). Cancel and status-update paths are naturally gated by the absence of a check_run_id.

Track GitHub Check Run IDs on code review records so we can update
the check status as the review progresses through its lifecycle.
Add createCheckRun() and updateCheckRun() to the GitHub adapter.
These use the GitHub Checks API to create rich status checks that
appear in the PR Checks tab and can be set as required checks in
branch protection rules.
Add setCommitStatus() to the GitLab adapter. GitLab commit statuses
are idempotent by (sha, name), so we upsert a 'kilo/code-review'
status on each commit. Users can configure this as a required
external approval in merge request approval rules.
When a PR triggers a code review, create a GitHub Check Run in
'queued' state so the PR immediately shows a pending Kilo Code
Review check. Skipped for lite (read-only) app installations.
Non-blocking — review still proceeds if check creation fails.
When an MR triggers a code review, set a 'pending' commit status so
the MR immediately shows a kilo/code-review check. Uses the existing
Project Access Token flow. Non-blocking — review proceeds if status
creation fails.
When the code review status changes (running, completed, failed,
cancelled), update the corresponding GitHub Check Run or GitLab
commit status. Maps review states to platform-specific statuses
with descriptive output. Non-blocking — status callback succeeds
even if the gate check update fails.
When enabled, the PR gate check will fail if the review finds
blocking issues. Defaults to false (system-failure-only mode).
The strict_gate behavior requires the cloud agent to report a
gate_result in its completion callback (future enhancement).
- Remove unnecessary 'as GitHubAppType' cast in PR handler
- Clear check_run_id on review retry so a fresh check run is created
- Use numeric platform_project_id for GitLab commit status updates
- Deduplicate getIntegrationById call in status callback (was called
  twice for terminal states — once for gate check, once for reactions)
- Fix indentation from removed inner if-block
# Conflicts:
#	packages/db/src/migrations/meta/0045_snapshot.json
#	packages/db/src/migrations/meta/_journal.json
@kilo-code-bot
Copy link
Contributor

kilo-code-bot bot commented Mar 6, 2026

Code Review Summary

Status: 1 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
cloud-agent/src/router/handlers/session-init.ts 326 v1 callback never reads gateResult from wrapped kilocode events
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

None.

Files Reviewed (37 files)
  • cloud-agent-next/src/callbacks/types.ts - 0 issues
  • cloud-agent-next/src/persistence/CloudAgentSession.ts - 0 issues
  • cloud-agent-next/src/persistence/schemas.ts - 0 issues
  • cloud-agent-next/src/persistence/types.ts - 0 issues
  • cloud-agent-next/src/router/handlers/session-prepare.ts - 0 issues
  • cloud-agent-next/src/router/schemas.ts - 0 issues
  • cloud-agent-next/src/session/ingest-handlers/execution-lifecycle.ts - 0 issues
  • cloud-agent-next/src/session/types.ts - 0 issues
  • cloud-agent-next/src/shared/protocol.ts - 0 issues
  • cloud-agent-next/src/websocket/ingest.ts - 0 issues
  • cloud-agent/src/callbacks.ts - 0 issues
  • cloud-agent/src/callbacks/types.ts - 0 issues
  • cloud-agent/src/persistence/CloudAgentSession.ts - 0 issues
  • cloud-agent/src/persistence/schemas.ts - 0 issues
  • cloud-agent/src/persistence/types.ts - 0 issues
  • cloud-agent/src/router/handlers/session-init.ts - 1 issue
  • cloud-agent/src/router/handlers/session-prepare.ts - 0 issues
  • cloud-agent/src/router/schemas.ts - 0 issues
  • cloud-agent/src/session/ingest-handlers/execution-lifecycle.ts - 0 issues
  • cloud-agent/src/session/types.ts - 0 issues
  • cloud-agent/src/shared/protocol.ts - 0 issues
  • cloud-agent/src/websocket/ingest.ts - 0 issues
  • cloudflare-code-review-infra/src/types.ts - 0 issues
  • packages/db/src/schema-types.ts - 0 issues
  • packages/db/src/schema.ts - 0 issues
  • src/app/api/internal/code-review-status/[reviewId]/route.ts - 0 issues
  • src/components/code-reviews/ReviewConfigForm.tsx - 0 issues
  • src/lib/cloud-agent-next/cloud-agent-client.ts - 0 issues
  • src/lib/code-reviews/db/code-reviews.ts - 0 issues
  • src/lib/code-reviews/triggers/prepare-review-payload.ts - 0 issues
  • src/lib/integrations/platforms/github/adapter.ts - 0 issues
  • src/lib/integrations/platforms/github/webhook-handlers/pull-request-handler.ts - 0 issues
  • src/lib/integrations/platforms/gitlab/adapter.ts - 0 issues
  • src/lib/integrations/platforms/gitlab/webhook-handlers/merge-request-handler.ts - 0 issues
  • src/routers/code-reviews-router.ts - 0 issues
  • src/routers/code-reviews/code-reviews-router.ts - 0 issues
  • src/routers/organizations/organization-code-reviews-router.ts - 0 issues

- Guard mapStatusToCheckRun against unsupported statuses by returning
  null instead of silently mapping to 'completed' without a conclusion
- Resolve GitLab access token once and share it between the gate check
  update and the reaction/footer block to avoid duplicate token refreshes
- Extract getGitLabInstanceUrl and resolveGitLabAccessToken helpers to
  reduce duplication
- Add Number.isSafeInteger guard in updateCheckRun to fail loudly if
  a check run ID ever exceeds JS safe integer range
- Use BigInt.toString() explicitly in PR handler log for consistency
Move the gate check update (GitHub Check Run / GitLab commit status)
before writing the terminal status to the database. This prevents a
race where a flaky GitHub/GitLab API call leaves the gate stale with
no retry path, since subsequent callbacks hit the terminal-state
early-return. Also report terminal gate failures to Sentry.
After resetCodeReviewForRetry() clears check_run_id, the retrigger
flow now creates a fresh GitHub Check Run (or GitLab commit status)
so that subsequent status callbacks can update the gate. Without this,
the stale failed/cancelled check would permanently block merging on
repos with required status checks.
If createCheckRun() succeeds on GitHub but updateCheckRunId() fails
to persist the ID in Postgres, the check run becomes orphaned — no
subsequent status callback can update it. On repos with Kilo Code
Review as a required check, this permanently blocks merging. Now we
detect this partial failure and cancel the orphaned check run.
If recreatePRGateCheck() creates a check run on GitHub but fails to
persist the ID via updateCheckRunId(), cancel the orphaned check run
so it doesn't permanently block merging on repos with required checks.
Re-throws the DB error so the outer catch logs it.
…ings

Accept an optional gateResult ('pass' | 'fail') field in the status
callback payload from the orchestrator or cloud-agent-next. When the
review completes but gateResult is 'fail' (agent found blocking
issues with strict_gate enabled), the GitHub Check Run conclusion is
set to 'failure' and GitLab commit status to 'failed' instead of
'success'. This lets repos with required checks block merging when
the review finds problems.
…fig schema

The new gate_threshold field supports 'off', 'all', 'warning', and 'critical'
levels, giving users granular control over when the PR gate check fails
based on review findings.
Wire gate_threshold through SaveReviewConfigInputSchema, getReviewConfig
response (defaulting to 'off'), and the saveReviewConfig mutation.
Mirror the personal router changes: add gateThreshold to the org
SaveReviewConfigInputSchema, getReviewConfig, and saveReviewConfig.
Add a Select control with four options (off, all, warning, critical),
local state, hydration from server config, and wiring to both org
and personal save mutations.
Unexpected gateResult values are logged and normalized to undefined
so only 'pass' or 'fail' reach the gate check update logic.
The setting has no effect on the legacy v1 agent, so hide it when
cloud-agent-next is not available to avoid user confusion.
Copy link
Contributor

@jeanduplessis jeanduplessis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm provisionally approving, but highly recommend updating the "Off" label.
Though (non-blocking): Do we maybe want to put this feature behind a feature flag and test it our ourselves first before rolling it out to everyone?

…outer

Add isPrGateFlagEnabled to the existing Promise.all that fetches
the agent config and cloud-agent-next flag. Include isPrGateEnabled
in both the default and loaded config response paths so the frontend
can read it.
Parallelize both flag checks via Promise.all (replacing the sequential
await for cloud-agent-next). Include isPrGateEnabled in both the
default and loaded config response paths. Uses the same
botUserId ?? ctx.user.id distinctId as the existing flag.
Extract isPrGateEnabled from configData (propagated from the router
changes) and use it instead of isCloudAgentNextEnabled to control
visibility of the PR Gate Threshold dropdown. When hidden, the
gateThreshold stays at its default 'off'.
Skip createCheckRun() when the code-review-pr-gate flag is disabled
for the owner. Uses owner.userId as the PostHog distinctId, matching
the pattern used in config routers.
Wrap the setCommitStatus('pending') call inside the PrAT block with
an isPrGateEnabled check. The eyes reaction remains ungated since
it is unrelated to the PR gate feature.
Defense-in-depth: force gateThreshold to 'off' in the session input
when the flag is disabled. Prevents a stale non-'off' config value
from activating gating after the flag is turned off.
Move owner construction above the recreatePRGateCheck call and gate
it behind the code-review-pr-gate flag. Without this, retriggering
a review when the flag is off would create orphaned pending check
runs that permanently block merges on repos with required checks.
…to callback

enqueueCallbackNotification never included gateResult in the callback
payload, making gate failures impossible on the v2 backend. Thread
gateResult from the complete event through the execution lifecycle
chain (ingest → handleExecutionComplete → updateExecutionStatus →
enqueueCallbackNotification → callback payload) so the code-review-
status endpoint can receive it.
invokeCallback call sites in session-init.ts only passed sessionId,
status, and errorMessage — gateResult was never included. Thread
gateResult through both the queue-based callback path
(enqueueCallbackNotification) and the direct SSE streaming path
(invokeCallback in session-init.ts) so gate failures can be reported
on the v1 backend.
@alex-alecu alex-alecu merged commit b0d87c4 into main Mar 9, 2026
17 checks passed
@alex-alecu alex-alecu deleted the feat/review-as-pr-gate branch March 9, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants