Skip to content

fix(github): pin pending installation selection and cancel to one request - #8570

Merged
iscekic merged 4 commits into
mainfrom
drain/github-deterministic-pending-installations
Oct 22, 2026
Merged

iscekic merged 4 commits into
mainfrom
drain/github-deterministic-pending-installations

Conversation

@iscekic

@iscekic iscekic commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Evidence

Evidence: demonstrated — database-backed regression tests and authenticated requests to the running local web app verified deterministic pending selection and exact-row cancellation, with PostgreSQL reads checking the effects.

Follow-up verification

On the merged branch, using an owned fake account, generated development credentials, and disposable local PostgreSQL:

  • Two pending requests with equal creation timestamps: githubApps.checkUserPendingInstallation returned the descending integration ID.
  • Cancelling without integrationId returned HTTP 400 BAD_REQUEST; both rows remained.
  • Cancelling the returned ID succeeded, deleted exactly that row, and the next lookup returned the remaining request.
  • Repeating cancellation of the deleted ID returned HTTP 404 NOT_FOUND; the remaining request was unchanged.
  • NODE_ENV=test pnpm exec jest --runInBand --runTestsByPath src/lib/integrations/github-apps-service.test.ts src/routers/github-apps-router.test.ts src/app/api/integrations/github/callback/route.test.ts from apps/web: 3 suites, 104 tests passed.
  • GOMEMLIMIT=2GiB pnpm exec tsgo --noEmit --checkers 1 --tsBuildInfoFile .tsbuildinfo/tsgo.tsbuildinfo from apps/web: passed.
  • Targeted oxfmt and oxlint on the resolved service and its regression test: passed.
  • pnpm install --frozen-lockfile: passed.

This follow-up exercised the HTTP API rather than the visual surface; no new screenshot is claimed. The initial session also exercised the web page's Cancel Request confirmation and observed deletion of only the displayed row. No provider HTTP request is required by these database-only paths; GitHub, GitLab, and Bitbucket provider behavior remains unproven by this run.

Change

  • findPendingInstallationByKiloUserId orders by created_at DESC, id DESC and checkUserPendingInstallation returns pendingIntegrationId identifying that response's row.
  • cancelPendingInstallation(owner, integrationId) requires an ID and scopes it to a pending GitHub request belonging to that owner. The router rejects omitted IDs, and the web cancellation passes the displayed row's ID. The organization caller already supplied its rendered ID.
  • Removed the uncalled findPendingInstallationByRequesterId; removed its stale test mock entry.
  • Regenerated the MCP catalog so its cancellation schema requires integrationId.
  • Resolved the conflict with main by retaining both the required ID and main's shared ownerCondition(owner) predicate.
  • Added a database regression for equal creation timestamps, including selection of the next request after cancelling the selected row.

Review decisions and limits

  • The test-mock remark was fixed: the router test distinguishes the requested integration ID from another integration belonging to the same owner.
  • The ordering suggestion was rejected per the proxy's decision. checkUserPendingInstallation is user-wide and newest-first; getInstallation is owner-scoped and ranks connection health. Each response identifies its own row, and cancellation targets that explicit ID. This PR does not change connection-health precedence.
  • Existing timestamps require no migration or backfill.

Proxy answers

