Files
orca/src/shared/hosted-review.ts
Brennan BensonandMerge Sim b5a85890ac perf(git): bound git subprocess execution with an atomic admission scheduler (#16874)
* perf(git): bound git subprocess execution with an atomic admission scheduler

Field traces (#16038, #11363) show Windows freeze storms driven by unbounded
concurrent git children (12+ at once, 50-65s status convoys for 25+ minutes).
Admit every main-process git child against atomic per-budget base+headroom
counters (general / network / per-route), with reserved interactive capacity,
ordering-only aging, close-bound permit release, a 120s fail-safe read timeout
that feeds scheduler backoff, tier plumbing through every option carrier, and
coalesced+jittered visibility pollers. Killswitch: ORCA_GIT_ADMISSION_DISABLED=1.

Storm harness A/B: max concurrent children 65 -> 6, interactive p95 791ms -> 88ms;
output-parity battery byte-identical with admission on vs off.

* test(git): run the admission output-parity battery on every platform

Parity needs real git, not the storm harness's PATH stub, so it must not share
that file's POSIX gate - Windows is the platform where parity evidence matters.

* fix(git): preserve interactive admission invariants

* perf(git): keep admission queue drains linear

* fix(git): close final admission gaps

* perf(git): bound eligible route selection

* fix(merge): remove unrelated stale snapshot changes

* fix(git): preserve refresh lifecycle authority

* test(git): align admission lifetime contracts

* fix(git): harden admission across runtime paths

* fix(git): restore freshness for bulk status reads

* test(git): repoint delete-dialog source pins after admission plumbing

The hydration effect now orders its targets through
orderDeleteWorktreeStatusHydrationTargets and passes includeLineStats
alongside the abort signal, so both literal anchors stopped matching.
The invariants are unchanged and still pinned: dropping the signal, the
main-worktree/folder filter, or getState-instead-of-subscribe each
still reddens this test.

* Fix git admission tier propagation and lock ordering

Decode optional Git status tiers permissively and default runtime RPC status reads to the status lane while preserving renderer caller intent.

Acquire the FETCH_HEAD mutex before atomic admission so same-repository fetch waiters hold no global or route permits.

Preserve automatic pull-request refresh reasons, keep explicit hosted-review refreshes interactive, remove the dead candidate tier, and keep relay scheduling unchanged.

Use tier-aware status lease keys because a shared lease cannot be safely promoted after its admission request is queued or granted.

* test: align expectations with admission plumbing

* refactor(child-process): move the process contract types to process-spec

run-process.ts crossed its line cap after gaining the termination observer;
the public types and defaults move out with re-exports so no caller changes.

* chore: restore pnpm-lock.yaml to main (unintended local drift)

---------

Co-authored-by: Merge Sim <sim@local>
2026-08-30 14:19:05 -07:00

211 lines
5.9 KiB
TypeScript

import type {
CheckStatus,
GitHubRepositoryIdentity,
PRConflictSummary,
PRMergeableState,
PRReviewDecision
} from './github/pull-request-types'
export type HostedReviewProvider =
| 'github'
| 'gitlab'
| 'bitbucket'
| 'azure-devops'
| 'gitea'
| 'unsupported'
export type HostedReviewState = 'open' | 'closed' | 'merged' | 'draft'
// Why: Bitbucket Cloud's API has no draft pull requests, so offering the toggle
// there would either publish a live PR or fail at submit.
export function hostedReviewProviderSupportsDraft(provider: HostedReviewProvider): boolean {
return provider !== 'bitbucket'
}
/** A linked review is identified by a positive integer PR/MR number. */
export function isPositiveHostedReviewNumber(value: unknown): value is number {
return typeof value === 'number' && Number.isInteger(value) && value > 0
}
export type HostedReviewInfo = {
provider: HostedReviewProvider
number: number
title: string
state: HostedReviewState
url: string
status: CheckStatus
updatedAt: string
mergeable: PRMergeableState
reviewDecision?: PRReviewDecision | null
autoMergeEnabled?: boolean
autoMergeAllowed?: boolean | null
mergeQueueRequired?: boolean | null
mergeStateStatus?: string | null
headSha?: string
/** GitHub repository that owns the PR; absent on older runtimes and other providers. */
githubRepository?: GitHubRepositoryIdentity
// Why: mirrors PRInfo.confirmedContainedHeadOid so merged-review staleness
// checks accept a worktree head confirmed to be part of the merged PR.
confirmedContainedHeadOid?: string
/** Target branch name for review-created worktree compare-base repair. */
baseRefName?: string
conflictSummary?: PRConflictSummary
}
export type HostedReviewForBranchArgs = {
repoPath: string
repoId?: string
admissionTier?: 'interactive' | 'status' | 'background'
/** Desktop IPC-only owner guard; runtime RPC callers omit this field. */
repoOwnerExecutionHostId?: string
branch: string
linkedGitHubPR?: number | null
fallbackGitHubPR?: number | null
linkedGitLabMR?: number | null
linkedBitbucketPR?: number | null
linkedAzureDevOpsPR?: number | null
linkedGiteaPR?: number | null
// The worktree's checked-out HEAD oid (GitHub merged-at-head visibility).
currentHeadOid?: string | null
/**
* Set only by surfaces scoped to the selected worktree. That tier is O(1), so
* the host re-checks it per minute; the worktree list is O(N) and is paced far
* more slowly to stay inside the shared API budget (#11532).
*/
active?: boolean
}
export type HostedReviewSummary = {
number?: number
url: string
}
export type CreateHostedReviewInput = {
provider: HostedReviewProvider
base: string
head?: string
title: string
body?: string
draft?: boolean
worktreePath?: string
useTemplate?: boolean
}
export type CreateHostedReviewArgs = CreateHostedReviewInput & {
repoPath: string
repoId?: string
connectionId?: string | null
}
export type CreateStackedHostedReviewInput = CreateHostedReviewInput
export type CreateStackedHostedReviewArgs = CreateStackedHostedReviewInput & {
repoPath: string
repoId?: string
connectionId?: string | null
}
export type CreateHostedReviewErrorCode =
| 'auth_required'
| 'unsupported_provider'
| 'already_exists'
| 'validation'
| 'timeout'
| 'unknown_completion'
| 'push_failed'
| 'unknown'
export type CreateHostedReviewResult =
| { ok: true; number: number; url: string }
| {
ok: false
code: CreateHostedReviewErrorCode
error: string
existingReview?: HostedReviewSummary
}
export type CreateStackedHostedReviewResult =
| {
ok: true
number: number
url: string
stackNumber: number
parentReview: HostedReviewSummary
}
| {
ok: false
code: CreateHostedReviewErrorCode
error: string
createdReview?: HostedReviewSummary
}
export type HostedReviewCreationBlockedReason =
| 'dirty'
| 'detached_head'
| 'default_branch'
| 'no_upstream'
| 'needs_push'
| 'needs_sync'
| 'auth_required'
| 'fork_head_unsupported'
| 'unsupported_provider'
| 'existing_review'
// Why: a stacked worktree's local-only parent base is unresolvable on the
// remote; blocked at create-time so the submit fails with actionable copy
// instead of the provider's opaque error.
| 'base_not_on_remote'
| null
export type HostedReviewCreationNextAction =
| 'commit'
| 'publish'
| 'push'
| 'sync'
| 'authenticate'
| 'open_existing_review'
| null
/**
* Records whether the eligibility result observed an authoritative existing-review
* lookup. `found` / `not_found` come only from an accepted provider lookup;
* `unavailable` marks a local-blocker fallback returned after a swallowed or
* skipped lookup, so it can never masquerade as authoritative no-review evidence.
*/
export type HostedReviewLookupOutcome = 'found' | 'not_found' | 'unavailable'
export type HostedReviewCreationEligibility = {
provider: HostedReviewProvider
review: HostedReviewSummary | null
canCreate: boolean
blockedReason: HostedReviewCreationBlockedReason
nextAction: HostedReviewCreationNextAction
reviewLookupOutcome: HostedReviewLookupOutcome
defaultBaseRef?: string | null
head?: string | null
title?: string | null
body?: string | null
/** Present only when the executing host supports GitHub stack creation. */
stackedCreationSupported?: boolean
}
export type HostedReviewCreationEligibilityArgs = {
repoPath: string
repoId?: string
worktreePath?: string
connectionId?: string | null
branch: string
base?: string | null
hasUncommittedChanges?: boolean
hasUpstream?: boolean
ahead?: number
behind?: number
linkedGitHubPR?: number | null
fallbackGitHubPR?: number | null
linkedGitLabMR?: number | null
linkedBitbucketPR?: number | null
linkedAzureDevOpsPR?: number | null
linkedGiteaPR?: number | null
}
export type HostedReviewDecision = 'approved' | 'changes_requested' | 'review_required' | null