The owner's proxy gave these answers in the owner's place.

  1. Question: Cancel contract: cancelPendingInstallation can currently be called with no integrationId and deletes an arbitrary pending row for the owner. How should the fixed contract look?
    Answer: Require integrationId
    Confidence: 0.85 · Precedent: Replace old paths instead of keeping them alongside the new path: session ad6a3f589364, "alongside? why not instead?"; the brief allows removing the unscoped fallback.
  2. Question: Chosen pending row: When a user has several pending GitHub installation requests, which one should the pending-state lookup return?
    Answer: Newest first (created_at DESC, id DESC)
    Confidence: 0.90 · Precedent: none
  3. Question: Adjacent lookup: findPendingInstallationByRequesterId in the same file has the same unordered .limit(1) shape but has no callers anywhere in the repository (only a jest mock lists it). What should this PR do with it?
    Answer: Delete it
    Confidence: 0.60 · Precedent: Delete obsolete code freely, since git can restore an overdeletion: session 2f71af3a787e, "delete all you can, we can restore from git later if we overdelete"; also fix the class of the issue.
  4. Question: Ordering surfaces: Kilobot says the new pending ordering disagrees with getInstallation (which renders the page's pending alert): with two pending rows, the page can display one request while checkUserPendingInstallation names the other. How should I resolve it?
    Answer: Keep them distinct, reject the remark
    Confidence: 0.62 · Precedent: Git and pull requests: 'Run the standard PR loop: check each kilobot comment, fix it or reject it' (session 80d037e14f3a); 'Keep each PR diff to files that the feature needs' (session df491946927a).

…uest

A user can hold several pending GitHub App installation requests, one per
GitHub target. Both the pending-state lookup and the cancel path picked one
of those rows without an order, so a resumed view could describe a different
request than the one shown and an unscoped cancel could delete any of them.

Order the requester lookup by created_at DESC, id DESC; require the
integration id when cancelling and delete exactly that row; return the
pending row id from checkUserPendingInstallation so clients and agents can
name it. Delete the unused findPendingInstallationByRequesterId, which had
the same unordered shape and no callers. Regenerate the MCP catalog for the
now-required input.
@iscekic iscekic added drain Work item from the findings drain janitor:features-workflows Janitor finding family: features-workflows labels Oct 22, 2026
@kilo-code-bot

kilo-code-bot Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The only incremental code change is a database-backed test for equal pending created_at values, and it matches the created_at DESC, id DESC lookup plus the id-pinned cancel with no new issues found.

Assumptions: [INFERENCE] Jest suites were not executed in this read-only review; the new test's assumption that Postgres uuid ordering matches canonical hex string ordering was verified by reading the column type (packages/db/src/schema.ts:3252) and the query, not by running it. The two pending surfaces remain deliberately distinct (user-wide newest-first vs owner-scoped health order), but both are now deterministic and every cancel names its row id, so no further code change is required for this fix.

Files Reviewed (3 files)
  • apps/web/src/lib/integrations/github-apps-service.test.ts - 0 issues (new tie-break test; equal timestamps resolve by descending id, then cancel deletes exactly the named row)
  • apps/web/src/lib/integrations/db/platform-integrations.ts - 0 issues (re-verified: ordering is deterministic; earlier mismatch concern is not a defect now that every cancel is pinned to the id the caller was given)
  • apps/web/src/routers/github-apps-router.test.ts - 0 issues (re-verified: recording mock distinguishes the requested id, so the NOT_FOUND case catches an owner-only lookup regression)
Previous Review Summaries (2 snapshots, latest commit f0726e3)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit f0726e3)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The incremental commit strengthens the NOT_FOUND test so it now proves the router matches the requested integration id; one previously reported mismatch between the pending lookup and the installation view remains open.

Assumptions: [INFERENCE] I did not execute the Jest suites in this read-only review, so the new mock wiring was verified by reading the test and router source only.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
apps/web/src/lib/integrations/db/platform-integrations.ts 886 findPendingInstallationByKiloUserId returns the newest created_at, but getInstallation (which renders the pending alert and page cancel button) still selects organizations by oldest created_at (github-apps-service.ts:57-63) and personal owners by personalGitHubIntegrationOrder() (desc(updated_at)). With two pending requests for one owner, checkUserPendingInstallation().pendingIntegrationId can name a different request than the one the page displays, so an agent/client following the "read pendingIntegrationId then cancel" flow can withdraw the other request.
Files Reviewed (2 files)
  • apps/web/src/routers/github-apps-router.test.ts - 0 issues (previous mock finding resolved; the new mockGetGitHubIntegrationById now distinguishes the requested id and the NOT_FOUND test fails if the handler regresses to an owner-only lookup)
  • apps/web/src/lib/integrations/db/platform-integrations.ts - 1 issue (re-verified at current HEAD, unchanged)

Fix these issues in Kilo Cloud

Previous review (commit fd86322)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2
Issue Details (click to expand)

SUGGESTION

File Line Issue
apps/web/src/lib/integrations/db/platform-integrations.ts 886 The new newest-created_at ordering disagrees with getInstallation, which still picks the row the page displays (oldest created for organizations, updated_at for personal owners), so pendingIntegrationId can name a different request than the pending alert/cancel button.
apps/web/src/routers/github-apps-router.test.ts 789 The getGitHubIntegrationById mock discards the integration id, so the NOT_FOUND test proves only that the owner has no integration and would not catch an owner-only lookup regression.
Files Reviewed (8 files)
  • apps/web/src/app/api/integrations/github/callback/route.test.ts - 0 issues
  • apps/web/src/components/integrations/GitHubIntegrationDetails.tsx - 0 issues
  • apps/web/src/lib/integrations/db/platform-integrations.ts - 1 issue
  • apps/web/src/lib/integrations/github-apps-service.test.ts - 0 issues
  • apps/web/src/lib/integrations/github-apps-service.ts - 0 issues
  • apps/web/src/routers/github-apps-router.test.ts - 1 issue
  • apps/web/src/routers/github-apps-router.ts - 0 issues
  • services/kilo-mcp/catalog.json - 0 issues

Reviewed by deepseek-flash-latest · Input: 73K · Output: 13.6K · Cached: 1.1M

Review guidance: REVIEW.md from base branch main

@iscekic iscekic added merge:blocked:kilobot-not-ready Review bot has not recommended merge at this head merge:human Substantive functional risk requires human review. labels Oct 22, 2026
…istic-pending-installations

# Conflicts:
#	apps/web/src/lib/integrations/github-apps-service.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

drain Work item from the findings drain janitor:features-workflows Janitor finding family: features-workflows merge:human Substantive functional risk requires human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants