@codraoss/core 0.9.4 → 0.9.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-XD6CGYHO.js → chunk-4XEMXRNG.js} +62 -39
- package/dist/chunk-4XEMXRNG.js.map +1 -0
- package/dist/{chunk-ILZUCIZH.js → chunk-EQL5OOEF.js} +7 -7
- package/dist/chunk-EQL5OOEF.js.map +1 -0
- package/dist/{chunk-RQ5S67XB.js → chunk-FKRCBUWZ.js} +8 -2
- package/dist/chunk-FKRCBUWZ.js.map +1 -0
- package/dist/{chunk-JC74XH7Q.js → chunk-GDX2ZEBA.js} +1 -1
- package/dist/chunk-GDX2ZEBA.js.map +1 -0
- package/dist/{chunk-OIQRTDYR.js → chunk-I5S3NBFL.js} +20 -4
- package/dist/chunk-I5S3NBFL.js.map +1 -0
- package/dist/{chunk-U3VKVDZ7.js → chunk-IKVUW4WC.js} +92 -19
- package/dist/chunk-IKVUW4WC.js.map +1 -0
- package/dist/{chunk-R2HRWFRH.js → chunk-JAF7JIZQ.js} +1 -1
- package/dist/chunk-JAF7JIZQ.js.map +1 -0
- package/dist/{chunk-V3RF6GQW.js → chunk-KTDBDKL5.js} +1 -1
- package/dist/chunk-KTDBDKL5.js.map +1 -0
- package/dist/{chunk-MMVQPGK3.js → chunk-ONJZXUQT.js} +112 -24
- package/dist/chunk-ONJZXUQT.js.map +1 -0
- package/dist/{chunk-PVXFURDR.js → chunk-QPPLO2YV.js} +2 -2
- package/dist/chunk-QPPLO2YV.js.map +1 -0
- package/dist/{chunk-FGXA7JNR.js → chunk-W3RY755V.js} +14 -2
- package/dist/chunk-W3RY755V.js.map +1 -0
- package/dist/{chunk-NSTV4TRP.js → chunk-XPYVLHSX.js} +1 -1
- package/dist/chunk-XPYVLHSX.js.map +1 -0
- package/dist/{chunk-WSK45HW4.js → chunk-Z5B5X7QP.js} +1 -1
- package/dist/chunk-Z5B5X7QP.js.map +1 -0
- package/dist/claim-checks.d.ts +8 -13
- package/dist/claim-checks.js +2 -2
- package/dist/diff/index.d.ts +3 -3
- package/dist/diff/index.js +2 -1
- package/dist/fingerprint.js +1 -1
- package/dist/formatter-CDFpqbDP.d.ts +24 -0
- package/dist/formatter.d.ts +29 -0
- package/dist/formatter.js +111 -0
- package/dist/formatter.js.map +1 -0
- package/dist/{index-BTDWXDck.d.ts → index-CrtHz4vN.d.ts} +3 -1
- package/dist/index.d.ts +12 -7
- package/dist/index.js +239 -62
- package/dist/index.js.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/{model-gRDTk8yk.d.ts → model-BkqfyVh9.d.ts} +27 -6
- package/dist/model-output/index.d.ts +2 -2
- package/dist/model-output/index.js +9 -9
- package/dist/ports/index.d.ts +22 -18
- package/dist/ports/index.js +1 -1
- package/dist/{position-h_jdn6ZH.d.ts → position-zZl8KGVu.d.ts} +1 -1
- package/dist/prompts/file-review.d.ts +3 -3
- package/dist/prompts/file-review.js +11 -5
- package/dist/prompts/languages.d.ts +2 -1
- package/dist/prompts/languages.js +5 -3
- package/dist/prompts/verify.d.ts +1 -1
- package/dist/prompts/verify.js +1 -1
- package/dist/rules/detect.d.ts +1 -1
- package/dist/rules/detect.js +5 -5
- package/dist/rules/table.js +1 -1
- package/dist/token-tracker.js +2 -2
- package/package.json +10 -2
- package/dist/chunk-FGXA7JNR.js.map +0 -1
- package/dist/chunk-ILZUCIZH.js.map +0 -1
- package/dist/chunk-JC74XH7Q.js.map +0 -1
- package/dist/chunk-MMVQPGK3.js.map +0 -1
- package/dist/chunk-NSTV4TRP.js.map +0 -1
- package/dist/chunk-OIQRTDYR.js.map +0 -1
- package/dist/chunk-PVXFURDR.js.map +0 -1
- package/dist/chunk-R2HRWFRH.js.map +0 -1
- package/dist/chunk-RQ5S67XB.js.map +0 -1
- package/dist/chunk-U3VKVDZ7.js.map +0 -1
- package/dist/chunk-V3RF6GQW.js.map +0 -1
- package/dist/chunk-WSK45HW4.js.map +0 -1
- package/dist/chunk-XD6CGYHO.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/review/index.ts","../src/review/request.ts","../src/review/diff-cache.ts","../src/review/budget.ts","../src/review/pack.ts","../src/review/phase-control.ts","../src/review/retry-policy.ts","../src/review/file-runner.ts","../src/review/bin-runner.ts","../src/finding-gates.ts","../src/review/prepare.ts","../src/review/phase.ts","../src/review/finalize.ts","../src/review/telemetry.ts","../src/review/gate-pipeline.ts"],"sourcesContent":["import { logger } from '../logger';\r\nimport { type WebhookPayload, type ChangeRequestWebhookPayload } from '@codraoss/schema/webhook';\r\nimport { REVIEW_CONCURRENCY_LIMITS, type ReviewJobMessage } from '@codraoss/schema';\r\nimport type { ReviewGitProvider, ReviewRuntime } from '../ports';\r\nimport { extractReviewRequest } from './request';\r\n\r\nexport { getDiffFiles, getOrFetchRawDiffForCompletedJob } from './diff-cache';\r\n\r\nexport { budgetAwareFileLimit, estimatedSubrequestsPerFile } from './budget';\r\n\r\nexport {\r\n narrowUnit,\r\n planReviewUnits,\r\n unitFiles,\r\n type LedgerEntry,\r\n type ReviewUnit,\r\n} from './pack';\r\n\r\nexport {\r\n BIN_DIFF_CHAR_BUDGET,\r\n BIN_MAX_FILES,\r\n BIN_TARGET_DIFF_LINES,\r\n PACKABLE_MAX_DIFF_LINES,\r\n} from '../constants';\r\n\r\nexport { proportionalSplit } from './bin-runner';\r\n\r\nexport { verifyFindings, type VerifyDrop, type VerifyOutcome } from '../finding-gates';\r\n\r\nexport { extractReviewRequest, type ReviewRequest } from './request';\r\n\r\n// workflows/review.ts floors its inter-phase sleep here; the eslint barrel guard stops it\r\nexport { FRESH_INVOCATION_YIELD_SECONDS } from '../constants';\r\n\r\nimport {\r\n type PersistedReviewJob,\r\n NextPhaseError,\r\n failJobAndCheckRun,\r\n} from './phase-control';\r\nimport {\r\n BUSY_RETRY_SECONDS,\r\n FRESH_INVOCATION_YIELD_SECONDS,\r\n JOB_LEASE_SECONDS,\r\n MAX_FINALIZE_CONTINUATIONS,\r\n MAX_JOB_CONTINUATIONS,\r\n} from '../constants';\r\nimport { getRetryableModelFailureDelaySeconds, isAwaitingAsyncReview, isSubrequestBudgetError } from './retry-policy';\r\nimport { persistFailedFileReview } from './file-runner';\r\nimport { runPreparePhase } from './prepare';\r\nimport { runReviewPhase } from './phase';\r\nimport { runFinalizePhase } from './finalize';\r\n\r\nexport { NextPhaseError, failJobAndCheckRun };\r\n\r\nexport type ReviewJobRunResult =\r\n | { action: 'ack' }\r\n | { action: 'retry'; delaySeconds: number }\r\n | { action: 'next_phase'; phase: 'prepare' | 'review' | 'finalize'; delaySeconds: number; jobId?: string; freshInstance?: boolean };\r\n\r\n/**\r\n * The engine's entrypoint. Runs EXACTLY ONE phase of a review job and returns what the caller should\r\n * do next; the caller owns the loop.\r\n *\r\n * Deliberately not a loop. Every `next_phase` result exists because the next phase needs a fresh\r\n * host invocation to get a clean subrequest budget, and only the driver can hibernate long enough to\r\n * produce one (see FRESH_INVOCATION_YIELD_SECONDS in ./phase-control). A loop in here would run the\r\n * next phase on the current one's spent budget while its TokenTracker restarted at zero -- the exact\r\n * failure that constant was introduced to fix.\r\n *\r\n * Contract for a driver:\r\n * - 'ack': the job is finished or not ours. Stop.\r\n * - 'retry': re-deliver the SAME message after `delaySeconds`. Admission was throttled or the lease\r\n * is held elsewhere; no work happened.\r\n * - 'next_phase': re-invoke with `{ jobId, phase }` after `delaySeconds`. `freshInstance` means the\r\n * delay must be long enough to actually hibernate, not merely to wait.\r\n *\r\n * Safe to call repeatedly for the same job: it claims a lease first, and every phase is idempotent\r\n * enough to resume. It throws only on a programming error -- job failures are recorded and acked.\r\n */\r\nexport async function runReview(env: ReviewRuntime, message: ReviewJobMessage): Promise<ReviewJobRunResult> {\r\n const resolved = await resolveQueuedJob(env, message);\r\n if (!resolved) {\r\n return { action: 'ack' };\r\n }\r\n\r\n if (resolved.job.status === 'queued') {\r\n const { concurrencyLevel } = await env.settings.getReviewSettings();\r\n const maxConcurrentJobs = REVIEW_CONCURRENCY_LIMITS[concurrencyLevel];\r\n const runningCount = await env.jobs.getOtherRunningJobsCount(resolved.job.id);\r\n if (runningCount >= maxConcurrentJobs) {\r\n logger.info(`Throttling admission of job ${resolved.job.id}: ${runningCount} other jobs are currently running.`);\r\n return { action: 'retry', delaySeconds: 30 };\r\n }\r\n }\r\n\r\n const leaseOwner = env.ids.randomUUID();\r\n const claim = await env.jobs.claimJobLease(resolved.job.id, leaseOwner, JOB_LEASE_SECONDS);\r\n if (claim.status === 'missing') {\r\n logger.warn(`Job not found for processing: ${resolved.job.id}`);\r\n return { action: 'ack' };\r\n }\r\n if (claim.status === 'terminal') {\r\n logger.info(`Job ${resolved.job.id} is already terminal (${claim.row.status}), acking queue delivery.`);\r\n return { action: 'ack' };\r\n }\r\n if (claim.status === 'busy') {\r\n logger.info(`Job ${resolved.job.id} has a fresh lease; retrying queue delivery later.`);\r\n return { action: 'retry', delaySeconds: Math.min(BUSY_RETRY_SECONDS, claim.retryAfterSeconds) };\r\n }\r\n\r\n const job = env.jobs.mapJob(claim.row);\r\n\r\n if (message.workflowInstanceId && job.workflowInstanceId !== message.workflowInstanceId) {\r\n try {\r\n await env.jobs.setJobWorkflowInstance(job.id, message.workflowInstanceId);\r\n } catch (error) {\r\n logger.warn(`Failed to bind workflow instance id for job ${job.id}`, error instanceof Error ? error : new Error(String(error)));\r\n }\r\n }\r\n\r\n const phase = resolved.phase;\r\n const tracker = env.createTokenTracker();\r\n const github = env.createGitHub(job.installationId, tracker);\r\n const model = env.createModel(job.id, tracker);\r\n const formatter = env.createFormatter();\r\n\r\n try {\r\n if (phase === 'prepare') {\r\n await runPreparePhase(env, job, leaseOwner, github);\r\n } else if (phase === 'finalize') {\r\n await runFinalizePhase(env, job, leaseOwner, github, formatter, model);\r\n } else {\r\n await runReviewPhase(env, job, leaseOwner, github, model, tracker);\r\n }\r\n\r\n await env.jobs.releaseJobLease(job.id, leaseOwner);\r\n return { action: 'ack' };\r\n } catch (error) {\r\n const messageText = error instanceof Error ? error.message : 'Unknown review failure';\r\n if (messageText === 'JOB_SUPERSEDED') {\r\n logger.info(`Job ${job.id} was superseded during execution, stopping.`);\r\n await env.jobs.releaseJobLease(job.id, leaseOwner);\r\n return { action: 'ack' };\r\n }\r\n\r\n if (error instanceof NextPhaseError) {\r\n await env.jobs.releaseJobLease(job.id, leaseOwner);\r\n return { action: 'next_phase', phase: error.phase, delaySeconds: error.delaySeconds, jobId: job.id, freshInstance: error.phase === 'finalize' };\r\n }\r\n\r\n if (env.modelErrors.isRetryableModelError(error)) {\r\n const delaySeconds = getRetryableModelFailureDelaySeconds(error);\r\n logger.warn(`Review job hit transient model/provider failure; scheduling delayed continuation: ${job.owner}/${job.repo} PR #${job.prNumber}`, {\r\n error: messageText,\r\n phase,\r\n delaySeconds,\r\n });\r\n return continueOrFailWedgedJob(env, job, github, leaseOwner, phase, delaySeconds, 'transient model/provider failures');\r\n }\r\n\r\n if (isSubrequestBudgetError(error)) {\r\n const record = error && typeof error === 'object' ? error as { retryAfterSeconds?: unknown } : null;\r\n const delaySeconds = typeof record?.retryAfterSeconds === 'number'\r\n ? record.retryAfterSeconds\r\n : FRESH_INVOCATION_YIELD_SECONDS;\r\n logger.warn(`Review job hit the per-invocation subrequest limit; rescheduling ${phase} on a fresh budget: ${job.owner}/${job.repo} PR #${job.prNumber}`, {\r\n error: messageText,\r\n phase,\r\n delaySeconds,\r\n });\r\n return continueOrFailWedgedJob(env, job, github, leaseOwner, phase, delaySeconds, 'per-invocation subrequest limits');\r\n }\r\n\r\n console.error('JOB FAILED WITH ERROR:', error);\r\n logger.error(`Review job failed: ${job.owner}/${job.repo} PR #${job.prNumber}`, error);\r\n await failJobAndCheckRun(env, job, github, messageText);\r\n await env.jobs.releaseJobLease(job.id, leaseOwner);\r\n return { action: 'ack' };\r\n }\r\n}\r\n\r\nasync function continueOrFailWedgedJob(\r\n env: ReviewRuntime,\r\n job: PersistedReviewJob,\r\n github: ReviewGitProvider,\r\n leaseOwner: string,\r\n phase: 'prepare' | 'review' | 'finalize',\r\n delaySeconds: number,\r\n reason: string,\r\n): Promise<ReviewJobRunResult> {\r\n const continuationCount = await env.jobs.markJobContinuationQueued(job.id, delaySeconds);\r\n\r\n const ceiling = phase === 'finalize' ? MAX_FINALIZE_CONTINUATIONS : MAX_JOB_CONTINUATIONS;\r\n\r\n if (continuationCount > ceiling) {\r\n if (phase === 'review') {\r\n logger.error(`Review job exceeded the continuation ceiling; degrading to a partial review: ${job.owner}/${job.repo} PR #${job.prNumber}`, {\r\n phase,\r\n continuationCount,\r\n reason,\r\n });\r\n const stillPending = (await env.fileReviews.getFileReviewsForJobs([job.id])).filter(isAwaitingAsyncReview);\r\n for (const review of stillPending) {\r\n await persistFailedFileReview(env, job.id, {\r\n filePath: review.file_path,\r\n modelUsed: review.async_model ?? review.model_used,\r\n diffLineCount: review.diff_line_count,\r\n errorMessage: 'Async batch review did not complete before the job wedged.',\r\n clearAsync: true,\r\n });\r\n }\r\n await env.jobs.resetJobContinuationCount(job.id);\r\n await env.jobs.releaseJobLease(job.id, leaseOwner);\r\n return { action: 'next_phase', phase: 'finalize', delaySeconds: FRESH_INVOCATION_YIELD_SECONDS, jobId: job.id, freshInstance: true };\r\n } else {\r\n const message = `Review could not make progress after ${continuationCount} continuation attempts (${reason}). Failing the job to avoid an endless retry loop; re-run it once the underlying provider issue clears.`;\r\n logger.error(`Review job exceeded the continuation ceiling; failing terminally: ${job.owner}/${job.repo} PR #${job.prNumber}`, {\r\n phase,\r\n continuationCount,\r\n reason,\r\n });\r\n await failJobAndCheckRun(env, job, github, message);\r\n await env.jobs.releaseJobLease(job.id, leaseOwner);\r\n return { action: 'ack' };\r\n }\r\n }\r\n\r\n await env.jobs.releaseJobLease(job.id, leaseOwner);\r\n const freshInstance = reason.includes('subrequest');\r\n return { action: 'next_phase', phase, delaySeconds, jobId: job.id, freshInstance };\r\n}\r\n\r\nasync function resolveQueuedJob(\r\n env: ReviewRuntime,\r\n message: ReviewJobMessage,\r\n): Promise<{ job: PersistedReviewJob; phase: 'prepare' | 'review' | 'finalize' } | null> {\r\n if (message.jobId) {\r\n const row = await env.jobs.getJobForProcessing(message.jobId);\r\n return row ? { job: env.jobs.mapJob(row), phase: message.phase ?? 'review' } : null;\r\n }\r\n\r\n if (!message.eventName) {\r\n logger.warn('Queue message ignored: missing eventName');\r\n return null;\r\n }\r\n\r\n let eventName = message.eventName;\r\n let payload = message.payload as WebhookPayload | undefined;\r\n\r\n if (payload === undefined) {\r\n const delivery = await env.webhooks.getWebhookDelivery(message.deliveryId);\r\n if (!delivery) {\r\n logger.warn(`Queue message ignored: webhook delivery not found: ${message.deliveryId}`);\r\n return null;\r\n }\r\n\r\n eventName = delivery.event_name;\r\n payload = delivery.payload as WebhookPayload;\r\n }\r\n\r\n if (eventName !== 'change_request' && eventName !== 'comment') {\r\n logger.info(`Queue message ignored: unsupported webhook event ${eventName}`);\r\n return null;\r\n }\r\n\r\n const installationId = String(payload.installationId ?? '');\r\n if (!installationId || !('repository' in payload) || !payload.repository) {\r\n logger.info('Queue message ignored: missing installation or repository info');\r\n return null;\r\n }\r\n\r\n const repoConfig = await env.repoConfig.loadRepoConfig({\r\n installationId,\r\n owner: payload.repository.owner,\r\n repo: payload.repository.name,\r\n });\r\n\r\n if (repoConfig.enabled === false) {\r\n logger.info(`Job ignored: repository ${payload.repository.owner}/${payload.repository.name} is disabled`);\r\n return null;\r\n }\r\n\r\n const extracted = extractReviewRequest({\r\n eventName,\r\n payload,\r\n botUsername: env.botUsername,\r\n config: repoConfig.parsedJson,\r\n });\r\n\r\n if (!extracted) {\r\n if (eventName === 'change_request') {\r\n const prPayload = payload as ChangeRequestWebhookPayload;\r\n if (prPayload.action === 'closed' && repoConfig.parsedJson.review.labels !== false) {\r\n const labels = repoConfig.parsedJson.review.labels;\r\n const gh = env.githubClients.forInstallation(installationId);\r\n await gh.removeIssueLabelsIfPresent(\r\n prPayload.repository.owner,\r\n prPayload.repository.name,\r\n prPayload.changeRequest.number,\r\n [labels.p1, labels.p2, labels.p3],\r\n );\r\n }\r\n }\r\n return null;\r\n }\r\n\r\n let resolved = extracted;\r\n const githubClient = env.githubClients.forInstallation(installationId);\r\n if (eventName === 'comment') {\r\n const pr = await githubClient.getPullRequest(extracted.owner, extracted.repo, extracted.prNumber);\r\n resolved = {\r\n ...extracted,\r\n prTitle: pr.title,\r\n prAuthor: pr.user.login,\r\n commitSha: pr.head.sha,\r\n baseSha: pr.base.sha,\r\n headRef: pr.head.ref,\r\n baseRef: pr.base.ref,\r\n };\r\n }\r\n\r\n const duplicateJob = await env.jobs.findExistingJobForHead({\r\n owner: resolved.owner,\r\n repo: resolved.repo,\r\n prNumber: resolved.prNumber,\r\n commitSha: resolved.commitSha,\r\n trigger: resolved.trigger,\r\n });\r\n if (duplicateJob) {\r\n if (duplicateJob.status === 'queued' || duplicateJob.status === 'running') {\r\n logger.info(`Resuming duplicate in-flight job ${duplicateJob.id} for ${resolved.owner}/${resolved.repo} PR #${resolved.prNumber}.`);\r\n return { job: duplicateJob, phase: message.phase ?? 'prepare' };\r\n }\r\n\r\n logger.info(`Duplicate terminal job found for ${resolved.owner}/${resolved.repo} PR #${resolved.prNumber}, skipping.`);\r\n return null;\r\n }\r\n\r\n const job = await env.jobs.insertJob({\r\n installationId: resolved.installationId,\r\n owner: resolved.owner,\r\n repo: resolved.repo,\r\n prNumber: resolved.prNumber,\r\n prTitle: resolved.prTitle,\r\n prAuthor: resolved.prAuthor,\r\n commitSha: resolved.commitSha,\r\n baseSha: resolved.baseSha,\r\n trigger: resolved.trigger,\r\n headRef: resolved.headRef,\r\n baseRef: resolved.baseRef,\r\n configSnapshot: repoConfig.parsedJson,\r\n });\r\n\r\n await env.jobs.supersedeOlderJobs({\r\n installationId: resolved.installationId,\r\n owner: resolved.owner,\r\n repo: resolved.repo,\r\n prNumber: resolved.prNumber,\r\n newJobId: job.id,\r\n });\r\n\r\n return { job, phase: 'prepare' };\r\n}\r\n","import type {\n WebhookEventName,\n WebhookPayload,\n CommentWebhookPayload,\n ChangeRequestWebhookPayload,\n} from '@codraoss/schema/webhook';\nimport type { RepoConfig } from '@codraoss/schema';\n\nfunction shouldTriggerFromChangeRequest(action: ChangeRequestWebhookPayload['action'], config: RepoConfig['review']) {\n return (config.on as string[]).includes(action);\n}\n\nexport type ReviewRequest = {\n installationId: string;\n owner: string;\n repo: string;\n prNumber: number;\n prTitle: string | null;\n prAuthor: string | null;\n commitSha: string;\n baseSha: string;\n headRef: string | null;\n baseRef: string | null;\n trigger: 'auto' | 'mention';\n};\n\nexport function extractReviewRequest(input: {\n eventName: WebhookEventName;\n payload: WebhookPayload;\n botUsername: string;\n config: RepoConfig;\n}): ReviewRequest | null {\n if (input.eventName === 'change_request') {\n const payload = input.payload as ChangeRequestWebhookPayload;\n if (input.config.review.ignore_drafts && payload.changeRequest.draft) {\n return null;\n }\n if (!shouldTriggerFromChangeRequest(payload.action, input.config.review)) {\n return null;\n }\n\n return {\n installationId: payload.installationId,\n owner: payload.repository.owner,\n repo: payload.repository.name,\n prNumber: payload.changeRequest.number,\n prTitle: payload.changeRequest.title,\n prAuthor: payload.changeRequest.author,\n commitSha: payload.changeRequest.head.sha,\n baseSha: payload.changeRequest.base.sha,\n headRef: payload.changeRequest.head.ref,\n baseRef: payload.changeRequest.base.ref,\n trigger: 'auto' as const,\n };\n }\n\n if (input.eventName === 'comment') {\n const payload = input.payload as CommentWebhookPayload;\n const mentionTrigger = input.config.review.mention_trigger;\n\n if (!payload.issue.isChangeRequest || payload.action !== 'created' || !mentionTrigger) {\n return null;\n }\n\n if (!payload.comment.body.includes(mentionTrigger)) {\n return null;\n }\n\n return {\n installationId: payload.installationId,\n owner: payload.repository.owner,\n repo: payload.repository.name,\n prNumber: payload.issue.number,\n prTitle: null,\n prAuthor: null,\n commitSha: '',\n baseSha: '',\n headRef: null,\n baseRef: null,\n trigger: 'mention' as const,\n };\n }\n\n return null;\n}\n","import { reviewMaxFilesRange, type RepoConfig } from '@codraoss/schema';\r\nimport { filterReviewableFiles, parseUnifiedDiff, type FileDiff } from '../diff';\r\nimport type { ReviewGitProvider, ReviewRuntime } from '../ports';\r\nimport { logger } from '../logger';\r\n\r\nimport { DIFF_CACHE_TTL_SECONDS } from '../constants';\r\n\r\nexport function diffCacheKey(jobId: string) {\r\n return `diff:${jobId}`;\r\n}\r\n\r\nexport async function getDiffFiles(\r\n env: Pick<ReviewRuntime, 'kv'>,\r\n job: { id: string; owner: string; repo: string; prNumber: number },\r\n github: Pick<ReviewGitProvider, 'getPullRequestDiff'>,\r\n config: RepoConfig,\r\n maxFiles: number = reviewMaxFilesRange.default,\r\n): Promise<{ files: FileDiff[]; skipped: number }> {\r\n const cacheKey = diffCacheKey(job.id);\r\n let rawDiff = await env.kv.get(cacheKey);\r\n\r\n if (!rawDiff) {\r\n rawDiff = await github.getPullRequestDiff(job.owner, job.repo, job.prNumber);\r\n try {\r\n await env.kv.put(cacheKey, rawDiff, { expirationTtl: DIFF_CACHE_TTL_SECONDS });\r\n } catch (error) {\r\n logger.warn(`Failed to cache PR diff for job ${job.id}; it will be re-fetched on the next phase`, error instanceof Error ? error : new Error(String(error)));\r\n }\r\n }\r\n\r\n return filterReviewableFiles(parseUnifiedDiff(rawDiff, config.review), config.review, maxFiles);\r\n}\r\n\r\nexport async function getOrFetchRawDiffForCompletedJob(\r\n env: Pick<ReviewRuntime, 'kv'>,\r\n job: { id: string; owner: string; repo: string; baseSha: string; commitSha: string },\r\n github: Pick<ReviewGitProvider, 'getCompareDiff'>,\r\n): Promise<string> {\r\n const cacheKey = diffCacheKey(job.id);\r\n const cached = await env.kv.get(cacheKey);\r\n if (cached) return cached;\r\n\r\n const rawDiff = await github.getCompareDiff(job.owner, job.repo, job.baseSha, job.commitSha);\r\n try {\r\n await env.kv.put(cacheKey, rawDiff, { expirationTtl: DIFF_CACHE_TTL_SECONDS });\r\n } catch (error) {\r\n logger.warn(`Failed to cache reconstructed diff for job ${job.id}`, error instanceof Error ? error : new Error(String(error)));\r\n }\r\n return rawDiff;\r\n}\r\n","\r\nimport { FILE_FIXED_SUBREQUESTS, MAX_MODEL_ATTEMPTS_ESTIMATE } from '../constants';\r\n\r\nexport function budgetAwareFileLimit(\r\n remainingSafeBudget: number,\r\n configuredChunkFileLimit: number,\r\n modelChainLength = 1,\r\n) {\r\n const budgetLimit = Math.floor(remainingSafeBudget / estimatedSubrequestsPerFile(modelChainLength));\r\n return Math.min(configuredChunkFileLimit, budgetLimit);\r\n}\r\n\r\nexport function estimatedSubrequestsPerFile(modelChainLength: number) {\r\n const modelAttempts = Math.max(1, Math.min(modelChainLength, MAX_MODEL_ATTEMPTS_ESTIMATE));\r\n return FILE_FIXED_SUBREQUESTS + modelAttempts;\r\n}\r\n","import { renderFileDiff } from '../prompts/file-review';\r\nimport type { FileDiff } from '../diff';\r\nimport {\r\n PACKABLE_MAX_DIFF_LINES,\r\n BIN_TARGET_DIFF_LINES,\r\n BIN_MAX_FILES,\r\n BIN_DIFF_CHAR_BUDGET,\r\n} from '../constants';\r\n\r\nexport type ReviewUnit =\r\n | { kind: 'single'; file: FileDiff }\r\n | { kind: 'bin'; files: FileDiff[]; diffLineCount: number; diffChars: number };\r\n\r\nexport type LedgerEntry = { handled: boolean; transientErrorCount: number };\r\n\r\nexport function unitFiles(unit: ReviewUnit): FileDiff[] {\r\n return unit.kind === 'single' ? [unit.file] : unit.files;\r\n}\r\n\r\nconst measure = (file: FileDiff) => renderFileDiff(file).length;\r\n\r\nconst asBin = (files: FileDiff[]): ReviewUnit => (files.length === 1\r\n ? { kind: 'single', file: files[0] }\r\n : {\r\n kind: 'bin',\r\n files,\r\n diffLineCount: files.reduce((sum, f) => sum + f.lineCount, 0),\r\n diffChars: files.reduce((sum, f) => sum + measure(f), 0),\r\n });\r\n\r\nexport function planReviewUnits(files: readonly FileDiff[], opts: { enabled: boolean }): ReviewUnit[] {\r\n if (!opts.enabled) return files.map((file) => ({ kind: 'single', file }));\r\n\r\n const units: ReviewUnit[] = [];\r\n let open: FileDiff[] = [];\r\n let lines = 0;\r\n let chars = 0;\r\n\r\n const close = () => {\r\n if (open.length > 0) units.push(asBin(open));\r\n open = [];\r\n lines = 0;\r\n chars = 0;\r\n };\r\n\r\n for (const file of files) {\r\n const fileChars = measure(file);\r\n if (file.lineCount > PACKABLE_MAX_DIFF_LINES || fileChars > BIN_DIFF_CHAR_BUDGET) {\r\n close();\r\n units.push({ kind: 'single', file });\r\n continue;\r\n }\r\n\r\n if (open.length > 0 && (\r\n lines + file.lineCount > BIN_TARGET_DIFF_LINES\r\n || chars + fileChars > BIN_DIFF_CHAR_BUDGET\r\n || open.length >= BIN_MAX_FILES\r\n )) close();\r\n\r\n open.push(file);\r\n lines += file.lineCount;\r\n chars += fileChars;\r\n }\r\n\r\n close();\r\n return units;\r\n}\r\n\r\nexport function narrowUnit(unit: ReviewUnit, ledger: Map<string, LedgerEntry>): ReviewUnit[] {\r\n const outstanding = unitFiles(unit).filter((file) => !ledger.get(file.path)?.handled);\r\n if (outstanding.length === 0) return [];\r\n\r\n const failedBefore = outstanding.some((file) => (ledger.get(file.path)?.transientErrorCount ?? 0) > 0);\r\n if (failedBefore) return outstanding.map((file): ReviewUnit => ({ kind: 'single', file }));\r\n\r\n return [asBin(outstanding)];\r\n}\r\n","import { logger } from '../logger';\r\nimport type { PersistedReviewJob, ReviewGitProvider, ReviewRuntime } from '../ports';\r\nimport {\r\n JOB_LEASE_SECONDS,\r\n} from '../constants';\r\n\r\n// JobSummary, which is exactly what mapJob returns; see the note on the port.\r\nexport type { PersistedReviewJob };\r\n\r\nexport async function heartbeatAndCheckSuperseded(env: ReviewRuntime, jobId: string, leaseOwner: string) {\r\n await env.jobs.heartbeatJobLease(jobId, leaseOwner, JOB_LEASE_SECONDS);\r\n const currentJob = await env.jobs.getJobForProcessing(jobId);\r\n if (currentJob?.status === 'superseded') {\r\n throw new Error('JOB_SUPERSEDED');\r\n }\r\n}\r\n\r\nexport class NextPhaseError extends Error {\r\n constructor(public phase: 'prepare' | 'review' | 'finalize', public delaySeconds: number) {\r\n super(`NextPhase: ${phase}`);\r\n }\r\n}\r\n\r\nexport async function enqueueJobPhase(\r\n env: ReviewRuntime,\r\n jobId: string,\r\n phase: 'prepare' | 'review' | 'finalize',\r\n delaySeconds = 0,\r\n) {\r\n await env.jobs.markJobContinuationQueued(jobId, delaySeconds);\r\n throw new NextPhaseError(phase, delaySeconds);\r\n}\r\n\r\nexport function hasCompletedStep(job: PersistedReviewJob, stepName: string) {\r\n return job.steps.some((step) => step.name === stepName && step.status === 'done');\r\n}\r\n\r\nexport async function failJobAndCheckRun(\r\n env: ReviewRuntime,\r\n job: Pick<PersistedReviewJob, 'id' | 'owner' | 'repo' | 'checkRunId'>,\r\n github: Pick<ReviewGitProvider, 'updateCheckRun'>,\r\n message: string,\r\n) {\r\n try {\r\n await env.jobs.failJob(job.id, message);\r\n } catch (dbError) {\r\n logger.error(`Critical: failed to mark job ${job.id} as failed in the DB; it may remain stuck until lease-expiry recovery reclaims it`, dbError);\r\n return;\r\n }\r\n\r\n try {\r\n const latest = await env.jobs.getJobForProcessing(job.id);\r\n const checkRunId = latest?.check_run_id ?? job.checkRunId;\r\n if (checkRunId) {\r\n await github.updateCheckRun(job.owner, job.repo, checkRunId, {\r\n status: 'completed',\r\n conclusion: 'failure',\r\n title: 'Review failed',\r\n summary: message,\r\n });\r\n await env.jobs.markJobCheckRunCompleted(job.id);\r\n }\r\n } catch (checkRunError) {\r\n logger.warn(`Failed to update GitHub check run for failed job ${job.id}; opportunistic maintenance will retry it`, checkRunError);\r\n }\r\n}\r\n","import { logger } from '../logger';\r\nimport { normalizeModelId, type RepoConfig } from '@codraoss/schema';\r\nimport { isSubrequestBudgetMessage, isTimeoutMessage, matchesAnyTransientSubstring } from '@codraoss/schema/transient-errors';\r\nimport type { ReviewRuntime } from '../ports';\r\nimport { RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS } from '../constants';\r\n\r\n\r\nexport function isRetryableFileReviewErrorMessage(message: string | null | undefined) {\r\n if (!message) return false;\r\n const lower = message.toLowerCase();\r\n\r\n if (lower.includes('retrying later') || lower.includes('all configured review models failed')) {\r\n return true;\r\n }\r\n\r\n if (isTimeoutMessage(lower)) {\r\n return false;\r\n }\r\n\r\n return (\r\n matchesAnyTransientSubstring(lower) ||\r\n lower.includes('google request failed with 5') ||\r\n lower.includes('temporary') ||\r\n lower.includes('subrequest')\r\n );\r\n}\r\n\r\nexport function isSubrequestBudgetError(error: unknown): boolean {\r\n return isSubrequestBudgetMessage(error);\r\n}\r\n\r\nexport function retryableModelFailureDelaySeconds(failureCount: number | null | undefined) {\r\n if (!failureCount || failureCount < 1) return RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS[0];\r\n const index = Math.min(failureCount - 1, RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS.length - 1);\r\n return RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS[index];\r\n}\r\n\r\nexport function getRetryableModelFailureDelaySeconds(error: unknown) {\r\n const record = error && typeof error === 'object' ? error as { retryAfterSeconds?: unknown } : null;\r\n const retryAfterSeconds =\r\n typeof record?.retryAfterSeconds === 'number'\r\n ? record.retryAfterSeconds\r\n : null;\r\n return retryAfterSeconds ?? RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS[0];\r\n}\r\n\r\nexport function shouldRetryExistingFileReview(review: { file_status: string; error_msg: string | null }) {\r\n return review.file_status === 'failed' && isRetryableFileReviewErrorMessage(review.error_msg);\r\n}\r\n\r\nexport function countsAsHandledFileReview(review: { file_status: string; error_msg: string | null }) {\r\n return !shouldRetryExistingFileReview(review);\r\n}\r\n\r\nexport function isAwaitingAsyncReview(review: { file_status: string; async_request_id?: string | null }) {\r\n return review.file_status === 'pending' && !!review.async_request_id;\r\n}\r\n\r\nexport function bareModelId(model: string): string {\r\n const normalized = normalizeModelId(model);\r\n const colon = normalized.indexOf(':');\r\n return colon === -1 ? normalized : normalized.slice(colon + 1);\r\n}\r\n\r\nexport function configuredModelSet(config: RepoConfig) {\r\n const models = new Set<string>();\r\n const addModel = (model: string | null | undefined) => {\r\n if (model) models.add(bareModelId(model));\r\n };\r\n\r\n addModel(config.model?.main);\r\n for (const fallback of config.model?.fallbacks ?? []) {\r\n addModel(fallback);\r\n }\r\n for (const tier of config.model?.size_overrides ?? []) {\r\n addModel(tier.model);\r\n for (const fallback of tier.fallbacks ?? []) {\r\n addModel(fallback);\r\n }\r\n }\r\n\r\n return models;\r\n}\r\n\r\nexport function canInheritParentFileReview(config: RepoConfig, review: { model_used: string }) {\r\n return configuredModelSet(config).has(bareModelId(review.model_used));\r\n}\r\n\r\nexport async function resolveModelProviderName(env: Pick<ReviewRuntime, 'modelConfigs'>, modelId: string | null | undefined) {\r\n if (!modelId || modelId === 'unconfigured') return null;\r\n\r\n try {\r\n const resolved = await env.modelConfigs.getResolvedModelConfig(normalizeModelId(modelId));\r\n return resolved?.providerName ?? null;\r\n } catch (error) {\r\n logger.warn(`Failed to resolve provider for model ${modelId}`, {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n return null;\r\n }\r\n}\r\n","import { logger } from '../logger';\r\nimport { type ParsedReviewComment, type RepoConfig } from '@codraoss/schema';\r\nimport { parseUnifiedDiff, type FileDiff } from '../diff';\r\nimport { ruleHitsToComments, scanFileForRuleHits, type RuleScanStats } from '../rules/detect';\r\nimport type { RejectedExemplar } from '../prompts/file-review';\r\nimport type { PullRequestRecord, ReviewModel, ReviewRuntime } from '../ports';\r\nimport { type PersistedReviewJob } from './phase-control';\r\nimport { FRESH_INVOCATION_YIELD_SECONDS, MAX_RETRYABLE_FILE_REVIEW_FAILURES } from '../constants';\r\nimport { isSubrequestBudgetError, retryableModelFailureDelaySeconds } from './retry-policy';\r\n\r\nexport async function persistCompletedReview(\r\n env: Pick<ReviewRuntime, 'fileReviews'>,\r\n job: PersistedReviewJob,\r\n file: ReturnType<typeof parseUnifiedDiff>[number],\r\n response: {\r\n modelUsed: string;\r\n provider: string;\r\n inputTokens: number;\r\n outputTokens: number;\r\n rawText: string;\r\n userPrompt: string;\r\n parsed: {\r\n comments: ParsedReviewComment[];\r\n verdict: 'approve' | 'comment';\r\n fileSummary: string;\r\n overallCorrectness?: string;\r\n confidenceScore?: number;\r\n };\r\n },\r\n) {\r\n await env.fileReviews.upsertFileReview(job.id, {\r\n filePath: file.path,\r\n fileStatus: 'done',\r\n modelUsed: response.modelUsed,\r\n modelProvider: response.provider,\r\n diffLineCount: file.lineCount,\r\n diffInput: null,\r\n rawAiOutput: response.rawText,\r\n parsedComments: response.parsed.comments,\r\n inputTokens: response.inputTokens,\r\n outputTokens: response.outputTokens,\r\n durationMs: null,\r\n verdict: response.parsed.verdict,\r\n fileSummary: response.parsed.fileSummary,\r\n overallCorrectness: response.parsed.overallCorrectness,\r\n confidenceScore: response.parsed.confidenceScore,\r\n errorMessage: null,\r\n asyncRequestId: null,\r\n asyncModel: null,\r\n });\r\n}\r\n\r\nexport async function persistFailedFileReview(\r\n env: Pick<ReviewRuntime, 'fileReviews'>,\r\n jobId: string,\r\n input: {\r\n filePath: string;\r\n modelUsed: string;\r\n modelProvider?: string | null;\r\n diffLineCount: number;\r\n durationMs?: number | null;\r\n errorMessage: string;\r\n clearAsync?: boolean;\r\n parsedComments?: ParsedReviewComment[];\r\n },\r\n) {\r\n await env.fileReviews.upsertFileReview(jobId, {\r\n filePath: input.filePath,\r\n fileStatus: 'failed',\r\n modelUsed: input.modelUsed,\r\n modelProvider: input.modelProvider ?? null,\r\n diffLineCount: input.diffLineCount,\r\n diffInput: null,\r\n rawAiOutput: null,\r\n parsedComments: input.parsedComments ?? [],\r\n inputTokens: null,\r\n outputTokens: null,\r\n durationMs: input.durationMs ?? null,\r\n verdict: null,\r\n fileSummary: null,\r\n errorMessage: input.errorMessage,\r\n ...(input.clearAsync ? { asyncRequestId: null, asyncModel: null } : {}),\r\n });\r\n}\r\n\r\nexport function scanRuleChannel(\r\n file: FileDiff,\r\n config: RepoConfig,\r\n): { comments: ParsedReviewComment[]; stats: RuleScanStats | null } {\r\n const rules = config.review.rules;\r\n if (!rules?.enabled) return { comments: [], stats: null };\r\n\r\n try {\r\n const result = scanFileForRuleHits(file, {\r\n disabledRuleIds: rules.disabled_rule_ids,\r\n shadowRuleIds: rules.shadow_rule_ids,\r\n deniedClaimTypes: config.review.deny_claim_types,\r\n });\r\n return { comments: ruleHitsToComments(file, result), stats: result.stats };\r\n } catch (error) {\r\n logger.warn(`Rule scan failed for ${file.path}; continuing with LLM findings only`, {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n return { comments: [], stats: null };\r\n }\r\n}\r\n\r\nexport async function reviewAndPersistFile(\r\n env: ReviewRuntime,\r\n job: PersistedReviewJob,\r\n file: ReturnType<typeof parseUnifiedDiff>[number],\r\n pr: PullRequestRecord,\r\n config: RepoConfig,\r\n totalLineCount: number,\r\n model: ReviewModel,\r\n resolveFailureModelProvider: () => Promise<string | null>,\r\n previousReview?: { transient_error_count: number },\r\n rejectedExemplars: readonly RejectedExemplar[] = [],\r\n) {\r\n const startedAt = env.clock.now();\r\n const compactPrompt = (previousReview?.transient_error_count ?? 0) > 0;\r\n\r\n const ruleScan = scanRuleChannel(file, config);\r\n\r\n try {\r\n const response = await model.reviewFile({\r\n file,\r\n prTitle: pr.title ?? null,\r\n prDescription: pr.body ?? null,\r\n config,\r\n totalLineCount,\r\n compactPrompt,\r\n rejectedExemplars,\r\n });\r\n\r\n await env.fileReviews.upsertFileReview(job.id, {\r\n filePath: file.path,\r\n fileStatus: 'done',\r\n modelUsed: response.modelUsed,\r\n modelProvider: response.provider,\r\n diffLineCount: file.lineCount,\r\n diffInput: null,\r\n rawAiOutput: response.rawText,\r\n parsedComments: [...response.parsed.comments, ...ruleScan.comments],\r\n inputTokens: response.inputTokens,\r\n outputTokens: response.outputTokens,\r\n durationMs: env.clock.now() - startedAt,\r\n verdict: response.parsed.verdict,\r\n fileSummary: response.parsed.fileSummary,\r\n overallCorrectness: response.parsed.overallCorrectness,\r\n confidenceScore: response.parsed.confidenceScore,\r\n errorMessage: null,\r\n withheldCounts: {\r\n evidence: (response.parsed.evidenceStats?.unmatched ?? 0)\r\n + (response.parsed.evidenceStats?.absent ?? 0)\r\n + (response.parsed.evidenceStats?.weak ?? 0),\r\n claimDenied: Object.values(response.parsed.deniedClaimCounts ?? {}).reduce((sum, n) => sum + n, 0),\r\n },\r\n });\r\n\r\n logger.info(`File review parsed: ${file.path}`, {\r\n jobId: job.id,\r\n model: response.modelUsed,\r\n kept: response.parsed.comments.length,\r\n evidence: response.parsed.evidenceStats,\r\n claimTypes: response.parsed.claimTypeCounts,\r\n deniedClaims: response.parsed.deniedClaimCounts,\r\n absenceCheck: response.parsed.absenceCheckStats,\r\n ruleChannel: ruleScan.stats,\r\n degraded: response.degraded,\r\n });\r\n\r\n if (response.wasPromptTruncated) {\r\n logger.warn(`Reviewed only part of ${file.path}; findings from the remainder are missing.`, {\r\n jobId: job.id,\r\n model: response.modelUsed,\r\n reviewedLineCount: response.reviewedLineCount,\r\n diffLineCount: file.lineCount,\r\n });\r\n }\r\n } catch (error) {\r\n const errorMessage = error instanceof Error ? error.message : 'Unknown file review error';\r\n const modelId = config.model?.main ?? 'unconfigured';\r\n const modelProvider = await resolveFailureModelProvider();\r\n\r\n if (isSubrequestBudgetError(error)) {\r\n logger.warn(`File review deferred for ${file.path}; subrequest budget will retry in a fresh invocation`, {\r\n error: errorMessage,\r\n });\r\n Object.defineProperty(error, 'retryAfterSeconds', {\r\n value: FRESH_INVOCATION_YIELD_SECONDS,\r\n configurable: true,\r\n });\r\n throw error;\r\n }\r\n\r\n if (env.modelErrors.isRetryableModelError(error)) {\r\n const failureCount = await env.fileReviews.recordRetryableFileReviewFailure(job.id, {\r\n filePath: file.path,\r\n modelUsed: modelId,\r\n modelProvider,\r\n diffLineCount: file.lineCount,\r\n diffInput: null,\r\n durationMs: env.clock.now() - startedAt,\r\n errorMessage,\r\n countsAsAttempt: env.modelErrors.nextChainIndexOf(error) === null,\r\n });\r\n\r\n if (failureCount >= MAX_RETRYABLE_FILE_REVIEW_FAILURES) {\r\n const finalError = `Review skipped after ${failureCount} repeated model provider outages.`;\r\n await persistFailedFileReview(env, job.id, {\r\n filePath: file.path,\r\n modelUsed: modelId,\r\n modelProvider,\r\n diffLineCount: file.lineCount,\r\n durationMs: env.clock.now() - startedAt,\r\n errorMessage: finalError,\r\n parsedComments: ruleScan.comments,\r\n });\r\n logger.error(`File review failed permanently for ${file.path} after transient retries`, {\r\n attempts: failureCount,\r\n error: errorMessage,\r\n });\r\n return;\r\n }\r\n\r\n logger.warn(`File review deferred for ${file.path}; transient model/provider failure will retry later`, {\r\n error: errorMessage,\r\n attempts: failureCount,\r\n });\r\n Object.defineProperty(error, 'retryAfterSeconds', {\r\n value: retryableModelFailureDelaySeconds(failureCount),\r\n configurable: true,\r\n });\r\n throw error;\r\n }\r\n\r\n logger.error(`File review failed for ${file.path}`, { error });\r\n\r\n const isHardLimit =\r\n errorMessage.includes('4006') ||\r\n errorMessage.toLowerCase().includes('allocation');\r\n\r\n if (isHardLimit) {\r\n logger.warn(`File review hit hard provider allocation limit for ${file.path}, marking as failed to allow partial PR review.`, { error: errorMessage });\r\n }\r\n\r\n await persistFailedFileReview(env, job.id, {\r\n filePath: file.path,\r\n modelUsed: modelId,\r\n modelProvider,\r\n diffLineCount: file.lineCount,\r\n durationMs: env.clock.now() - startedAt,\r\n errorMessage,\r\n parsedComments: ruleScan.comments,\r\n });\r\n }\r\n}\r\n","import { logger } from '../logger';\r\nimport type { RepoConfig } from '@codraoss/schema';\r\nimport type { FileDiff } from '../diff';\r\nimport { renderFileDiff, type RejectedExemplar } from '../prompts/file-review';\r\nimport type { BulkFileReviewInput, PullRequestRecord, ReviewModel, ReviewRuntime } from '../ports';\r\nimport { type PersistedReviewJob } from './phase-control';\r\nimport { FRESH_INVOCATION_YIELD_SECONDS, MAX_RETRYABLE_FILE_REVIEW_FAILURES, MISSING_FILE_ERROR } from '../constants';\r\nimport { isSubrequestBudgetError, retryableModelFailureDelaySeconds } from './retry-policy';\r\nimport { scanRuleChannel } from './file-runner';\r\n\r\n\r\n\r\nexport function proportionalSplit(total: number, weights: number[]): number[] {\r\n if (weights.length === 0) return [];\r\n\r\n const sum = weights.reduce((a, b) => a + b, 0);\r\n const parts = sum <= 0\r\n ? weights.map(() => Math.floor(total / weights.length))\r\n : weights.map((w) => Math.floor((total * w) / sum));\r\n\r\n const assigned = parts.reduce((a, b) => a + b, 0);\r\n if (assigned < total) {\r\n const largest = weights.indexOf(Math.max(...weights));\r\n parts[largest === -1 ? 0 : largest] += total - assigned;\r\n }\r\n return parts;\r\n}\r\n\r\nexport async function reviewAndPersistBin(\r\n env: ReviewRuntime,\r\n job: PersistedReviewJob,\r\n files: FileDiff[],\r\n pr: PullRequestRecord,\r\n config: RepoConfig,\r\n totalLineCount: number,\r\n model: ReviewModel,\r\n resolveFailureModelProvider: () => Promise<string | null>,\r\n rejectedExemplars: readonly RejectedExemplar[] = [],\r\n): Promise<number> {\r\n const startedAt = env.clock.now();\r\n\r\n const ruleScans = new Map(files.map((file) => [file.path, scanRuleChannel(file, config)]));\r\n\r\n const persisted = new Set<string>();\r\n let terminalCount = 0;\r\n\r\n const failedRow = (file: FileDiff, errorMessage: string, modelProvider?: string | null): BulkFileReviewInput => ({\r\n filePath: file.path,\r\n fileStatus: 'failed',\r\n modelUsed: config.model?.main ?? 'unconfigured',\r\n modelProvider: modelProvider ?? null,\r\n diffLineCount: file.lineCount,\r\n rawAiOutput: null,\r\n parsedComments: ruleScans.get(file.path)?.comments ?? [],\r\n inputTokens: null,\r\n outputTokens: null,\r\n durationMs: env.clock.now() - startedAt,\r\n verdict: null,\r\n fileSummary: null,\r\n errorMessage,\r\n batchSize: files.length,\r\n });\r\n\r\n try {\r\n const response = await model.reviewFiles({\r\n files,\r\n prTitle: pr.title ?? null,\r\n prDescription: pr.body ?? null,\r\n config,\r\n totalLineCount,\r\n rejectedExemplars,\r\n });\r\n\r\n const reviewed = files.filter((file) => response.batch.reviews.has(file.path));\r\n const weights = reviewed.map((file) => renderFileDiff(file).length);\r\n const inputSplit = proportionalSplit(response.inputTokens, weights);\r\n const outputSplit = proportionalSplit(response.outputTokens, weights);\r\n const durationMs = env.clock.now() - startedAt;\r\n\r\n const rows: BulkFileReviewInput[] = reviewed.map((file, index) => {\r\n const parsed = response.batch.reviews.get(file.path)!;\r\n const rules = ruleScans.get(file.path)!;\r\n return {\r\n filePath: file.path,\r\n fileStatus: 'done',\r\n modelUsed: response.modelUsed,\r\n modelProvider: response.provider,\r\n diffLineCount: file.lineCount,\r\n rawAiOutput: response.rawText,\r\n parsedComments: [...parsed.comments, ...rules.comments],\r\n inputTokens: inputSplit[index],\r\n outputTokens: outputSplit[index],\r\n durationMs,\r\n verdict: parsed.verdict,\r\n fileSummary: parsed.fileSummary,\r\n overallCorrectness: parsed.overallCorrectness,\r\n confidenceScore: parsed.confidenceScore,\r\n errorMessage: null,\r\n withheldCounts: {\r\n evidence: (parsed.evidenceStats?.unmatched ?? 0)\r\n + (parsed.evidenceStats?.absent ?? 0)\r\n + (parsed.evidenceStats?.weak ?? 0),\r\n claimDenied: Object.values(parsed.deniedClaimCounts ?? {}).reduce((sum, n) => sum + n, 0),\r\n },\r\n batchSize: files.length,\r\n };\r\n });\r\n\r\n if (rows.length > 0) {\r\n await env.fileReviews.bulkUpsertFileReviews(job.id, rows);\r\n for (const row of rows) persisted.add(row.filePath);\r\n terminalCount += rows.length;\r\n }\r\n\r\n if (response.batch.missing.length > 0) {\r\n const counts = await env.fileReviews.bulkRecordRetryableFileReviewFailures(job.id, response.batch.missing.map((path) => ({\r\n filePath: path,\r\n modelUsed: response.modelUsed,\r\n diffLineCount: files.find((f) => f.path === path)?.lineCount ?? 0,\r\n errorMessage: MISSING_FILE_ERROR,\r\n })));\r\n for (const count of counts) persisted.add(count.filePath);\r\n\r\n const exhausted = counts.filter((c) => c.transientErrorCount >= MAX_RETRYABLE_FILE_REVIEW_FAILURES);\r\n if (exhausted.length > 0) {\r\n await env.fileReviews.bulkUpsertFileReviews(job.id, exhausted.map((c) => failedRow(\r\n files.find((f) => f.path === c.filePath)!,\r\n `Review skipped after the model omitted this file ${c.transientErrorCount} times.`,\r\n )));\r\n terminalCount += exhausted.length;\r\n }\r\n }\r\n\r\n logger.info('Batched file review parsed', {\r\n jobId: job.id,\r\n model: response.modelUsed,\r\n binSize: files.length,\r\n binPaths: files.map((f) => f.path),\r\n binDiffLines: files.reduce((sum, f) => sum + f.lineCount, 0),\r\n durationMs,\r\n inputTokens: response.inputTokens,\r\n outputTokens: response.outputTokens,\r\n keptPerFile: rows.map((r) => ({ path: r.filePath, kept: r.parsedComments.length })),\r\n entriesReturned: response.batch.stats.entriesReturned,\r\n missingFiles: response.batch.missing,\r\n unroutableEntries: response.batch.stats.unroutableEntries,\r\n pathMismatchFindings: response.batch.stats.pathMismatchFindings,\r\n ambiguousAcrossBin: response.batch.stats.ambiguousAcrossBin,\r\n flatFallback: response.batch.stats.flatFallback,\r\n overCap: response.batch.stats.overCap,\r\n });\r\n } catch (error) {\r\n const errorMessage = error instanceof Error ? error.message : 'Unknown batched review error';\r\n const modelId = config.model?.main ?? 'unconfigured';\r\n const modelProvider = await resolveFailureModelProvider();\r\n\r\n if (isSubrequestBudgetError(error)) {\r\n logger.warn('Batched review deferred; subrequest budget will retry in a fresh invocation', {\r\n jobId: job.id,\r\n paths: files.map((f) => f.path),\r\n error: errorMessage,\r\n });\r\n Object.defineProperty(error, 'retryAfterSeconds', { value: FRESH_INVOCATION_YIELD_SECONDS, configurable: true });\r\n throw error;\r\n }\r\n\r\n const outstanding = files.filter((file) => !persisted.has(file.path));\r\n\r\n if (outstanding.length === 0) {\r\n logger.warn('Batched review hit an error after every file was persisted; keeping the committed rows', {\r\n jobId: job.id,\r\n paths: files.map((f) => f.path),\r\n error: errorMessage,\r\n });\r\n return terminalCount;\r\n }\r\n\r\n if (env.modelErrors.isRetryableModelError(error)) {\r\n const advancedTo = env.modelErrors.nextChainIndexOf(error);\r\n const counts = await env.fileReviews.bulkRecordRetryableFileReviewFailures(job.id, outstanding.map((file) => ({\r\n filePath: file.path,\r\n modelUsed: modelId,\r\n diffLineCount: file.lineCount,\r\n errorMessage,\r\n })), { countsAsAttempt: advancedTo === null });\r\n\r\n const exhausted = counts.filter((c) => c.transientErrorCount >= MAX_RETRYABLE_FILE_REVIEW_FAILURES);\r\n if (exhausted.length > 0) {\r\n await env.fileReviews.bulkUpsertFileReviews(job.id, exhausted.map((c) => failedRow(\r\n files.find((f) => f.path === c.filePath)!,\r\n `Review skipped after ${c.transientErrorCount} repeated model provider outages.`,\r\n modelProvider,\r\n )));\r\n terminalCount += exhausted.length;\r\n logger.error('Files in a batched review failed permanently after transient retries', {\r\n jobId: job.id,\r\n paths: exhausted.map((c) => c.filePath),\r\n error: errorMessage,\r\n });\r\n }\r\n\r\n const stillRetrying = counts.filter((c) => c.transientErrorCount < MAX_RETRYABLE_FILE_REVIEW_FAILURES);\r\n if (stillRetrying.length === 0) return terminalCount;\r\n\r\n logger.warn('Batched review deferred; transient model/provider failure will retry later', {\r\n jobId: job.id,\r\n paths: stillRetrying.map((c) => c.filePath),\r\n error: errorMessage,\r\n });\r\n Object.defineProperty(error, 'retryAfterSeconds', {\r\n value: retryableModelFailureDelaySeconds(Math.max(...stillRetrying.map((c) => c.transientErrorCount))),\r\n configurable: true,\r\n });\r\n throw error;\r\n }\r\n\r\n logger.error('Batched review failed', { jobId: job.id, paths: outstanding.map((f) => f.path), error });\r\n\r\n await env.fileReviews.bulkUpsertFileReviews(job.id, outstanding.map((file) => failedRow(file, errorMessage, modelProvider)));\r\n terminalCount += outstanding.length;\r\n }\r\n\r\n return terminalCount;\r\n}\r\n","import type { FindingDisposition, ParsedReviewComment, RepoConfig } from '@codraoss/schema';\r\nimport type { FileDiff } from './diff';\r\nimport type { ReviewModel } from './ports';\r\nimport { renderDiffSnippet, parseVerifyResponse, type VerifyCandidate } from './prompts/verify';\r\nimport { logger } from './logger';\r\n\r\ntype VerifiableJob = { id: string };\r\n\r\nconst LOW_YIELD_TITLE = /missing|redundant|repetitive|inconsisten|documentation|\\btype\\b|\\bany\\b|potential/i;\r\n\r\nexport function shadowEvaluate(candidates: ParsedReviewComment[], posted: ParsedReviewComment[]) {\r\n const postedSet = new Set(posted);\r\n const count = (predicate: (c: ParsedReviewComment) => boolean) => ({\r\n wouldDrop: candidates.filter(predicate).length,\r\n wouldDropPosted: posted.filter(predicate).length,\r\n });\r\n\r\n return {\r\n candidates: candidates.length,\r\n posted: postedSet.size,\r\n dropP3AndNit: count((c) => c.severity === 'P3' || c.severity === 'nit'),\r\n dropLowYieldTitle: count((c) => LOW_YIELD_TITLE.test(c.title)),\r\n dropUnmatchedEvidence: count((c) => !c.evidence),\r\n };\r\n}\r\n\r\nfunction verifyCandidateLimit(effectiveMaxComments: number) {\r\n return Math.min(40, Math.max(10, effectiveMaxComments * 3));\r\n}\r\n\r\nimport { VERIFY_MIN_ANSWER_RATIO } from './constants';\r\n\r\nexport type VerifyDrop = {\r\n comment: ParsedReviewComment;\r\n disposition: Extract<FindingDisposition, 'verify' | 'verify_unanswered'>;\r\n reason?: string;\r\n};\r\n\r\nexport type VerifyOutcome = {\r\n comments: ParsedReviewComment[];\r\n dropped: VerifyDrop[];\r\n reasons: Map<ParsedReviewComment, string>;\r\n};\r\n\r\nexport async function verifyFindings(params: {\r\n job: VerifiableJob;\r\n config: RepoConfig;\r\n files: FileDiff[];\r\n comments: ParsedReviewComment[];\r\n model: Pick<ReviewModel, 'verifyFindings'>;\r\n maxCandidates?: number;\r\n}): Promise<VerifyOutcome> {\r\n const { comments, files, model, config, job } = params;\r\n\r\n const keepAll = (): VerifyOutcome => ({ comments, dropped: [], reasons: new Map() });\r\n\r\n if (comments.length === 0) return keepAll();\r\n\r\n const limit = verifyCandidateLimit(params.maxCandidates ?? config.review.max_comments);\r\n const toVerify = comments.slice(0, limit);\r\n\r\n const fileByPath = new Map(files.map((file) => [file.path, file]));\r\n const prepared = toVerify.map((comment) => ({\r\n comment,\r\n snippet: renderDiffSnippet(fileByPath.get(comment.path), comment.line ?? undefined),\r\n }));\r\n\r\n const verifiable = prepared.filter((entry) => entry.snippet !== '' || entry.comment.evidence);\r\n if (verifiable.length === 0) return keepAll();\r\n\r\n const candidates: VerifyCandidate[] = verifiable.map((entry, index) => ({\r\n index,\r\n path: entry.comment.path,\r\n line: entry.comment.line ?? null,\r\n title: entry.comment.title,\r\n body: entry.comment.body,\r\n snippet: entry.snippet,\r\n evidence: entry.comment.evidence ?? null,\r\n }));\r\n\r\n try {\r\n const response = await model.verifyFindings({ candidates, config });\r\n const results = parseVerifyResponse(response.rawText);\r\n\r\n const byIndex = new Map<number, { verdict: 'keep' | 'drop'; reason?: string }>();\r\n const conflicting = new Set<number>();\r\n for (const result of results) {\r\n if (!Number.isInteger(result.index) || result.index < 0 || result.index >= candidates.length) continue;\r\n const verdict = result.decidable === false ? 'drop' as const : result.verdict;\r\n const prior = byIndex.get(result.index);\r\n if (prior && prior.verdict !== verdict) {\r\n conflicting.add(result.index);\r\n continue;\r\n }\r\n if (!prior) byIndex.set(result.index, { verdict, reason: result.reason });\r\n }\r\n for (const index of conflicting) byIndex.delete(index);\r\n\r\n const answered = byIndex.size;\r\n if (answered === 0 || answered / candidates.length < VERIFY_MIN_ANSWER_RATIO) {\r\n logger.warn('Verification did not answer enough indices; keeping all findings', {\r\n jobId: job.id, candidates: candidates.length, answered,\r\n });\r\n return keepAll();\r\n }\r\n\r\n const dropped: VerifyDrop[] = [];\r\n const reasons = new Map<ParsedReviewComment, string>();\r\n\r\n verifiable.forEach((entry, index) => {\r\n const result = byIndex.get(index);\r\n if (result?.reason) reasons.set(entry.comment, result.reason);\r\n\r\n if (result?.verdict === 'drop') {\r\n dropped.push({ comment: entry.comment, disposition: 'verify', reason: result.reason });\r\n return;\r\n }\r\n if (!result) {\r\n dropped.push({\r\n comment: entry.comment,\r\n disposition: 'verify_unanswered',\r\n reason: 'the verifier returned no verdict for this finding',\r\n });\r\n }\r\n });\r\n\r\n const droppedSet = new Set(dropped.map((drop) => drop.comment));\r\n logger.info('Verification pass complete', {\r\n jobId: job.id,\r\n candidates: candidates.length,\r\n answered,\r\n dropped: dropped.length,\r\n topReasons: dropped.slice(0, 5).map((drop) => drop.reason),\r\n });\r\n\r\n return { comments: comments.filter((comment) => !droppedSet.has(comment)), dropped, reasons };\r\n } catch (error) {\r\n logger.warn('Verification pass failed; posting pre-verification findings', {\r\n jobId: job.id,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n return keepAll();\r\n }\r\n}\r\n","import { logger } from '../logger';\r\nimport { defaultRepoConfig, type RepoConfig } from '@codraoss/schema';\r\nimport type { ReviewGitProvider, ReviewRuntime } from '../ports';\r\nimport { getDiffFiles } from './diff-cache';\r\nimport type { RejectedExemplar } from '../prompts/file-review';\r\nimport { type PersistedReviewJob, enqueueJobPhase } from './phase-control';\r\nimport { JOB_LEASE_SECONDS, FRESH_INVOCATION_YIELD_SECONDS } from '../constants';\r\n\r\nexport async function runPreparePhase(\r\n env: ReviewRuntime,\r\n job: PersistedReviewJob,\r\n leaseOwner: string,\r\n github: ReviewGitProvider,\r\n) {\r\n await env.jobs.updateJobStep(job.id, 'Preparation', { status: 'running' });\r\n const pr = await github.getPullRequest(job.owner, job.repo, job.prNumber);\r\n const config = (job.configSnapshot ?? defaultRepoConfig) as RepoConfig;\r\n\r\n try {\r\n await env.jobs.setJobPullRequestMeta(job.id, {\r\n prTitle: pr.title ?? null,\r\n prAuthor: pr.user?.login ?? null,\r\n });\r\n } catch (error) {\r\n logger.warn(`Failed to refresh PR metadata for job ${job.id}`, error instanceof Error ? error : new Error(String(error)));\r\n }\r\n\r\n let checkRunId = job.checkRunId;\r\n if (!checkRunId) {\r\n const checkRun = await github.createCheckRun(job.owner, job.repo, {\r\n headSha: pr.head.sha,\r\n title: 'Review queued',\r\n summary: 'Codra has started reviewing this pull request.',\r\n });\r\n checkRunId = checkRun.id;\r\n await env.jobs.updateJobCheckRun(job.id, checkRun.id);\r\n }\r\n\r\n const { maxFiles } = await env.settings.getReviewSettings();\r\n const { files } = await getDiffFiles(env, job, github, config, maxFiles);\r\n await env.jobs.completePreparationStep(job.id, files.length);\r\n await env.jobs.heartbeatJobLease(job.id, leaseOwner, JOB_LEASE_SECONDS);\r\n\r\n if (files.length === 0) {\r\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'done' });\r\n await enqueueJobPhase(env, job.id, 'finalize', FRESH_INVOCATION_YIELD_SECONDS);\r\n return;\r\n }\r\n\r\n if (checkRunId) {\r\n try {\r\n await github.updateCheckRun(job.owner, job.repo, checkRunId, {\r\n title: `Reviewing (0/${files.length})`,\r\n summary: 'Codra is analyzing changed files.',\r\n });\r\n } catch (error) {\r\n logger.warn(`Failed to update initial progress check run for job ${job.id}; continuing to the review phase anyway`, error instanceof Error ? error : new Error(String(error)));\r\n }\r\n }\r\n await enqueueJobPhase(env, job.id, 'review', FRESH_INVOCATION_YIELD_SECONDS);\r\n}\r\n\r\nexport async function loadRejectedExemplars(env: Pick<ReviewRuntime, 'learning'>, job: PersistedReviewJob): Promise<RejectedExemplar[]> {\r\n try {\r\n const repositoryId = await env.learning.getRepositoryIdForJob(job.id);\r\n if (repositoryId === null) return [];\r\n const rows = await env.learning.getRejectedExemplars({ repositoryId, limit: 5 });\r\n return rows.map((row) => ({ title: row.title, claimType: row.claim_type }));\r\n } catch (error) {\r\n logger.warn('Could not load rejected exemplars; reviewing without them', {\r\n jobId: job.id,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n return [];\r\n }\r\n}\r\n","import { logger } from '../logger';\r\nimport { defaultRepoConfig, REVIEW_CONCURRENCY_LIMITS, type ParsedReviewComment, type RepoConfig } from '@codraoss/schema';\r\nimport { budgetAwareFileLimit } from './budget';\r\nimport { narrowUnit, planReviewUnits } from './pack';\r\nimport { reviewAndPersistBin } from './bin-runner';\r\nimport { getDiffFiles } from './diff-cache';\r\nimport type { ReviewGitProvider, ReviewModel, ReviewRuntime } from '../ports';\r\nimport { TokenTracker } from '../token-tracker';\r\nimport {\r\n type PersistedReviewJob,\r\n NextPhaseError,\r\n enqueueJobPhase,\r\n hasCompletedStep,\r\n heartbeatAndCheckSuperseded,\r\n} from './phase-control';\r\nimport {\r\n ASYNC_BATCH_POLL_DELAY_SECONDS,\r\n FRESH_INVOCATION_YIELD_SECONDS,\r\n MAX_JOB_CONTINUATIONS,\r\n REVIEW_CHUNK_WALL_CLOCK_MS,\r\n} from '../constants';\r\nimport {\r\n canInheritParentFileReview,\r\n countsAsHandledFileReview,\r\n isAwaitingAsyncReview,\r\n isSubrequestBudgetError,\r\n resolveModelProviderName,\r\n} from './retry-policy';\r\nimport { loadRejectedExemplars, runPreparePhase } from './prepare';\r\nimport { persistCompletedReview, persistFailedFileReview, reviewAndPersistFile } from './file-runner';\r\n\r\nexport async function runReviewPhase(\r\n env: ReviewRuntime,\r\n job: PersistedReviewJob,\r\n leaseOwner: string,\r\n github: ReviewGitProvider,\r\n model: ReviewModel,\r\n tracker: TokenTracker,\r\n) {\r\n if (!hasCompletedStep(job, 'Preparation')) {\r\n await runPreparePhase(env, job, leaseOwner, github);\r\n return;\r\n }\r\n\r\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'running' });\r\n\r\n const [rejectedExemplars, pr] = await Promise.all([\r\n loadRejectedExemplars(env, job),\r\n github.getPullRequest(job.owner, job.repo, job.prNumber),\r\n ]);\r\n const config = (job.configSnapshot ?? defaultRepoConfig) as RepoConfig;\r\n const failureModelId = config.model?.main ?? 'unconfigured';\r\n let failureModelProviderPromise: Promise<string | null> | null = null;\r\n const resolveFailureModelProvider = () => {\r\n failureModelProviderPromise ??= resolveModelProviderName(env, failureModelId);\r\n return failureModelProviderPromise;\r\n };\r\n const { concurrencyLevel, maxFiles } = await env.settings.getReviewSettings();\r\n const { files } = await getDiffFiles(env, job, github, config, maxFiles);\r\n const totalLineCount = files.reduce((sum, file) => sum + file.lineCount, 0);\r\n const configuredChunkFileLimit = REVIEW_CONCURRENCY_LIMITS[concurrencyLevel];\r\n const modelChainLength = 1 + (config.model.fallbacks?.length ?? 0);\r\n const reviewChunkFileLimit = budgetAwareFileLimit(\r\n tracker.remainingSafeBudget(),\r\n configuredChunkFileLimit,\r\n modelChainLength,\r\n );\r\n if (reviewChunkFileLimit <= 0) {\r\n throw new Error('Subrequest budget for this invocation was exhausted before starting the next review chunk.');\r\n }\r\n const startedAt = env.clock.now();\r\n let processedThisChunk = 0;\r\n\r\n const jobIdsToQuery = [job.id];\r\n if (job.retryOfJobId) jobIdsToQuery.push(job.retryOfJobId);\r\n const allExistingReviews = await env.fileReviews.getFileReviewsForJobs(jobIdsToQuery);\r\n type ExistingReview = (typeof allExistingReviews)[number];\r\n const currentReviews = new Map<string, ExistingReview>();\r\n const parentReviews = new Map<string, ExistingReview>();\r\n for (const review of allExistingReviews) {\r\n if (review.job_id === job.id) currentReviews.set(review.file_path, review);\r\n else if (review.file_status === 'done') parentReviews.set(review.file_path, review);\r\n }\r\n\r\n const reviewTasks: Array<Promise<void>> = [];\r\n let terminalProgress = 0;\r\n let awaitingAsync = 0;\r\n\r\n if (job.retryOfJobId && parentReviews.size > 0) {\r\n const inheritablePaths = files.flatMap((file) => {\r\n if (currentReviews.has(file.path)) return [];\r\n const parent = parentReviews.get(file.path);\r\n return parent && canInheritParentFileReview(config, parent) ? [file.path] : [];\r\n });\r\n\r\n if (inheritablePaths.length > 0) {\r\n const inheritedPaths = await env.fileReviews.bulkInheritFileReviews({\r\n jobId: job.id,\r\n parentJobId: job.retryOfJobId,\r\n filePaths: inheritablePaths,\r\n });\r\n for (const path of inheritedPaths) {\r\n const parent = parentReviews.get(path);\r\n if (parent) currentReviews.set(path, parent);\r\n }\r\n terminalProgress += inheritedPaths.length;\r\n if (inheritedPaths.length > 0) {\r\n logger.info(`Bulk-inherited ${inheritedPaths.length} parent file reviews for job ${job.id} in one pass`);\r\n }\r\n }\r\n }\r\n\r\n const binnedPaths = new Set<string>();\r\n if (config.review.batch_small_files) {\r\n const ledger = new Map(files.map((file) => {\r\n const existing = currentReviews.get(file.path);\r\n const inheritable = parentReviews.get(file.path);\r\n return [file.path, {\r\n handled: Boolean((existing && countsAsHandledFileReview(existing)) || (inheritable && canInheritParentFileReview(config, inheritable))),\r\n transientErrorCount: existing?.transient_error_count ?? 0,\r\n }];\r\n }));\r\n\r\n const units = planReviewUnits(files, { enabled: true }).flatMap((unit) => narrowUnit(unit, ledger));\r\n const plannedBins = units.filter((unit) => unit.kind === 'bin');\r\n let binsDispatched = 0;\r\n let filesDispatchedInBins = 0;\r\n\r\n for (const unit of plannedBins) {\r\n if (processedThisChunk >= reviewChunkFileLimit) break;\r\n if (env.clock.now() - startedAt >= REVIEW_CHUNK_WALL_CLOCK_MS) break;\r\n\r\n const binFiles = unit.kind === 'bin' ? unit.files : [];\r\n binFiles.forEach((file) => binnedPaths.add(file.path));\r\n reviewTasks.push((async () => {\r\n const terminal = await reviewAndPersistBin(env, job, binFiles, pr, config, totalLineCount, model, resolveFailureModelProvider, rejectedExemplars);\r\n terminalProgress += terminal;\r\n })());\r\n processedThisChunk += 1;\r\n binsDispatched += 1;\r\n filesDispatchedInBins += binFiles.length;\r\n }\r\n\r\n if (plannedBins.length > 0) {\r\n logger.info('Batched review plan', {\r\n jobId: job.id,\r\n binsPlanned: plannedBins.length,\r\n binsDispatched,\r\n filesInBins: filesDispatchedInBins,\r\n modelCallsSaved: filesDispatchedInBins - binsDispatched,\r\n });\r\n }\r\n }\r\n\r\n for (const file of files) {\r\n if (binnedPaths.has(file.path)) continue;\r\n\r\n const existingReview = currentReviews.get(file.path);\r\n const awaitingReview = existingReview && isAwaitingAsyncReview(existingReview) ? existingReview : null;\r\n if (existingReview && countsAsHandledFileReview(existingReview) && !awaitingReview) {\r\n continue;\r\n }\r\n\r\n if (!awaitingReview && processedThisChunk >= reviewChunkFileLimit) {\r\n continue;\r\n }\r\n\r\n const inherited = parentReviews.get(file.path);\r\n const reviewTask = async () => {\r\n if (awaitingReview) {\r\n const poll = await model.pollReviewBatch({\r\n model: awaitingReview.async_model ?? awaitingReview.model_used,\r\n requestId: awaitingReview.async_request_id!,\r\n file,\r\n config,\r\n });\r\n if (poll.status === 'pending') {\r\n awaitingAsync += 1;\r\n return;\r\n }\r\n if (poll.status === 'failed') {\r\n logger.warn(`Async batch poll failed for ${file.path}; falling back to synchronous review`, {\r\n error: poll.error instanceof Error ? poll.error.message : String(poll.error),\r\n });\r\n await reviewAndPersistFile(env, job, file, pr, config, totalLineCount, model, resolveFailureModelProvider, existingReview, rejectedExemplars);\r\n terminalProgress += 1;\r\n return;\r\n }\r\n await persistCompletedReview(env, job, file, poll.response);\r\n terminalProgress += 1;\r\n return;\r\n }\r\n\r\n if (!inherited) {\r\n const submitted = await model.submitReviewBatch({\r\n file,\r\n prTitle: pr.title ?? null,\r\n prDescription: pr.body ?? null,\r\n config,\r\n totalLineCount,\r\n compactPrompt: (existingReview?.transient_error_count ?? 0) > 0,\r\n });\r\n if (submitted) {\r\n await env.fileReviews.upsertFileReview(job.id, {\r\n filePath: file.path,\r\n fileStatus: 'pending',\r\n modelUsed: submitted.model,\r\n modelProvider: null,\r\n diffLineCount: file.lineCount,\r\n diffInput: null,\r\n rawAiOutput: null,\r\n parsedComments: [],\r\n inputTokens: null,\r\n outputTokens: null,\r\n durationMs: null,\r\n verdict: null,\r\n fileSummary: null,\r\n overallCorrectness: null,\r\n confidenceScore: null,\r\n errorMessage: null,\r\n asyncRequestId: submitted.requestId,\r\n asyncModel: submitted.model,\r\n });\r\n awaitingAsync += 1;\r\n return;\r\n }\r\n await reviewAndPersistFile(env, job, file, pr, config, totalLineCount, model, resolveFailureModelProvider, existingReview, rejectedExemplars);\r\n terminalProgress += 1;\r\n return;\r\n }\r\n\r\n if (!canInheritParentFileReview(config, inherited)) {\r\n logger.info(`Ignoring inherited review for ${file.path}; parent model ${inherited.model_used} is not in the current model strategy`);\r\n await reviewAndPersistFile(env, job, file, pr, config, totalLineCount, model, resolveFailureModelProvider, existingReview, rejectedExemplars);\r\n terminalProgress += 1;\r\n } else {\r\n await env.fileReviews.upsertFileReview(job.id, {\r\n filePath: file.path,\r\n fileStatus: 'done',\r\n modelUsed: inherited.model_used,\r\n modelProvider: inherited.model_provider,\r\n diffLineCount: inherited.diff_line_count,\r\n diffInput: inherited.diff_input,\r\n rawAiOutput: inherited.raw_ai_output,\r\n parsedComments: inherited.parsed_comments as ParsedReviewComment[],\r\n inputTokens: inherited.input_tokens,\r\n outputTokens: inherited.output_tokens,\r\n durationMs: inherited.duration_ms,\r\n verdict: inherited.verdict,\r\n fileSummary: inherited.file_summary,\r\n overallCorrectness: inherited.overall_correctness,\r\n confidenceScore: inherited.confidence_score,\r\n errorMessage: null,\r\n });\r\n currentReviews.set(file.path, inherited);\r\n terminalProgress += 1;\r\n }\r\n };\r\n\r\n reviewTasks.push(reviewTask());\r\n if (!awaitingReview) processedThisChunk += 1;\r\n\r\n if (env.clock.now() - startedAt >= REVIEW_CHUNK_WALL_CLOCK_MS) {\r\n break;\r\n }\r\n }\r\n\r\n const results = await Promise.allSettled(reviewTasks);\r\n await heartbeatAndCheckSuperseded(env, job.id, leaseOwner);\r\n\r\n if (terminalProgress > 0) {\r\n await env.jobs.resetJobContinuationCount(job.id);\r\n }\r\n\r\n logger.info('Review chunk model usage', {\r\n jobId: job.id,\r\n subrequests: tracker.getSubrequestCount(),\r\n usage: tracker.getTotalUsage(),\r\n wasted: tracker.getWasted(),\r\n });\r\n\r\n const rejected = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected');\r\n if (rejected.length > 0) {\r\n rejected.forEach((result, index) => {\r\n logger.error(`Review chunk task ${index + 1}/${rejected.length} failed`, result.reason);\r\n });\r\n\r\n const deferrableError = rejected.map(r => r.reason).find(r => env.modelErrors.isRetryableModelError(r) || isSubrequestBudgetError(r));\r\n if (deferrableError) {\r\n throw deferrableError;\r\n }\r\n\r\n throw rejected.length === 1\r\n ? rejected[0].reason\r\n : new AggregateError(rejected.map((result) => result.reason), `${rejected.length} review chunk tasks failed`);\r\n }\r\n\r\n const latestReviews = await env.fileReviews.getFileReviewsForJobs([job.id]);\r\n const reviewedPaths = new Set(\r\n latestReviews.flatMap((review) => (\r\n countsAsHandledFileReview(review) && !isAwaitingAsyncReview(review) ? [review.file_path] : []\r\n )),\r\n );\r\n const completedCount = files.filter((file) => reviewedPaths.has(file.path)).length;\r\n\r\n if (completedCount >= files.length) {\r\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'done' });\r\n await enqueueJobPhase(env, job.id, 'finalize', FRESH_INVOCATION_YIELD_SECONDS);\r\n return;\r\n }\r\n\r\n if (awaitingAsync > 0 && terminalProgress === 0) {\r\n const pollCount = await env.jobs.markJobContinuationQueued(job.id, ASYNC_BATCH_POLL_DELAY_SECONDS);\r\n if (pollCount > MAX_JOB_CONTINUATIONS) {\r\n logger.error(`Async batch reviews did not complete after ${pollCount} polls; degrading to a partial review: ${job.owner}/${job.repo} PR #${job.prNumber}`);\r\n for (const review of latestReviews.filter(isAwaitingAsyncReview)) {\r\n await persistFailedFileReview(env, job.id, {\r\n filePath: review.file_path,\r\n modelUsed: review.async_model ?? review.model_used,\r\n diffLineCount: review.diff_line_count,\r\n errorMessage: 'Async batch review did not complete in time.',\r\n clearAsync: true,\r\n });\r\n }\r\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'done' });\r\n throw new NextPhaseError('finalize', FRESH_INVOCATION_YIELD_SECONDS);\r\n }\r\n throw new NextPhaseError('review', ASYNC_BATCH_POLL_DELAY_SECONDS);\r\n }\r\n\r\n if (job.checkRunId) {\r\n try {\r\n await github.updateCheckRun(job.owner, job.repo, job.checkRunId, {\r\n title: `Reviewing (${completedCount}/${files.length})`,\r\n summary: 'Codra is continuing this review in the next queue chunk.',\r\n });\r\n } catch (error) {\r\n logger.warn(`Failed to update progress check run for job ${job.id}; continuing to the next chunk anyway`, error instanceof Error ? error : new Error(String(error)));\r\n }\r\n }\r\n await enqueueJobPhase(env, job.id, 'review', FRESH_INVOCATION_YIELD_SECONDS);\r\n}\r\n","import { logger } from '../logger';\r\nimport { defaultRepoConfig, type ParsedReviewComment, type RepoConfig } from '@codraoss/schema';\r\nimport { shadowEvaluate } from '../finding-gates';\r\nimport { getDiffFiles } from './diff-cache';\r\nimport type { ReviewFormatter, ReviewGitProvider, ReviewModel, ReviewRuntime } from '../ports';\r\nimport {\r\n type PersistedReviewJob,\r\n enqueueJobPhase,\r\n heartbeatAndCheckSuperseded,\r\n} from './phase-control';\r\nimport { FRESH_INVOCATION_YIELD_SECONDS } from '../constants';\r\nimport { sendReviewTelemetry } from './telemetry';\r\nimport { applyFindingGates } from './gate-pipeline';\r\n\r\nexport async function runFinalizePhase(\r\n env: ReviewRuntime,\r\n job: PersistedReviewJob,\r\n leaseOwner: string,\r\n github: ReviewGitProvider,\r\n formatter: ReviewFormatter,\r\n model: ReviewModel,\r\n) {\r\n await env.jobs.updateJobStep(job.id, 'Generating Summary', { status: 'running' });\r\n\r\n const pr = await github.getPullRequest(job.owner, job.repo, job.prNumber);\r\n const config = (job.configSnapshot ?? defaultRepoConfig) as RepoConfig;\r\n const reviewSettings = await env.settings.getReviewSettings();\r\n const [{ files, skipped: filesOverCap }, initialReviews] = await Promise.all([\r\n getDiffFiles(env, job, github, config, reviewSettings.maxFiles),\r\n env.fileReviews.getFileReviewsForJobs([job.id]),\r\n ]);\r\n let reviews = initialReviews;\r\n\r\n {\r\n const reviewedPaths = new Set(reviews.map((r) => r.file_path));\r\n const missingFiles = files.filter((f) => !reviewedPaths.has(f.path));\r\n\r\n if (missingFiles.length > 0) {\r\n logger.warn(`Job ${job.id} reached finalize phase with ${missingFiles.length} missing file reviews. Forcing them to failed state.`);\r\n await env.fileReviews.bulkMarkFilesFailed(\r\n job.id,\r\n missingFiles.map((file) => ({ filePath: file.path, diffLineCount: file.lineCount })),\r\n { modelUsed: config.model?.main ?? 'unconfigured', errorMessage: 'This file was not reviewed before the review run completed.' },\r\n );\r\n\r\n reviews = await env.fileReviews.getFileReviewsForJobs([job.id]);\r\n } else if (reviews.length < files.length) {\r\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'running' });\r\n await enqueueJobPhase(env, job.id, 'review', FRESH_INVOCATION_YIELD_SECONDS);\r\n return;\r\n }\r\n }\r\n\r\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'done' });\r\n\r\n const reviewedComments = reviews.flatMap((review) => review.parsed_comments as ParsedReviewComment[]);\r\n const fileSummaries = reviews.map((review) => ({\r\n path: review.file_path,\r\n summary: review.file_status === 'failed'\r\n ? `Review failed: ${review.error_msg ?? 'Unknown file review error'}`\r\n : (review.file_summary ?? ''),\r\n verdict: review.file_status === 'failed' ? 'failed' : (review.verdict ?? 'comment'),\r\n }));\r\n\r\n const { concurrencyLevel, maxComments: globalMaxComments } = reviewSettings;\r\n const effectiveMaxComments = Math.min(config.review.max_comments, globalMaxComments);\r\n const retryCount = job.retryOfJobId ? 1 : 0;\r\n\r\n if (fileSummaries.length > 0 && fileSummaries.every((file) => file.verdict === 'failed')) {\r\n await env.jobs.updateJobStep(job.id, 'Generating Summary', { status: 'failed', error: 'All files failed to review' });\r\n\r\n await sendReviewTelemetry(\r\n env,\r\n job,\r\n files,\r\n reviews,\r\n { findingsReported: 0, verdict: 'failed', severityDistribution: {} },\r\n { concurrencyLevel, retryCount },\r\n );\r\n\r\n throw new Error('All files failed to review');\r\n }\r\n\r\n const hasFailures = fileSummaries.some((file) => file.verdict === 'failed');\r\n const failedFileCount = fileSummaries.filter((file) => file.verdict === 'failed').length;\r\n const {\r\n finalComments,\r\n dispositions,\r\n verifyReasons,\r\n suppressedComments,\r\n droppedBySuppression,\r\n beforeVerifyList,\r\n droppedByVerification,\r\n droppedByCap,\r\n omittedCount,\r\n droppedByFilters,\r\n withheldByParser,\r\n byClaimType,\r\n } = await applyFindingGates({\r\n env, job, config, files, model, effectiveMaxComments, reviewedComments, reviews,\r\n });\r\n\r\n\r\n logger.info('Finding pipeline outcome', {\r\n jobId: job.id,\r\n parsed: reviewedComments.length,\r\n droppedByFilters,\r\n droppedBySuppression,\r\n droppedByVerification,\r\n droppedByCap,\r\n posted: finalComments.length,\r\n withheldByParser,\r\n byClaimType,\r\n byChannel: {\r\n llm: finalComments.filter((c) => c.source !== 'rule').length,\r\n rule: finalComments.filter((c) => c.source === 'rule').length,\r\n },\r\n byRule: reviewedComments.reduce<Record<string, number>>((acc, c) => {\r\n if (c.source === 'rule' && c.ruleId) acc[c.ruleId] = (acc[c.ruleId] ?? 0) + 1;\r\n return acc;\r\n }, {}),\r\n postedAny: finalComments.length > 0,\r\n postedPer100Files: files.length > 0\r\n ? Math.round((finalComments.length / files.length) * 1000) / 10\r\n : 0,\r\n });\r\n\r\n logger.info('Shadow filter evaluation', {\r\n jobId: job.id,\r\n ...shadowEvaluate(beforeVerifyList, finalComments),\r\n });\r\n\r\n const rawVerdict = formatter.summarizeVerdict([...finalComments, ...suppressedComments], hasFailures);\r\n\r\n const everythingWithheld = finalComments.length === 0\r\n && suppressedComments.length === 0\r\n && (withheldByParser > 0 || omittedCount > 0);\r\n const verdictSummary = everythingWithheld && rawVerdict.verdict === 'approve'\r\n ? { ...rawVerdict, verdict: 'comment' as const }\r\n : rawVerdict;\r\n await env.jobs.updateJobStep(job.id, 'Generating Summary', { status: 'done' });\r\n await heartbeatAndCheckSuperseded(env, job.id, leaseOwner);\r\n\r\n let formattedSummary = formatter.formatReviewOverview(pr.head.sha, env.botUsername);\r\n\r\n if (filesOverCap > 0) {\r\n formattedSummary += `\\n\\n> [!WARNING]\\n> **${filesOverCap} file${filesOverCap === 1 ? ' was' : 's were'} not reviewed.** This pull request has ${files.length + filesOverCap} reviewable files and the limit is ${reviewSettings.maxFiles}. Raise it in Settings to cover the whole diff.`;\r\n }\r\n\r\n const finalizeRetriedPastPost = job.steps.some(\r\n (step) => step.name === 'Completing' && (step.status === 'running' || step.status === 'done'),\r\n );\r\n await env.jobs.updateJobStep(job.id, 'Completing', { status: 'running' });\r\n const existingReview: { id: number; postedIndices?: number[] } | null = finalizeRetriedPastPost\r\n ? await github.findBotReviewForCommit(job.owner, job.repo, job.prNumber, pr.head.sha, env.botUsername)\r\n : null;\r\n const review = existingReview ?? await github.createReview(job.owner, job.repo, job.prNumber, {\r\n commitSha: pr.head.sha,\r\n event: formatter.toReviewEvent(verdictSummary.verdict),\r\n body: formattedSummary,\r\n comments: finalComments.map(comment => ({\r\n path: comment.path,\r\n line: comment.line ?? undefined,\r\n side: 'RIGHT' as const,\r\n position: comment.position ?? undefined,\r\n body: formatter.formatInlineComment(comment),\r\n })),\r\n });\r\n\r\n if (review.postedIndices && review.postedIndices.length > 0) {\r\n const postedFingerprints = review.postedIndices\r\n .map((index) => finalComments[index]?.fingerprint)\r\n .filter((fingerprint): fingerprint is string => Boolean(fingerprint));\r\n await env.fileReviews.markCommentsPosted(job.id, postedFingerprints);\r\n }\r\n\r\n try {\r\n const withReasons = new Map<string, { disposition: string | null; reason: string | null }>();\r\n for (const fingerprint of new Set([...dispositions.keys(), ...verifyReasons.keys()])) {\r\n withReasons.set(fingerprint, {\r\n disposition: dispositions.get(fingerprint) ?? null,\r\n reason: verifyReasons.get(fingerprint) ?? null,\r\n });\r\n }\r\n await env.fileReviews.markCommentDispositions(job.id, withReasons);\r\n } catch (error) {\r\n logger.warn('Could not record finding dispositions', {\r\n jobId: job.id,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n }\r\n\r\n const fileInputTokens = reviews.reduce((sum, review) => sum + (review.input_tokens ?? 0), 0);\r\n const fileOutputTokens = reviews.reduce((sum, review) => sum + (review.output_tokens ?? 0), 0);\r\n\r\n const severityDistribution: Record<string, number> = {};\r\n for (const comment of finalComments) {\r\n const sev = comment.severity || 'unknown';\r\n severityDistribution[sev] = (severityDistribution[sev] || 0) + 1;\r\n }\r\n\r\n const partialErrorMessage = hasFailures\r\n ? `Partial review: ${failedFileCount} of ${files.length} file${files.length === 1 ? '' : 's'} could not be reviewed.`\r\n : null;\r\n await env.jobs.completeJob(job.id, {\r\n verdict: verdictSummary.verdict,\r\n fileCount: files.length,\r\n commentCount: finalComments.length,\r\n totalInputTokens: fileInputTokens,\r\n totalOutputTokens: fileOutputTokens,\r\n summaryMarkdown: formattedSummary,\r\n reviewId: review.id,\r\n summaryModel: null,\r\n errorMessage: partialErrorMessage,\r\n });\r\n logger.info(`Review job completed: ${job.owner}/${job.repo} PR #${job.prNumber}`);\r\n\r\n try {\r\n if (job.checkRunId) {\r\n await github.updateCheckRun(job.owner, job.repo, job.checkRunId, {\r\n status: 'completed',\r\n conclusion: hasFailures ? 'failure' : (verdictSummary.verdict === 'approve' ? 'success' : 'neutral'),\r\n title: hasFailures ? 'Review partially failed' : (verdictSummary.verdict === 'approve' ? 'LGTM' : 'Comments posted'),\r\n summary: `${finalComments.length} inline comments across ${files.length} files.${hasFailures ? ` ${failedFileCount} file${failedFileCount === 1 ? '' : 's'} could not be reviewed.` : ''}`,\r\n });\r\n await env.jobs.markJobCheckRunCompleted(job.id);\r\n }\r\n\r\n if (config.review.labels !== false) {\r\n const labels = config.review.labels;\r\n const labelMap = {\r\n comment: { name: labels.p1, color: 'f79009' },\r\n approve: { name: labels.p2, color: '027a48' },\r\n } as const;\r\n const label = labelMap[verdictSummary.verdict];\r\n\r\n await github.removeIssueLabelsIfPresent(\r\n job.owner,\r\n job.repo,\r\n job.prNumber,\r\n [labels.p1, labels.p2, labels.p3].filter(possibleLabel => possibleLabel !== label.name),\r\n );\r\n\r\n await github.ensureLabel(job.owner, job.repo, label.name, label.color);\r\n await github.addIssueLabels(job.owner, job.repo, job.prNumber, [label.name]);\r\n }\r\n } catch (error) {\r\n logger.warn(`Post-review labels/check-run update failed for job ${job.id}; review is posted and job is completed, so leaving it best-effort`, error instanceof Error ? error : new Error(String(error)));\r\n }\r\n\r\n await sendReviewTelemetry(\r\n env,\r\n job,\r\n files,\r\n reviews,\r\n { findingsReported: finalComments.length, verdict: verdictSummary.verdict, severityDistribution },\r\n { concurrencyLevel, retryCount },\r\n );\r\n}\r\n","import { logger } from '../logger';\r\nimport type { ReviewRuntime } from '../ports';\r\nimport { type PersistedReviewJob } from './phase-control';\r\nimport { bareModelId } from './retry-policy';\r\n\r\nexport async function sendReviewTelemetry(\r\n env: ReviewRuntime,\r\n job: PersistedReviewJob,\r\n files: Array<{ path: string; lineCount: number }>,\r\n reviews: Array<{ file_status: string; input_tokens: number | null; output_tokens: number | null; model_used: string }>,\r\n overrides: { findingsReported: number; verdict: string; severityDistribution: Record<string, number> },\r\n meta: { concurrencyLevel: string; retryCount: number },\r\n) {\r\n try {\r\n const doneReviews = reviews.filter((r) => r.file_status === 'done');\r\n\r\n const cleanModels = Array.from(\r\n new Set(\r\n doneReviews.flatMap((r) => {\r\n const model = bareModelId(r.model_used);\r\n return model && !model.toLowerCase().includes('test') ? [model] : [];\r\n }),\r\n ),\r\n );\r\n\r\n const extractExtension = (filePath: string): string => {\r\n const name = filePath.split('/').pop() || filePath;\r\n const dotIndex = name.lastIndexOf('.');\r\n if (dotIndex <= 0) return '';\r\n return name.slice(dotIndex + 1).toLowerCase();\r\n };\r\n\r\n await env.telemetry.send({\r\n linesReviewed: files.reduce((sum, file) => sum + file.lineCount, 0),\r\n inputTokens: doneReviews.reduce((sum, r) => sum + (r.input_tokens ?? 0), 0),\r\n outputTokens: doneReviews.reduce((sum, r) => sum + (r.output_tokens ?? 0), 0),\r\n modelsUsed: cleanModels,\r\n fileExtensions: Array.from(new Set(files.flatMap((f) => {\r\n const extension = extractExtension(f.path);\r\n return extension ? [extension] : [];\r\n }))),\r\n triggerType: job.trigger,\r\n reviewDurationMs: Math.max(0, env.clock.now() - new Date(job.createdAt).getTime()),\r\n filesReviewed: files.length,\r\n concurrencyLevel: meta.concurrencyLevel,\r\n prTotalLinesChanged: files.reduce((sum, file) => sum + file.lineCount, 0),\r\n retryCount: meta.retryCount,\r\n ...overrides,\r\n });\r\n } catch (e) {\r\n logger.error('Failed to send telemetry', e instanceof Error ? e : new Error(String(e)));\r\n }\r\n}\r\n\r\nexport async function loadSuppressedFingerprints(env: Pick<ReviewRuntime, 'fileReviews'>, jobId: string) {\r\n const posted = new Map<string, Set<string>>();\r\n const rejected = new Set<string>();\r\n const postedV2 = new Set<string>();\r\n const rejectedV2 = new Set<string>();\r\n\r\n try {\r\n for (const row of await env.fileReviews.getSuppressedFindings(jobId)) {\r\n if (!row.anchored) {\r\n if (row.fingerprint) rejected.add(row.fingerprint);\r\n if (row.fingerprint_v2) rejectedV2.add(row.fingerprint_v2);\r\n continue;\r\n }\r\n if (row.fingerprint_v2) postedV2.add(row.fingerprint_v2);\r\n if (!row.fingerprint || !row.anchor_hash) continue;\r\n const anchors = posted.get(row.fingerprint) ?? new Set<string>();\r\n anchors.add(row.anchor_hash);\r\n posted.set(row.fingerprint, anchors);\r\n }\r\n } catch (error) {\r\n logger.warn('Could not load suppressed findings; posting without cross-run dedupe', {\r\n jobId,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n }\r\n\r\n return { posted, rejected, postedV2, rejectedV2 };\r\n}\r\n","import { dedupeFindings } from '../model-output';\r\nimport { verifyFindings } from '../finding-gates';\r\nimport type { FindingDisposition, ParsedReviewComment, RepoConfig } from '@codraoss/schema';\r\nimport type { FileDiff } from '../diff';\r\nimport type { PersistedReviewJob } from './phase-control';\r\nimport type { ReviewModel, ReviewRuntime } from '../ports';\r\nimport { loadSuppressedFingerprints } from './telemetry';\r\n\r\nexport async function applyFindingGates(params: {\r\n env: Pick<ReviewRuntime, 'fileReviews'>;\r\n job: PersistedReviewJob;\r\n config: RepoConfig;\r\n files: FileDiff[];\r\n model: Pick<ReviewModel, 'verifyFindings'>;\r\n effectiveMaxComments: number;\r\n reviewedComments: ParsedReviewComment[];\r\n reviews: Array<{ withheld_counts?: { evidence?: number; claimDenied?: number } | null }>;\r\n}) {\r\n const { env, job, config, files, model, effectiveMaxComments, reviewedComments, reviews } = params;\r\n\r\n const severityRanks: Record<string, number> = { P0: 0, P1: 1, P2: 2, P3: 3, nit: 4 };\r\n const minRank = severityRanks[config.review.min_severity] ?? 4;\r\n const minConfidence = config.review.min_confidence ?? 0;\r\n\r\n const dispositions = new Map<string, FindingDisposition>();\r\n const verifyReasons = new Map<string, string>();\r\n const recordDisposition = (comments: ParsedReviewComment[], stage: FindingDisposition) => {\r\n for (const comment of comments) {\r\n if (comment.fingerprint && !dispositions.has(comment.fingerprint)) {\r\n dispositions.set(comment.fingerprint, stage);\r\n }\r\n }\r\n };\r\n\r\n let finalComments = reviewedComments.filter((c) => {\r\n if ((severityRanks[c.severity] ?? 4) > minRank) {\r\n recordDisposition([c], 'severity');\r\n return false;\r\n }\r\n if (typeof c.confidenceScore === 'number' && c.confidenceScore < minConfidence) {\r\n recordDisposition([c], 'confidence');\r\n return false;\r\n }\r\n return true;\r\n });\r\n\r\n const suppressed = await loadSuppressedFingerprints(env, job.id);\r\n const suppressedComments: ParsedReviewComment[] = [];\r\n const hasSuppressionData = suppressed.rejected.size > 0 || suppressed.posted.size > 0\r\n || suppressed.rejectedV2.size > 0 || suppressed.postedV2.size > 0;\r\n if (hasSuppressionData) {\r\n finalComments = finalComments.filter((c) => {\r\n const rejected = (c.fingerprint && suppressed.rejected.has(c.fingerprint))\r\n || (c.fingerprintV2 && suppressed.rejectedV2.has(c.fingerprintV2));\r\n\r\n const anchors = c.fingerprint ? suppressed.posted.get(c.fingerprint) : undefined;\r\n const alreadyPosted = (anchors && c.anchorHash && anchors.has(c.anchorHash))\r\n || (c.fingerprintV2 && suppressed.postedV2.has(c.fingerprintV2));\r\n\r\n if (rejected || alreadyPosted) {\r\n suppressedComments.push(c);\r\n return false;\r\n }\r\n return true;\r\n });\r\n }\r\n const droppedBySuppression = suppressedComments.length;\r\n recordDisposition(suppressedComments, 'suppression');\r\n\r\n const beforeDedupe = finalComments;\r\n finalComments = dedupeFindings(finalComments);\r\n const survivedDedupe = new Set(finalComments);\r\n recordDisposition(beforeDedupe.filter((c) => !survivedDedupe.has(c)), 'dedupe');\r\n\r\n finalComments.sort((a, b) => {\r\n const rankDiff = (severityRanks[a.severity] ?? 4) - (severityRanks[b.severity] ?? 4);\r\n if (rankDiff !== 0) return rankDiff;\r\n return (b.confidenceScore ?? 0) - (a.confidenceScore ?? 0);\r\n });\r\n\r\n const beforeVerifyList = finalComments;\r\n const verify = await verifyFindings({ job, config, files, comments: finalComments, model, maxCandidates: effectiveMaxComments });\r\n finalComments = verify.comments;\r\n const droppedByVerification = verify.dropped.length;\r\n for (const drop of verify.dropped) recordDisposition([drop.comment], drop.disposition);\r\n for (const [comment, reason] of verify.reasons) {\r\n if (comment.fingerprint) verifyReasons.set(comment.fingerprint, reason);\r\n }\r\n\r\n const beforeCapList = finalComments;\r\n const beforeCap = finalComments.length;\r\n if (finalComments.length > effectiveMaxComments) {\r\n finalComments = finalComments.slice(0, effectiveMaxComments);\r\n }\r\n const droppedByCap = beforeCap - finalComments.length;\r\n recordDisposition(beforeCapList.slice(effectiveMaxComments), 'cap');\r\n const omittedCount = reviewedComments.length - finalComments.length;\r\n const droppedByFilters = omittedCount - droppedBySuppression - droppedByVerification - droppedByCap;\r\n\r\n const withheldByParser = reviews.reduce(\r\n (sum, review) => sum + (review.withheld_counts?.evidence ?? 0) + (review.withheld_counts?.claimDenied ?? 0),\r\n 0,\r\n );\r\n\r\n const byClaimType: Record<string, { generated: number; posted: number }> = {};\r\n for (const comment of reviewedComments) {\r\n const key = comment.claimType ?? 'unlabelled';\r\n byClaimType[key] ??= { generated: 0, posted: 0 };\r\n byClaimType[key].generated += 1;\r\n }\r\n for (const comment of finalComments) {\r\n const key = comment.claimType ?? 'unlabelled';\r\n byClaimType[key] ??= { generated: 0, posted: 0 };\r\n byClaimType[key].posted += 1;\r\n }\r\n return {\r\n finalComments,\r\n dispositions,\r\n verifyReasons,\r\n suppressedComments,\r\n droppedBySuppression,\r\n beforeVerifyList,\r\n droppedByVerification,\r\n droppedByCap,\r\n omittedCount,\r\n droppedByFilters,\r\n withheldByParser,\r\n byClaimType,\r\n };\r\n}\r\n"],"mappings":"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","names":["REVIEW_CONCURRENCY_LIMITS","defaultRepoConfig","defaultRepoConfig","defaultRepoConfig","defaultRepoConfig","review","REVIEW_CONCURRENCY_LIMITS"]}
|
|
1
|
+
{"version":3,"sources":["../src/review/index.ts","../src/review/request.ts","../src/review/diff-cache.ts","../src/review/budget.ts","../src/review/pack.ts","../src/review/phase-control.ts","../src/review/retry-policy.ts","../src/review/file-runner.ts","../src/review/bin-runner.ts","../src/finding-gates.ts","../src/review/prepare.ts","../src/review/phase.ts","../src/review/file-context.ts","../src/review/finalize.ts","../src/review/telemetry.ts","../src/review/gate-pipeline.ts"],"sourcesContent":["import { logger } from '../logger';\nimport { type WebhookPayload, type ChangeRequestWebhookPayload } from '@codraoss/schema/webhook';\nimport { REVIEW_CONCURRENCY_LIMITS, type ReviewJobMessage } from '@codraoss/schema';\nimport type { ReviewGitProvider, ReviewRuntime } from '../ports';\nimport { extractReviewRequest } from './request';\n\nexport { getDiffFiles, getOrFetchRawDiffForCompletedJob } from './diff-cache';\n\nexport { budgetAwareFileLimit, estimatedSubrequestsPerFile } from './budget';\n\nexport {\n narrowUnit,\n planReviewUnits,\n unitFiles,\n type LedgerEntry,\n type ReviewUnit,\n} from './pack';\n\nexport {\n BIN_DIFF_CHAR_BUDGET,\n BIN_MAX_FILES,\n BIN_TARGET_DIFF_LINES,\n PACKABLE_MAX_DIFF_LINES,\n} from '../constants';\n\nexport { proportionalSplit } from './bin-runner';\n\nexport { verifyFindings, type VerifyDrop, type VerifyOutcome } from '../finding-gates';\n\nexport { extractReviewRequest, type ReviewRequest } from './request';\n\n// workflows/review.ts floors its inter-phase sleep here; the eslint barrel guard stops it\nexport { FRESH_INVOCATION_YIELD_SECONDS } from '../constants';\n\nimport {\n type PersistedReviewJob,\n NextPhaseError,\n failJobAndCheckRun,\n} from './phase-control';\nimport {\n BUSY_RETRY_SECONDS,\n FRESH_INVOCATION_YIELD_SECONDS,\n JOB_LEASE_SECONDS,\n MAX_FINALIZE_CONTINUATIONS,\n MAX_JOB_CONTINUATIONS,\n} from '../constants';\nimport { getRetryableModelFailureDelaySeconds, isAwaitingAsyncReview, isSubrequestBudgetError } from './retry-policy';\nimport { persistFailedFileReview } from './file-runner';\nimport { runPreparePhase } from './prepare';\nimport { runReviewPhase } from './phase';\nimport { runFinalizePhase } from './finalize';\n\nexport { NextPhaseError, failJobAndCheckRun };\n\nexport type ReviewJobRunResult =\n | { action: 'ack' }\n | { action: 'retry'; delaySeconds: number }\n | { action: 'next_phase'; phase: 'prepare' | 'review' | 'finalize'; delaySeconds: number; jobId?: string; freshInstance?: boolean };\n\n/**\n * The engine's entrypoint. Runs EXACTLY ONE phase of a review job and returns what the caller should\n * do next; the caller owns the loop.\n *\n * Deliberately not a loop. Every `next_phase` result exists because the next phase needs a fresh\n * host invocation to get a clean subrequest budget, and only the driver can hibernate long enough to\n * produce one (see FRESH_INVOCATION_YIELD_SECONDS in ./phase-control). A loop in here would run the\n * next phase on the current one's spent budget while its TokenTracker restarted at zero -- the exact\n * failure that constant was introduced to fix.\n *\n * Contract for a driver:\n * - 'ack': the job is finished or not ours. Stop.\n * - 'retry': re-deliver the SAME message after `delaySeconds`. Admission was throttled or the lease\n * is held elsewhere; no work happened.\n * - 'next_phase': re-invoke with `{ jobId, phase }` after `delaySeconds`. `freshInstance` means the\n * delay must be long enough to actually hibernate, not merely to wait.\n *\n * Safe to call repeatedly for the same job: it claims a lease first, and every phase is idempotent\n * enough to resume. It throws only on a programming error -- job failures are recorded and acked.\n */\nexport async function runReview(env: ReviewRuntime, message: ReviewJobMessage): Promise<ReviewJobRunResult> {\n const resolved = await resolveQueuedJob(env, message);\n if (!resolved) {\n return { action: 'ack' };\n }\n\n if (resolved.job.status === 'queued') {\n const { concurrencyLevel } = await env.settings.getReviewSettings();\n const maxConcurrentJobs = REVIEW_CONCURRENCY_LIMITS[concurrencyLevel];\n const runningCount = await env.jobs.getOtherRunningJobsCount(resolved.job.id);\n if (runningCount >= maxConcurrentJobs) {\n logger.info(`Throttling admission of job ${resolved.job.id}: ${runningCount} other jobs are currently running.`);\n return { action: 'retry', delaySeconds: 30 };\n }\n }\n\n const leaseOwner = env.ids.randomUUID();\n const claim = await env.jobs.claimJobLease(resolved.job.id, leaseOwner, JOB_LEASE_SECONDS);\n if (claim.status === 'missing') {\n logger.warn(`Job not found for processing: ${resolved.job.id}`);\n return { action: 'ack' };\n }\n if (claim.status === 'terminal') {\n logger.info(`Job ${resolved.job.id} is already terminal (${claim.row.status}), acking queue delivery.`);\n return { action: 'ack' };\n }\n if (claim.status === 'busy') {\n logger.info(`Job ${resolved.job.id} has a fresh lease; retrying queue delivery later.`);\n return { action: 'retry', delaySeconds: Math.min(BUSY_RETRY_SECONDS, claim.retryAfterSeconds) };\n }\n\n const job = env.jobs.mapJob(claim.row);\n\n if (message.workflowInstanceId && job.workflowInstanceId !== message.workflowInstanceId) {\n try {\n await env.jobs.setJobWorkflowInstance(job.id, message.workflowInstanceId);\n } catch (error) {\n logger.warn(`Failed to bind workflow instance id for job ${job.id}`, error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n const phase = resolved.phase;\n const tracker = env.createTokenTracker();\n const github = env.createGitHub(job.installationId, tracker);\n const model = env.createModel(job.id, tracker);\n const formatter = env.createFormatter();\n\n try {\n if (phase === 'prepare') {\n await runPreparePhase(env, job, leaseOwner, github);\n } else if (phase === 'finalize') {\n await runFinalizePhase(env, job, leaseOwner, github, formatter, model);\n } else {\n await runReviewPhase(env, job, leaseOwner, github, model, tracker);\n }\n\n await env.jobs.releaseJobLease(job.id, leaseOwner);\n return { action: 'ack' };\n } catch (error) {\n const messageText = error instanceof Error ? error.message : 'Unknown review failure';\n if (messageText === 'JOB_SUPERSEDED') {\n logger.info(`Job ${job.id} was superseded during execution, stopping.`);\n await env.jobs.releaseJobLease(job.id, leaseOwner);\n return { action: 'ack' };\n }\n\n if (error instanceof NextPhaseError) {\n await env.jobs.releaseJobLease(job.id, leaseOwner);\n return { action: 'next_phase', phase: error.phase, delaySeconds: error.delaySeconds, jobId: job.id, freshInstance: error.phase === 'finalize' };\n }\n\n if (env.modelErrors.isRetryableModelError(error)) {\n const delaySeconds = getRetryableModelFailureDelaySeconds(error);\n logger.warn(`Review job hit transient model/provider failure; scheduling delayed continuation: ${job.owner}/${job.repo} PR #${job.prNumber}`, {\n error: messageText,\n phase,\n delaySeconds,\n });\n return continueOrFailWedgedJob(env, job, github, leaseOwner, phase, delaySeconds, 'transient model/provider failures');\n }\n\n if (isSubrequestBudgetError(error)) {\n const record = error && typeof error === 'object' ? error as { retryAfterSeconds?: unknown } : null;\n const delaySeconds = typeof record?.retryAfterSeconds === 'number'\n ? record.retryAfterSeconds\n : FRESH_INVOCATION_YIELD_SECONDS;\n logger.warn(`Review job hit the per-invocation subrequest limit; rescheduling ${phase} on a fresh budget: ${job.owner}/${job.repo} PR #${job.prNumber}`, {\n error: messageText,\n phase,\n delaySeconds,\n });\n return continueOrFailWedgedJob(env, job, github, leaseOwner, phase, delaySeconds, 'per-invocation subrequest limits');\n }\n\n console.error('JOB FAILED WITH ERROR:', error);\n logger.error(`Review job failed: ${job.owner}/${job.repo} PR #${job.prNumber}`, error);\n await failJobAndCheckRun(env, job, github, messageText);\n await env.jobs.releaseJobLease(job.id, leaseOwner);\n return { action: 'ack' };\n }\n}\n\nasync function continueOrFailWedgedJob(\n env: ReviewRuntime,\n job: PersistedReviewJob,\n github: ReviewGitProvider,\n leaseOwner: string,\n phase: 'prepare' | 'review' | 'finalize',\n delaySeconds: number,\n reason: string,\n): Promise<ReviewJobRunResult> {\n const continuationCount = await env.jobs.markJobContinuationQueued(job.id, delaySeconds);\n\n const ceiling = phase === 'finalize' ? MAX_FINALIZE_CONTINUATIONS : MAX_JOB_CONTINUATIONS;\n\n if (continuationCount > ceiling) {\n if (phase === 'review') {\n logger.error(`Review job exceeded the continuation ceiling; degrading to a partial review: ${job.owner}/${job.repo} PR #${job.prNumber}`, {\n phase,\n continuationCount,\n reason,\n });\n const stillPending = (await env.fileReviews.getFileReviewsForJobs([job.id])).filter(isAwaitingAsyncReview);\n for (const review of stillPending) {\n await persistFailedFileReview(env, job.id, {\n filePath: review.file_path,\n modelUsed: review.async_model ?? review.model_used,\n diffLineCount: review.diff_line_count,\n errorMessage: 'Async batch review did not complete before the job wedged.',\n clearAsync: true,\n });\n }\n await env.jobs.resetJobContinuationCount(job.id);\n await env.jobs.releaseJobLease(job.id, leaseOwner);\n return { action: 'next_phase', phase: 'finalize', delaySeconds: FRESH_INVOCATION_YIELD_SECONDS, jobId: job.id, freshInstance: true };\n } else {\n const message = `Review could not make progress after ${continuationCount} continuation attempts (${reason}). Failing the job to avoid an endless retry loop; re-run it once the underlying provider issue clears.`;\n logger.error(`Review job exceeded the continuation ceiling; failing terminally: ${job.owner}/${job.repo} PR #${job.prNumber}`, {\n phase,\n continuationCount,\n reason,\n });\n await failJobAndCheckRun(env, job, github, message);\n await env.jobs.releaseJobLease(job.id, leaseOwner);\n return { action: 'ack' };\n }\n }\n\n await env.jobs.releaseJobLease(job.id, leaseOwner);\n const freshInstance = reason.includes('subrequest');\n return { action: 'next_phase', phase, delaySeconds, jobId: job.id, freshInstance };\n}\n\nasync function resolveQueuedJob(\n env: ReviewRuntime,\n message: ReviewJobMessage,\n): Promise<{ job: PersistedReviewJob; phase: 'prepare' | 'review' | 'finalize' } | null> {\n if (message.jobId) {\n const row = await env.jobs.getJobForProcessing(message.jobId);\n return row ? { job: env.jobs.mapJob(row), phase: message.phase ?? 'review' } : null;\n }\n\n if (!message.eventName) {\n logger.warn('Queue message ignored: missing eventName');\n return null;\n }\n\n let eventName = message.eventName;\n let payload = message.payload as WebhookPayload | undefined;\n\n if (payload === undefined) {\n const delivery = await env.webhooks.getWebhookDelivery(message.deliveryId);\n if (!delivery) {\n logger.warn(`Queue message ignored: webhook delivery not found: ${message.deliveryId}`);\n return null;\n }\n\n eventName = delivery.event_name;\n payload = delivery.payload as WebhookPayload;\n }\n\n if (eventName !== 'change_request' && eventName !== 'comment') {\n logger.info(`Queue message ignored: unsupported webhook event ${eventName}`);\n return null;\n }\n\n const installationId = String(payload.installationId ?? '');\n if (!installationId || !('repository' in payload) || !payload.repository) {\n logger.info('Queue message ignored: missing installation or repository info');\n return null;\n }\n\n const repoConfig = await env.repoConfig.loadRepoConfig({\n installationId,\n owner: payload.repository.owner,\n repo: payload.repository.name,\n });\n\n if (repoConfig.enabled === false) {\n logger.info(`Job ignored: repository ${payload.repository.owner}/${payload.repository.name} is disabled`);\n return null;\n }\n\n const extracted = extractReviewRequest({\n eventName,\n payload,\n botUsername: env.botUsername,\n config: repoConfig.parsedJson,\n });\n\n if (!extracted) {\n if (eventName === 'change_request') {\n const prPayload = payload as ChangeRequestWebhookPayload;\n if (prPayload.action === 'closed' && repoConfig.parsedJson.review.labels !== false) {\n const labels = repoConfig.parsedJson.review.labels;\n const gh = env.githubClients.forInstallation(installationId);\n await gh.removeIssueLabelsIfPresent(\n prPayload.repository.owner,\n prPayload.repository.name,\n prPayload.changeRequest.number,\n [labels.p1, labels.p2, labels.p3],\n );\n }\n }\n return null;\n }\n\n let resolved = extracted;\n const githubClient = env.githubClients.forInstallation(installationId);\n if (eventName === 'comment') {\n const pr = await githubClient.getPullRequest(extracted.owner, extracted.repo, extracted.prNumber);\n resolved = {\n ...extracted,\n prTitle: pr.title,\n prAuthor: pr.user.login,\n commitSha: pr.head.sha,\n baseSha: pr.base.sha,\n headRef: pr.head.ref,\n baseRef: pr.base.ref,\n };\n }\n\n const duplicateJob = await env.jobs.findExistingJobForHead({\n owner: resolved.owner,\n repo: resolved.repo,\n prNumber: resolved.prNumber,\n commitSha: resolved.commitSha,\n trigger: resolved.trigger,\n });\n if (duplicateJob) {\n if (duplicateJob.status === 'queued' || duplicateJob.status === 'running') {\n logger.info(`Resuming duplicate in-flight job ${duplicateJob.id} for ${resolved.owner}/${resolved.repo} PR #${resolved.prNumber}.`);\n return { job: duplicateJob, phase: message.phase ?? 'prepare' };\n }\n\n logger.info(`Duplicate terminal job found for ${resolved.owner}/${resolved.repo} PR #${resolved.prNumber}, skipping.`);\n return null;\n }\n\n const job = await env.jobs.insertJob({\n installationId: resolved.installationId,\n owner: resolved.owner,\n repo: resolved.repo,\n prNumber: resolved.prNumber,\n prTitle: resolved.prTitle,\n prAuthor: resolved.prAuthor,\n commitSha: resolved.commitSha,\n baseSha: resolved.baseSha,\n trigger: resolved.trigger,\n headRef: resolved.headRef,\n baseRef: resolved.baseRef,\n configSnapshot: repoConfig.parsedJson,\n });\n\n await env.jobs.supersedeOlderJobs({\n installationId: resolved.installationId,\n owner: resolved.owner,\n repo: resolved.repo,\n prNumber: resolved.prNumber,\n newJobId: job.id,\n });\n\n return { job, phase: 'prepare' };\n}\n","import type {\n WebhookEventName,\n WebhookPayload,\n CommentWebhookPayload,\n ChangeRequestWebhookPayload,\n} from '@codraoss/schema/webhook';\nimport type { RepoConfig } from '@codraoss/schema';\n\nfunction shouldTriggerFromChangeRequest(action: ChangeRequestWebhookPayload['action'], config: RepoConfig['review']) {\n return (config.on as string[]).includes(action);\n}\n\nexport type ReviewRequest = {\n installationId: string;\n owner: string;\n repo: string;\n prNumber: number;\n prTitle: string | null;\n prAuthor: string | null;\n commitSha: string;\n baseSha: string;\n headRef: string | null;\n baseRef: string | null;\n trigger: 'auto' | 'mention';\n};\n\nexport function extractReviewRequest(input: {\n eventName: WebhookEventName;\n payload: WebhookPayload;\n botUsername: string;\n config: RepoConfig;\n}): ReviewRequest | null {\n if (input.eventName === 'change_request') {\n const payload = input.payload as ChangeRequestWebhookPayload;\n if (input.config.review.ignore_drafts && payload.changeRequest.draft) {\n return null;\n }\n if (!shouldTriggerFromChangeRequest(payload.action, input.config.review)) {\n return null;\n }\n\n return {\n installationId: payload.installationId,\n owner: payload.repository.owner,\n repo: payload.repository.name,\n prNumber: payload.changeRequest.number,\n prTitle: payload.changeRequest.title,\n prAuthor: payload.changeRequest.author,\n commitSha: payload.changeRequest.head.sha,\n baseSha: payload.changeRequest.base.sha,\n headRef: payload.changeRequest.head.ref,\n baseRef: payload.changeRequest.base.ref,\n trigger: 'auto' as const,\n };\n }\n\n if (input.eventName === 'comment') {\n const payload = input.payload as CommentWebhookPayload;\n const mentionTrigger = input.config.review.mention_trigger;\n\n if (!payload.issue.isChangeRequest || payload.action !== 'created' || !mentionTrigger) {\n return null;\n }\n\n if (!payload.comment.body.includes(mentionTrigger)) {\n return null;\n }\n\n return {\n installationId: payload.installationId,\n owner: payload.repository.owner,\n repo: payload.repository.name,\n prNumber: payload.issue.number,\n prTitle: null,\n prAuthor: null,\n commitSha: '',\n baseSha: '',\n headRef: null,\n baseRef: null,\n trigger: 'mention' as const,\n };\n }\n\n return null;\n}\n","import { reviewMaxFilesRange, type RepoConfig } from '@codraoss/schema';\nimport { filterReviewableFiles, parseUnifiedDiff, type FileDiff } from '../diff';\nimport type { ReviewGitProvider, ReviewRuntime } from '../ports';\nimport { logger } from '../logger';\n\nimport { DIFF_CACHE_TTL_SECONDS } from '../constants';\n\nexport function diffCacheKey(jobId: string) {\n return `diff:${jobId}`;\n}\n\nexport async function getDiffFiles(\n env: Pick<ReviewRuntime, 'kv'>,\n job: { id: string; owner: string; repo: string; prNumber: number },\n github: Pick<ReviewGitProvider, 'getPullRequestDiff'>,\n config: RepoConfig,\n maxFiles: number = reviewMaxFilesRange.default,\n): Promise<{ files: FileDiff[]; skipped: number }> {\n const cacheKey = diffCacheKey(job.id);\n let rawDiff = await env.kv.get(cacheKey);\n\n if (!rawDiff) {\n rawDiff = await github.getPullRequestDiff(job.owner, job.repo, job.prNumber);\n try {\n await env.kv.put(cacheKey, rawDiff, { expirationTtl: DIFF_CACHE_TTL_SECONDS });\n } catch (error) {\n logger.warn(`Failed to cache PR diff for job ${job.id}; it will be re-fetched on the next phase`, error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n return filterReviewableFiles(parseUnifiedDiff(rawDiff, config.review), config.review, maxFiles);\n}\n\nexport async function getOrFetchRawDiffForCompletedJob(\n env: Pick<ReviewRuntime, 'kv'>,\n job: { id: string; owner: string; repo: string; baseSha: string; commitSha: string },\n github: Pick<ReviewGitProvider, 'getCompareDiff'>,\n): Promise<string> {\n const cacheKey = diffCacheKey(job.id);\n const cached = await env.kv.get(cacheKey);\n if (cached) return cached;\n\n const rawDiff = await github.getCompareDiff(job.owner, job.repo, job.baseSha, job.commitSha);\n try {\n await env.kv.put(cacheKey, rawDiff, { expirationTtl: DIFF_CACHE_TTL_SECONDS });\n } catch (error) {\n logger.warn(`Failed to cache reconstructed diff for job ${job.id}`, error instanceof Error ? error : new Error(String(error)));\n }\n return rawDiff;\n}\n","\nimport { FILE_FIXED_SUBREQUESTS, MAX_MODEL_ATTEMPTS_ESTIMATE } from '../constants';\n\nexport function budgetAwareFileLimit(\n remainingSafeBudget: number,\n configuredChunkFileLimit: number,\n modelChainLength = 1,\n fetchesFileContent = false,\n runsSecondaryReviewer = false,\n) {\n const budgetLimit = Math.floor(\n remainingSafeBudget / estimatedSubrequestsPerFile(modelChainLength, fetchesFileContent, runsSecondaryReviewer),\n );\n // Never zero while any budget remains: a file limit of 0 defers every file forever, and a job that\n // can afford one file at a time should make progress one file at a time.\n return Math.max(remainingSafeBudget > 0 ? 1 : 0, Math.min(configuredChunkFileLimit, budgetLimit));\n}\n\nexport function estimatedSubrequestsPerFile(\n modelChainLength: number,\n fetchesFileContent = false,\n runsSecondaryReviewer = false,\n) {\n const modelAttempts = Math.max(1, Math.min(modelChainLength, MAX_MODEL_ATTEMPTS_ESTIMATE));\n // A second reviewer walks its own chain, so it doubles the model half of the estimate but not the\n // fixed per-file cost. Roughly halving files per invocation is the correct answer, not a problem:\n // the continuation loop already carries the rest of the job into the next invocation.\n return FILE_FIXED_SUBREQUESTS\n + modelAttempts * (runsSecondaryReviewer ? 2 : 1)\n + (fetchesFileContent ? 1 : 0);\n}\n","import { renderFileDiff } from '../prompts/file-review';\nimport type { FileDiff } from '../diff';\nimport {\n PACKABLE_MAX_DIFF_LINES,\n BIN_TARGET_DIFF_LINES,\n BIN_MAX_FILES,\n BIN_DIFF_CHAR_BUDGET,\n FRAGMENTED_HUNK_THRESHOLD,\n FRAGMENTED_MIN_LINES,\n} from '../constants';\n\nexport type ReviewUnit =\n | { kind: 'single'; file: FileDiff }\n | { kind: 'bin'; files: FileDiff[]; diffLineCount: number; diffChars: number };\n\nexport type LedgerEntry = { handled: boolean; transientErrorCount: number };\n\nexport function unitFiles(unit: ReviewUnit): FileDiff[] {\n return unit.kind === 'single' ? [unit.file] : unit.files;\n}\n\nconst measure = (file: FileDiff) => renderFileDiff(file).length;\n\n/** Changes scattered thinly across a file rather than concentrated in one place. */\nexport function isFragmented(file: FileDiff): boolean {\n return file.hunks.length >= FRAGMENTED_HUNK_THRESHOLD\n && file.lineCount >= FRAGMENTED_MIN_LINES\n && !file.isNew;\n}\n\nconst asBin = (files: FileDiff[]): ReviewUnit => (files.length === 1\n ? { kind: 'single', file: files[0] }\n : {\n kind: 'bin',\n files,\n diffLineCount: files.reduce((sum, f) => sum + f.lineCount, 0),\n diffChars: files.reduce((sum, f) => sum + measure(f), 0),\n });\n\nexport function planReviewUnits(\n files: readonly FileDiff[],\n opts: { enabled: boolean; fullFileContext?: boolean },\n): ReviewUnit[] {\n if (!opts.enabled) return files.map((file) => ({ kind: 'single', file }));\n\n const units: ReviewUnit[] = [];\n let open: FileDiff[] = [];\n let lines = 0;\n let chars = 0;\n\n const close = () => {\n if (open.length > 0) units.push(asBin(open));\n open = [];\n lines = 0;\n chars = 0;\n };\n\n for (const file of files) {\n const fileChars = measure(file);\n const promoteForContext = opts.fullFileContext === true && isFragmented(file);\n if (promoteForContext || file.lineCount > PACKABLE_MAX_DIFF_LINES || fileChars > BIN_DIFF_CHAR_BUDGET) {\n close();\n units.push({ kind: 'single', file });\n continue;\n }\n\n if (open.length > 0 && (\n lines + file.lineCount > BIN_TARGET_DIFF_LINES\n || chars + fileChars > BIN_DIFF_CHAR_BUDGET\n || open.length >= BIN_MAX_FILES\n )) close();\n\n open.push(file);\n lines += file.lineCount;\n chars += fileChars;\n }\n\n close();\n return units;\n}\n\nexport function narrowUnit(unit: ReviewUnit, ledger: Map<string, LedgerEntry>): ReviewUnit[] {\n const outstanding = unitFiles(unit).filter((file) => !ledger.get(file.path)?.handled);\n if (outstanding.length === 0) return [];\n\n const failedBefore = outstanding.some((file) => (ledger.get(file.path)?.transientErrorCount ?? 0) > 0);\n if (failedBefore) return outstanding.map((file): ReviewUnit => ({ kind: 'single', file }));\n\n return [asBin(outstanding)];\n}\n","import { logger } from '../logger';\nimport type { PersistedReviewJob, ReviewGitProvider, ReviewRuntime } from '../ports';\nimport {\n JOB_LEASE_SECONDS,\n} from '../constants';\n\n// JobSummary, which is exactly what mapJob returns; see the note on the port.\nexport type { PersistedReviewJob };\n\nexport async function heartbeatAndCheckSuperseded(env: ReviewRuntime, jobId: string, leaseOwner: string) {\n await env.jobs.heartbeatJobLease(jobId, leaseOwner, JOB_LEASE_SECONDS);\n const currentJob = await env.jobs.getJobForProcessing(jobId);\n if (currentJob?.status === 'superseded') {\n throw new Error('JOB_SUPERSEDED');\n }\n}\n\nexport class NextPhaseError extends Error {\n constructor(public phase: 'prepare' | 'review' | 'finalize', public delaySeconds: number) {\n super(`NextPhase: ${phase}`);\n }\n}\n\nexport async function enqueueJobPhase(\n env: ReviewRuntime,\n jobId: string,\n phase: 'prepare' | 'review' | 'finalize',\n delaySeconds = 0,\n) {\n await env.jobs.markJobContinuationQueued(jobId, delaySeconds);\n throw new NextPhaseError(phase, delaySeconds);\n}\n\nexport function hasCompletedStep(job: PersistedReviewJob, stepName: string) {\n return job.steps.some((step) => step.name === stepName && step.status === 'done');\n}\n\nexport async function failJobAndCheckRun(\n env: ReviewRuntime,\n job: Pick<PersistedReviewJob, 'id' | 'owner' | 'repo' | 'checkRunId'>,\n github: Pick<ReviewGitProvider, 'updateCheckRun'>,\n message: string,\n) {\n try {\n await env.jobs.failJob(job.id, message);\n } catch (dbError) {\n logger.error(`Critical: failed to mark job ${job.id} as failed in the DB; it may remain stuck until lease-expiry recovery reclaims it`, dbError);\n return;\n }\n\n try {\n const latest = await env.jobs.getJobForProcessing(job.id);\n const checkRunId = latest?.check_run_id ?? job.checkRunId;\n if (checkRunId) {\n await github.updateCheckRun(job.owner, job.repo, checkRunId, {\n status: 'completed',\n conclusion: 'failure',\n title: 'Review failed',\n summary: message,\n });\n await env.jobs.markJobCheckRunCompleted(job.id);\n }\n } catch (checkRunError) {\n logger.warn(`Failed to update GitHub check run for failed job ${job.id}; opportunistic maintenance will retry it`, checkRunError);\n }\n}\n","import { logger } from '../logger';\nimport { normalizeModelId, type RepoConfig } from '@codraoss/schema';\nimport { isSubrequestBudgetMessage, isTimeoutMessage, matchesAnyTransientSubstring } from '@codraoss/schema/transient-errors';\nimport type { ReviewRuntime } from '../ports';\nimport { RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS } from '../constants';\n\n\nexport function isRetryableFileReviewErrorMessage(message: string | null | undefined) {\n if (!message) return false;\n const lower = message.toLowerCase();\n\n if (lower.includes('retrying later') || lower.includes('all configured review models failed')) {\n return true;\n }\n\n if (isTimeoutMessage(lower)) {\n return false;\n }\n\n return (\n matchesAnyTransientSubstring(lower) ||\n lower.includes('google request failed with 5') ||\n lower.includes('temporary') ||\n lower.includes('subrequest')\n );\n}\n\nexport function isSubrequestBudgetError(error: unknown): boolean {\n return isSubrequestBudgetMessage(error);\n}\n\nexport function retryableModelFailureDelaySeconds(failureCount: number | null | undefined) {\n if (!failureCount || failureCount < 1) return RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS[0];\n const index = Math.min(failureCount - 1, RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS.length - 1);\n return RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS[index];\n}\n\nexport function getRetryableModelFailureDelaySeconds(error: unknown) {\n const record = error && typeof error === 'object' ? error as { retryAfterSeconds?: unknown } : null;\n const retryAfterSeconds =\n typeof record?.retryAfterSeconds === 'number'\n ? record.retryAfterSeconds\n : null;\n return retryAfterSeconds ?? RETRYABLE_MODEL_FAILURE_RETRY_DELAYS_SECONDS[0];\n}\n\nexport function shouldRetryExistingFileReview(review: { file_status: string; error_msg: string | null }) {\n return review.file_status === 'failed' && isRetryableFileReviewErrorMessage(review.error_msg);\n}\n\nexport function countsAsHandledFileReview(review: { file_status: string; error_msg: string | null }) {\n return !shouldRetryExistingFileReview(review);\n}\n\nexport function isAwaitingAsyncReview(review: { file_status: string; async_request_id?: string | null }) {\n return review.file_status === 'pending' && !!review.async_request_id;\n}\n\nexport function bareModelId(model: string): string {\n const normalized = normalizeModelId(model);\n const colon = normalized.indexOf(':');\n return colon === -1 ? normalized : normalized.slice(colon + 1);\n}\n\nexport function configuredModelSet(config: RepoConfig) {\n const models = new Set<string>();\n const addModel = (model: string | null | undefined) => {\n if (model) models.add(bareModelId(model));\n };\n\n addModel(config.model?.main);\n for (const fallback of config.model?.fallbacks ?? []) {\n addModel(fallback);\n }\n for (const tier of config.model?.size_overrides ?? []) {\n addModel(tier.model);\n for (const fallback of tier.fallbacks ?? []) {\n addModel(fallback);\n }\n }\n\n return models;\n}\n\nexport function canInheritParentFileReview(config: RepoConfig, review: { model_used: string }) {\n return configuredModelSet(config).has(bareModelId(review.model_used));\n}\n\nexport async function resolveModelProviderName(env: Pick<ReviewRuntime, 'modelConfigs'>, modelId: string | null | undefined) {\n if (!modelId || modelId === 'unconfigured') return null;\n\n try {\n const resolved = await env.modelConfigs.getResolvedModelConfig(normalizeModelId(modelId));\n return resolved?.providerName ?? null;\n } catch (error) {\n logger.warn(`Failed to resolve provider for model ${modelId}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n}\n","import { logger } from '../logger';\nimport { type ParsedReviewComment, type RepoConfig } from '@codraoss/schema';\nimport { parseUnifiedDiff, type FileDiff } from '../diff';\nimport { ruleHitsToComments, scanFileForRuleHits, type RuleScanStats } from '../rules/detect';\nimport type { RejectedExemplar } from '../prompts/file-review';\nimport type { PullRequestRecord, ReviewModel, ReviewRuntime } from '../ports';\nimport { type PersistedReviewJob } from './phase-control';\nimport { FRESH_INVOCATION_YIELD_SECONDS, MAX_RETRYABLE_FILE_REVIEW_FAILURES } from '../constants';\nimport { isSubrequestBudgetError, retryableModelFailureDelaySeconds } from './retry-policy';\n\nexport async function persistCompletedReview(\n env: Pick<ReviewRuntime, 'fileReviews'>,\n job: PersistedReviewJob,\n file: ReturnType<typeof parseUnifiedDiff>[number],\n response: {\n modelUsed: string;\n provider: string;\n inputTokens: number;\n outputTokens: number;\n rawText: string;\n userPrompt: string;\n parsed: {\n comments: ParsedReviewComment[];\n verdict: 'approve' | 'comment';\n fileSummary: string;\n overallCorrectness?: string;\n confidenceScore?: number;\n };\n },\n) {\n await env.fileReviews.upsertFileReview(job.id, {\n filePath: file.path,\n fileStatus: 'done',\n modelUsed: response.modelUsed,\n modelProvider: response.provider,\n diffLineCount: file.lineCount,\n diffInput: null,\n rawAiOutput: response.rawText,\n parsedComments: response.parsed.comments,\n inputTokens: response.inputTokens,\n outputTokens: response.outputTokens,\n durationMs: null,\n verdict: response.parsed.verdict,\n fileSummary: response.parsed.fileSummary,\n overallCorrectness: response.parsed.overallCorrectness,\n confidenceScore: response.parsed.confidenceScore,\n errorMessage: null,\n asyncRequestId: null,\n asyncModel: null,\n });\n}\n\nexport async function persistFailedFileReview(\n env: Pick<ReviewRuntime, 'fileReviews'>,\n jobId: string,\n input: {\n filePath: string;\n modelUsed: string;\n modelProvider?: string | null;\n diffLineCount: number;\n durationMs?: number | null;\n errorMessage: string;\n clearAsync?: boolean;\n parsedComments?: ParsedReviewComment[];\n },\n) {\n await env.fileReviews.upsertFileReview(jobId, {\n filePath: input.filePath,\n fileStatus: 'failed',\n modelUsed: input.modelUsed,\n modelProvider: input.modelProvider ?? null,\n diffLineCount: input.diffLineCount,\n diffInput: null,\n rawAiOutput: null,\n parsedComments: input.parsedComments ?? [],\n inputTokens: null,\n outputTokens: null,\n durationMs: input.durationMs ?? null,\n verdict: null,\n fileSummary: null,\n errorMessage: input.errorMessage,\n ...(input.clearAsync ? { asyncRequestId: null, asyncModel: null } : {}),\n });\n}\n\nexport function scanRuleChannel(\n file: FileDiff,\n config: RepoConfig,\n): { comments: ParsedReviewComment[]; stats: RuleScanStats | null } {\n const rules = config.review.rules;\n if (!rules?.enabled) return { comments: [], stats: null };\n\n try {\n const result = scanFileForRuleHits(file, {\n disabledRuleIds: rules.disabled_rule_ids,\n shadowRuleIds: rules.shadow_rule_ids,\n deniedClaimTypes: config.review.deny_claim_types,\n });\n return { comments: ruleHitsToComments(file, result), stats: result.stats };\n } catch (error) {\n logger.warn(`Rule scan failed for ${file.path}; continuing with LLM findings only`, {\n error: error instanceof Error ? error.message : String(error),\n });\n return { comments: [], stats: null };\n }\n}\n\n/**\n * Marks who found what, for display only.\n *\n * Explicitly NOT for scoring. In the measured corpus, claims found by seven configurations were right\n * 7% of the time against 20% for claims found by one -- so the fact that both reviewers found\n * something is not a reason to trust it more, and this field must never become a weight.\n */\nfunction tagReviewer(comments: ParsedReviewComment[], reviewerModel: string): ParsedReviewComment[] {\n return comments.map((comment) => ({ ...comment, reviewerModel }));\n}\n\n/** Never throws: the primary review already succeeded, and a second opinion is not worth losing it. */\nasync function runSecondaryReview(\n model: ReviewModel,\n params: Parameters<ReviewModel['reviewFile']>[0],\n secondary: { model: string; fallbacks: string[] },\n path: string,\n) {\n try {\n // `selectModel` reads `config.model`, so swapping it is the whole mechanism -- no second runner,\n // no second chain type. `size_overrides` are deliberately not carried: the secondary is one\n // deliberate choice, not a size ladder.\n return await model.reviewFile({\n ...params,\n config: {\n ...params.config,\n model: { ...params.config.model, main: secondary.model, fallbacks: secondary.fallbacks },\n },\n });\n } catch (error) {\n logger.warn(`Secondary reviewer failed for ${path}; keeping the primary review`, {\n model: secondary.model,\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n}\n\nexport async function reviewAndPersistFile(\n env: ReviewRuntime,\n job: PersistedReviewJob,\n file: ReturnType<typeof parseUnifiedDiff>[number],\n pr: PullRequestRecord,\n config: RepoConfig,\n totalLineCount: number,\n model: ReviewModel,\n resolveFailureModelProvider: () => Promise<string | null>,\n previousReview?: { transient_error_count: number },\n rejectedExemplars: readonly RejectedExemplar[] = [],\n changelogExcerpt: string | null = null,\n fileContext: string | null = null,\n) {\n const startedAt = env.clock.now();\n const compactPrompt = (previousReview?.transient_error_count ?? 0) > 0;\n\n const ruleScan = scanRuleChannel(file, config);\n\n try {\n const reviewParams = {\n file,\n fileContext,\n prTitle: pr.title ?? null,\n prDescription: pr.body ?? null,\n changelogExcerpt,\n config,\n totalLineCount,\n compactPrompt,\n rejectedExemplars,\n };\n\n const response = await model.reviewFile(reviewParams);\n\n // A second, independent reviewer over the same file. Its findings are UNIONED with the primary's:\n // the measured gain from two reviewers is entirely coverage, and nothing here counts agreement.\n //\n // Best-effort by construction. The primary's result already exists, so a failing secondary must\n // never cost the file -- it logs and the review stands on the primary alone. Skipped when\n // `compactPrompt` is set, because that flag means the last attempt was already too much.\n const secondary = config.model?.secondary ?? null;\n const secondaryReview = secondary && !compactPrompt\n ? await runSecondaryReview(model, reviewParams, secondary, file.path)\n : null;\n\n const llmComments = [\n ...tagReviewer(response.parsed.comments, response.modelUsed),\n ...(secondaryReview ? tagReviewer(secondaryReview.parsed.comments, secondaryReview.modelUsed) : []),\n ];\n\n await env.fileReviews.upsertFileReview(job.id, {\n filePath: file.path,\n fileStatus: 'done',\n modelUsed: response.modelUsed,\n modelProvider: response.provider,\n diffLineCount: file.lineCount,\n diffInput: null,\n rawAiOutput: response.rawText,\n // One row per file, always: `file_reviews` is unique on (job_id, file_path), and review\n // inheritance, resume and finalize all assume that. The two reviewers merge into it.\n parsedComments: [...llmComments, ...ruleScan.comments],\n inputTokens: response.inputTokens + (secondaryReview?.inputTokens ?? 0),\n outputTokens: response.outputTokens + (secondaryReview?.outputTokens ?? 0),\n durationMs: env.clock.now() - startedAt,\n verdict: response.parsed.verdict,\n fileSummary: response.parsed.fileSummary,\n overallCorrectness: response.parsed.overallCorrectness,\n confidenceScore: response.parsed.confidenceScore,\n errorMessage: null,\n withheldCounts: {\n evidence: (response.parsed.evidenceStats?.unmatched ?? 0)\n + (response.parsed.evidenceStats?.absent ?? 0)\n + (response.parsed.evidenceStats?.weak ?? 0),\n claimDenied: Object.values(response.parsed.deniedClaimCounts ?? {}).reduce((sum, n) => sum + n, 0),\n // Findings about code the diff never touched. Counted apart from the evidence gate: those are\n // findings whose quote could not be found at all, these are ones that were found in the wrong\n // place, and only the second number says anything about how the reviewer is misreading a PR.\n contextOnly: response.parsed.evidenceStats?.contextOnly ?? 0,\n // \"X is missing\", answered by finding X. Counted so the gate's real hit rate is visible.\n absenceRefuted: response.parsed.absenceCheckStats?.refuted ?? 0,\n },\n // Only logged until now, which made \"how often did a review run unconstrained or truncated?\"\n // unanswerable without reading the logs of every job one at a time.\n degraded: response.degraded ?? null,\n });\n\n logger.info(`File review parsed: ${file.path}`, {\n jobId: job.id,\n model: response.modelUsed,\n kept: response.parsed.comments.length,\n evidence: response.parsed.evidenceStats,\n claimTypes: response.parsed.claimTypeCounts,\n deniedClaims: response.parsed.deniedClaimCounts,\n absenceCheck: response.parsed.absenceCheckStats,\n ruleChannel: ruleScan.stats,\n degraded: response.degraded,\n });\n\n if (response.wasPromptTruncated) {\n logger.warn(`Reviewed only part of ${file.path}; findings from the remainder are missing.`, {\n jobId: job.id,\n model: response.modelUsed,\n reviewedLineCount: response.reviewedLineCount,\n diffLineCount: file.lineCount,\n });\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown file review error';\n const modelId = config.model?.main ?? 'unconfigured';\n const modelProvider = await resolveFailureModelProvider();\n\n if (isSubrequestBudgetError(error)) {\n logger.warn(`File review deferred for ${file.path}; subrequest budget will retry in a fresh invocation`, {\n error: errorMessage,\n });\n Object.defineProperty(error, 'retryAfterSeconds', {\n value: FRESH_INVOCATION_YIELD_SECONDS,\n configurable: true,\n });\n throw error;\n }\n\n if (env.modelErrors.isRetryableModelError(error)) {\n const failureCount = await env.fileReviews.recordRetryableFileReviewFailure(job.id, {\n filePath: file.path,\n modelUsed: modelId,\n modelProvider,\n diffLineCount: file.lineCount,\n diffInput: null,\n durationMs: env.clock.now() - startedAt,\n errorMessage,\n countsAsAttempt: env.modelErrors.nextChainIndexOf(error) === null,\n });\n\n if (failureCount >= MAX_RETRYABLE_FILE_REVIEW_FAILURES) {\n const finalError = `Review skipped after ${failureCount} repeated model provider outages.`;\n await persistFailedFileReview(env, job.id, {\n filePath: file.path,\n modelUsed: modelId,\n modelProvider,\n diffLineCount: file.lineCount,\n durationMs: env.clock.now() - startedAt,\n errorMessage: finalError,\n parsedComments: ruleScan.comments,\n });\n logger.error(`File review failed permanently for ${file.path} after transient retries`, {\n attempts: failureCount,\n error: errorMessage,\n });\n return;\n }\n\n logger.warn(`File review deferred for ${file.path}; transient model/provider failure will retry later`, {\n error: errorMessage,\n attempts: failureCount,\n });\n Object.defineProperty(error, 'retryAfterSeconds', {\n value: retryableModelFailureDelaySeconds(failureCount),\n configurable: true,\n });\n throw error;\n }\n\n logger.error(`File review failed for ${file.path}`, { error });\n\n const isHardLimit =\n errorMessage.includes('4006') ||\n errorMessage.toLowerCase().includes('allocation');\n\n if (isHardLimit) {\n logger.warn(`File review hit hard provider allocation limit for ${file.path}, marking as failed to allow partial PR review.`, { error: errorMessage });\n }\n\n await persistFailedFileReview(env, job.id, {\n filePath: file.path,\n modelUsed: modelId,\n modelProvider,\n diffLineCount: file.lineCount,\n durationMs: env.clock.now() - startedAt,\n errorMessage,\n parsedComments: ruleScan.comments,\n });\n }\n}\n","import { logger } from '../logger';\nimport type { RepoConfig } from '@codraoss/schema';\nimport type { FileDiff } from '../diff';\nimport { renderFileDiff, type RejectedExemplar } from '../prompts/file-review';\nimport type { BulkFileReviewInput, PullRequestRecord, ReviewModel, ReviewRuntime } from '../ports';\nimport { type PersistedReviewJob } from './phase-control';\nimport { FRESH_INVOCATION_YIELD_SECONDS, MAX_RETRYABLE_FILE_REVIEW_FAILURES, MISSING_FILE_ERROR } from '../constants';\nimport { isSubrequestBudgetError, retryableModelFailureDelaySeconds } from './retry-policy';\nimport { scanRuleChannel } from './file-runner';\n\n\n\nexport function proportionalSplit(total: number, weights: number[]): number[] {\n if (weights.length === 0) return [];\n\n const sum = weights.reduce((a, b) => a + b, 0);\n const parts = sum <= 0\n ? weights.map(() => Math.floor(total / weights.length))\n : weights.map((w) => Math.floor((total * w) / sum));\n\n const assigned = parts.reduce((a, b) => a + b, 0);\n if (assigned < total) {\n const largest = weights.indexOf(Math.max(...weights));\n parts[largest === -1 ? 0 : largest] += total - assigned;\n }\n return parts;\n}\n\nexport async function reviewAndPersistBin(\n env: ReviewRuntime,\n job: PersistedReviewJob,\n files: FileDiff[],\n pr: PullRequestRecord,\n config: RepoConfig,\n totalLineCount: number,\n model: ReviewModel,\n resolveFailureModelProvider: () => Promise<string | null>,\n rejectedExemplars: readonly RejectedExemplar[] = [],\n changelogExcerpt: string | null = null,\n): Promise<number> {\n const startedAt = env.clock.now();\n\n const ruleScans = new Map(files.map((file) => [file.path, scanRuleChannel(file, config)]));\n\n const persisted = new Set<string>();\n let terminalCount = 0;\n\n const failedRow = (file: FileDiff, errorMessage: string, modelProvider?: string | null): BulkFileReviewInput => ({\n filePath: file.path,\n fileStatus: 'failed',\n modelUsed: config.model?.main ?? 'unconfigured',\n modelProvider: modelProvider ?? null,\n diffLineCount: file.lineCount,\n rawAiOutput: null,\n parsedComments: ruleScans.get(file.path)?.comments ?? [],\n inputTokens: null,\n outputTokens: null,\n durationMs: env.clock.now() - startedAt,\n verdict: null,\n fileSummary: null,\n errorMessage,\n batchSize: files.length,\n });\n\n try {\n const response = await model.reviewFiles({\n files,\n prTitle: pr.title ?? null,\n prDescription: pr.body ?? null,\n changelogExcerpt,\n config,\n totalLineCount,\n rejectedExemplars,\n });\n\n const reviewed = files.filter((file) => response.batch.reviews.has(file.path));\n const weights = reviewed.map((file) => renderFileDiff(file).length);\n const inputSplit = proportionalSplit(response.inputTokens, weights);\n const outputSplit = proportionalSplit(response.outputTokens, weights);\n const durationMs = env.clock.now() - startedAt;\n\n const rows: BulkFileReviewInput[] = reviewed.map((file, index) => {\n const parsed = response.batch.reviews.get(file.path)!;\n const rules = ruleScans.get(file.path)!;\n return {\n filePath: file.path,\n fileStatus: 'done',\n modelUsed: response.modelUsed,\n modelProvider: response.provider,\n diffLineCount: file.lineCount,\n rawAiOutput: response.rawText,\n parsedComments: [...parsed.comments, ...rules.comments],\n inputTokens: inputSplit[index],\n outputTokens: outputSplit[index],\n durationMs,\n verdict: parsed.verdict,\n fileSummary: parsed.fileSummary,\n overallCorrectness: parsed.overallCorrectness,\n confidenceScore: parsed.confidenceScore,\n errorMessage: null,\n withheldCounts: {\n evidence: (parsed.evidenceStats?.unmatched ?? 0)\n + (parsed.evidenceStats?.absent ?? 0)\n + (parsed.evidenceStats?.weak ?? 0),\n claimDenied: Object.values(parsed.deniedClaimCounts ?? {}).reduce((sum, n) => sum + n, 0),\n contextOnly: parsed.evidenceStats?.contextOnly ?? 0,\n absenceRefuted: parsed.absenceCheckStats?.refuted ?? 0,\n },\n // The whole bin shared one call, so every member inherits its degradation.\n degraded: response.degraded ?? null,\n batchSize: files.length,\n };\n });\n\n if (rows.length > 0) {\n await env.fileReviews.bulkUpsertFileReviews(job.id, rows);\n for (const row of rows) persisted.add(row.filePath);\n terminalCount += rows.length;\n }\n\n if (response.batch.missing.length > 0) {\n const counts = await env.fileReviews.bulkRecordRetryableFileReviewFailures(job.id, response.batch.missing.map((path) => ({\n filePath: path,\n modelUsed: response.modelUsed,\n diffLineCount: files.find((f) => f.path === path)?.lineCount ?? 0,\n errorMessage: MISSING_FILE_ERROR,\n })));\n for (const count of counts) persisted.add(count.filePath);\n\n const exhausted = counts.filter((c) => c.transientErrorCount >= MAX_RETRYABLE_FILE_REVIEW_FAILURES);\n if (exhausted.length > 0) {\n await env.fileReviews.bulkUpsertFileReviews(job.id, exhausted.map((c) => failedRow(\n files.find((f) => f.path === c.filePath)!,\n `Review skipped after the model omitted this file ${c.transientErrorCount} times.`,\n )));\n terminalCount += exhausted.length;\n }\n }\n\n logger.info('Batched file review parsed', {\n jobId: job.id,\n model: response.modelUsed,\n binSize: files.length,\n binPaths: files.map((f) => f.path),\n binDiffLines: files.reduce((sum, f) => sum + f.lineCount, 0),\n durationMs,\n inputTokens: response.inputTokens,\n outputTokens: response.outputTokens,\n keptPerFile: rows.map((r) => ({ path: r.filePath, kept: r.parsedComments.length })),\n entriesReturned: response.batch.stats.entriesReturned,\n missingFiles: response.batch.missing,\n unroutableEntries: response.batch.stats.unroutableEntries,\n pathMismatchFindings: response.batch.stats.pathMismatchFindings,\n ambiguousAcrossBin: response.batch.stats.ambiguousAcrossBin,\n flatFallback: response.batch.stats.flatFallback,\n overCap: response.batch.stats.overCap,\n });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown batched review error';\n const modelId = config.model?.main ?? 'unconfigured';\n const modelProvider = await resolveFailureModelProvider();\n\n if (isSubrequestBudgetError(error)) {\n logger.warn('Batched review deferred; subrequest budget will retry in a fresh invocation', {\n jobId: job.id,\n paths: files.map((f) => f.path),\n error: errorMessage,\n });\n Object.defineProperty(error, 'retryAfterSeconds', { value: FRESH_INVOCATION_YIELD_SECONDS, configurable: true });\n throw error;\n }\n\n const outstanding = files.filter((file) => !persisted.has(file.path));\n\n if (outstanding.length === 0) {\n logger.warn('Batched review hit an error after every file was persisted; keeping the committed rows', {\n jobId: job.id,\n paths: files.map((f) => f.path),\n error: errorMessage,\n });\n return terminalCount;\n }\n\n if (env.modelErrors.isRetryableModelError(error)) {\n const advancedTo = env.modelErrors.nextChainIndexOf(error);\n const counts = await env.fileReviews.bulkRecordRetryableFileReviewFailures(job.id, outstanding.map((file) => ({\n filePath: file.path,\n modelUsed: modelId,\n diffLineCount: file.lineCount,\n errorMessage,\n })), { countsAsAttempt: advancedTo === null });\n\n const exhausted = counts.filter((c) => c.transientErrorCount >= MAX_RETRYABLE_FILE_REVIEW_FAILURES);\n if (exhausted.length > 0) {\n await env.fileReviews.bulkUpsertFileReviews(job.id, exhausted.map((c) => failedRow(\n files.find((f) => f.path === c.filePath)!,\n `Review skipped after ${c.transientErrorCount} repeated model provider outages.`,\n modelProvider,\n )));\n terminalCount += exhausted.length;\n logger.error('Files in a batched review failed permanently after transient retries', {\n jobId: job.id,\n paths: exhausted.map((c) => c.filePath),\n error: errorMessage,\n });\n }\n\n const stillRetrying = counts.filter((c) => c.transientErrorCount < MAX_RETRYABLE_FILE_REVIEW_FAILURES);\n if (stillRetrying.length === 0) return terminalCount;\n\n logger.warn('Batched review deferred; transient model/provider failure will retry later', {\n jobId: job.id,\n paths: stillRetrying.map((c) => c.filePath),\n error: errorMessage,\n });\n Object.defineProperty(error, 'retryAfterSeconds', {\n value: retryableModelFailureDelaySeconds(Math.max(...stillRetrying.map((c) => c.transientErrorCount))),\n configurable: true,\n });\n throw error;\n }\n\n logger.error('Batched review failed', { jobId: job.id, paths: outstanding.map((f) => f.path), error });\n\n await env.fileReviews.bulkUpsertFileReviews(job.id, outstanding.map((file) => failedRow(file, errorMessage, modelProvider)));\n terminalCount += outstanding.length;\n }\n\n return terminalCount;\n}\n","import type { FindingDisposition, ParsedReviewComment, RepoConfig } from '@codraoss/schema';\nimport type { FileDiff } from './diff';\nimport type { ReviewModel } from './ports';\nimport { renderDiffSnippet, parseVerifyResponse, type VerifyCandidate } from './prompts/verify';\nimport { logger } from './logger';\nimport { reviewBreadth } from './prompts/file-review';\n\ntype VerifiableJob = { id: string };\n\nconst LOW_YIELD_TITLE = /missing|redundant|repetitive|inconsisten|documentation|\\btype\\b|\\bany\\b|potential/i;\n\nexport function shadowEvaluate(candidates: ParsedReviewComment[], posted: ParsedReviewComment[]) {\n const postedSet = new Set(posted);\n const count = (predicate: (c: ParsedReviewComment) => boolean) => ({\n wouldDrop: candidates.filter(predicate).length,\n wouldDropPosted: posted.filter(predicate).length,\n });\n\n return {\n candidates: candidates.length,\n posted: postedSet.size,\n dropP3AndNit: count((c) => c.severity === 'P3' || c.severity === 'nit'),\n dropLowYieldTitle: count((c) => LOW_YIELD_TITLE.test(c.title)),\n dropUnmatchedEvidence: count((c) => !c.evidence),\n };\n}\n\nfunction verifyCandidateLimit(breadth: number) {\n return Math.min(40, Math.max(10, breadth * 3));\n}\n\nimport { VERIFY_MIN_ANSWER_RATIO } from './constants';\n\nexport type VerifyDrop = {\n comment: ParsedReviewComment;\n disposition: Extract<FindingDisposition, 'verify' | 'verify_unanswered'>;\n reason?: string;\n};\n\n/** `null` means verification ran; any other value means findings were posted unverified. */\nexport type VerifySkipReason = 'no_verifiable_candidates' | 'low_answer_ratio' | 'verify_call_failed';\n\nexport type VerifyOutcome = {\n comments: ParsedReviewComment[];\n dropped: VerifyDrop[];\n reasons: Map<ParsedReviewComment, string>;\n skipped: VerifySkipReason | null;\n};\n\nexport async function verifyFindings(params: {\n job: VerifiableJob;\n config: RepoConfig;\n files: FileDiff[];\n comments: ParsedReviewComment[];\n model: Pick<ReviewModel, 'verifyFindings'>;\n maxCandidates?: number;\n}): Promise<VerifyOutcome> {\n const { comments, files, model, config, job } = params;\n\n const keepAll = (skipped: VerifySkipReason | null): VerifyOutcome => ({\n comments,\n dropped: [],\n reasons: new Map(),\n skipped,\n });\n\n if (comments.length === 0) return keepAll(null);\n\n const limit = verifyCandidateLimit(params.maxCandidates ?? reviewBreadth(config.review));\n const toVerify = comments.slice(0, limit);\n\n const fileByPath = new Map(files.map((file) => [file.path, file]));\n const prepared = toVerify.map((comment) => ({\n comment,\n snippet: renderDiffSnippet(fileByPath.get(comment.path), comment.line ?? undefined),\n }));\n\n const verifiable = prepared.filter((entry) => entry.snippet !== '' || entry.comment.evidence);\n if (verifiable.length === 0) return keepAll('no_verifiable_candidates');\n\n const candidates: VerifyCandidate[] = verifiable.map((entry, index) => ({\n index,\n path: entry.comment.path,\n line: entry.comment.line ?? null,\n title: entry.comment.title,\n body: entry.comment.body,\n snippet: entry.snippet,\n evidence: entry.comment.evidence ?? null,\n }));\n\n try {\n const response = await model.verifyFindings({ candidates, config });\n const results = parseVerifyResponse(response.rawText);\n\n const byIndex = new Map<number, { verdict: 'keep' | 'drop'; reason?: string }>();\n const conflicting = new Set<number>();\n for (const result of results) {\n if (!Number.isInteger(result.index) || result.index < 0 || result.index >= candidates.length) continue;\n const verdict = result.decidable === false ? 'drop' as const : result.verdict;\n const prior = byIndex.get(result.index);\n if (prior && prior.verdict !== verdict) {\n conflicting.add(result.index);\n continue;\n }\n if (!prior) byIndex.set(result.index, { verdict, reason: result.reason });\n }\n for (const index of conflicting) byIndex.delete(index);\n\n const answered = byIndex.size;\n if (answered === 0 || answered / candidates.length < VERIFY_MIN_ANSWER_RATIO) {\n logger.warn('Verification did not answer enough indices; keeping all findings', {\n jobId: job.id, candidates: candidates.length, answered,\n });\n return keepAll('low_answer_ratio');\n }\n\n const dropped: VerifyDrop[] = [];\n const reasons = new Map<ParsedReviewComment, string>();\n\n verifiable.forEach((entry, index) => {\n const result = byIndex.get(index);\n if (result?.reason) reasons.set(entry.comment, result.reason);\n\n if (result?.verdict === 'drop') {\n dropped.push({ comment: entry.comment, disposition: 'verify', reason: result.reason });\n return;\n }\n if (!result) {\n dropped.push({\n comment: entry.comment,\n disposition: 'verify_unanswered',\n reason: 'the verifier returned no verdict for this finding',\n });\n }\n });\n\n const droppedSet = new Set(dropped.map((drop) => drop.comment));\n logger.info('Verification pass complete', {\n jobId: job.id,\n candidates: candidates.length,\n answered,\n dropped: dropped.length,\n topReasons: dropped.slice(0, 5).map((drop) => drop.reason),\n });\n\n return { comments: comments.filter((comment) => !droppedSet.has(comment)), dropped, reasons, skipped: null };\n } catch (error) {\n logger.warn('Verification pass failed; posting pre-verification findings', {\n jobId: job.id,\n error: error instanceof Error ? error.message : String(error),\n });\n return keepAll('verify_call_failed');\n }\n}\n","import { logger } from '../logger';\nimport { defaultRepoConfig, type RepoConfig } from '@codraoss/schema';\nimport type { ReviewGitProvider, ReviewRuntime } from '../ports';\nimport { getDiffFiles } from './diff-cache';\nimport type { RejectedExemplar } from '../prompts/file-review';\nimport { type PersistedReviewJob, enqueueJobPhase } from './phase-control';\nimport { JOB_LEASE_SECONDS, FRESH_INVOCATION_YIELD_SECONDS } from '../constants';\n\nexport async function runPreparePhase(\n env: ReviewRuntime,\n job: PersistedReviewJob,\n leaseOwner: string,\n github: ReviewGitProvider,\n) {\n await env.jobs.updateJobStep(job.id, 'Preparation', { status: 'running' });\n const pr = await github.getPullRequest(job.owner, job.repo, job.prNumber);\n const config = (job.configSnapshot ?? defaultRepoConfig) as RepoConfig;\n\n try {\n await env.jobs.setJobPullRequestMeta(job.id, {\n prTitle: pr.title ?? null,\n prAuthor: pr.user?.login ?? null,\n });\n } catch (error) {\n logger.warn(`Failed to refresh PR metadata for job ${job.id}`, error instanceof Error ? error : new Error(String(error)));\n }\n\n let checkRunId = job.checkRunId;\n if (!checkRunId) {\n const checkRun = await github.createCheckRun(job.owner, job.repo, {\n headSha: pr.head.sha,\n title: 'Review queued',\n summary: 'Codra has started reviewing this pull request.',\n });\n checkRunId = checkRun.id;\n await env.jobs.updateJobCheckRun(job.id, checkRun.id);\n }\n\n const { maxFiles } = await env.settings.getReviewSettings();\n const { files } = await getDiffFiles(env, job, github, config, maxFiles);\n await env.jobs.completePreparationStep(job.id, files.length);\n await env.jobs.heartbeatJobLease(job.id, leaseOwner, JOB_LEASE_SECONDS);\n\n if (files.length === 0) {\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'done' });\n await enqueueJobPhase(env, job.id, 'finalize', FRESH_INVOCATION_YIELD_SECONDS);\n return;\n }\n\n if (checkRunId) {\n try {\n await github.updateCheckRun(job.owner, job.repo, checkRunId, {\n title: `Reviewing (0/${files.length})`,\n summary: 'Codra is analyzing changed files.',\n });\n } catch (error) {\n logger.warn(`Failed to update initial progress check run for job ${job.id}; continuing to the review phase anyway`, error instanceof Error ? error : new Error(String(error)));\n }\n }\n await enqueueJobPhase(env, job.id, 'review', FRESH_INVOCATION_YIELD_SECONDS);\n}\n\nexport async function loadRejectedExemplars(env: Pick<ReviewRuntime, 'learning'>, job: PersistedReviewJob): Promise<RejectedExemplar[]> {\n try {\n const repositoryId = await env.learning.getRepositoryIdForJob(job.id);\n if (repositoryId === null) return [];\n const rows = await env.learning.getRejectedExemplars({ repositoryId, limit: 5 });\n return rows.map((row) => ({ title: row.title, claimType: row.claim_type }));\n } catch (error) {\n logger.warn('Could not load rejected exemplars; reviewing without them', {\n jobId: job.id,\n error: error instanceof Error ? error.message : String(error),\n });\n return [];\n }\n}\n","import { logger } from '../logger';\nimport { defaultRepoConfig, REVIEW_CONCURRENCY_LIMITS, type ParsedReviewComment, type RepoConfig } from '@codraoss/schema';\nimport { budgetAwareFileLimit } from './budget';\nimport { narrowUnit, planReviewUnits } from './pack';\nimport { reviewAndPersistBin } from './bin-runner';\nimport { getDiffFiles } from './diff-cache';\nimport { changelogExcerptFromDiff, wantsFileContext } from '../prompts/file-review';\nimport { loadFileContext } from './file-context';\nimport type { ReviewGitProvider, ReviewModel, ReviewRuntime } from '../ports';\nimport { TokenTracker } from '../token-tracker';\nimport {\n type PersistedReviewJob,\n NextPhaseError,\n enqueueJobPhase,\n hasCompletedStep,\n heartbeatAndCheckSuperseded,\n} from './phase-control';\nimport {\n ASYNC_BATCH_POLL_DELAY_SECONDS,\n FRESH_INVOCATION_YIELD_SECONDS,\n MAX_JOB_CONTINUATIONS,\n REVIEW_CHUNK_WALL_CLOCK_MS,\n} from '../constants';\nimport {\n canInheritParentFileReview,\n countsAsHandledFileReview,\n isAwaitingAsyncReview,\n isSubrequestBudgetError,\n resolveModelProviderName,\n} from './retry-policy';\nimport { loadRejectedExemplars, runPreparePhase } from './prepare';\nimport { persistCompletedReview, persistFailedFileReview, reviewAndPersistFile } from './file-runner';\n\nexport async function runReviewPhase(\n env: ReviewRuntime,\n job: PersistedReviewJob,\n leaseOwner: string,\n github: ReviewGitProvider,\n model: ReviewModel,\n tracker: TokenTracker,\n) {\n if (!hasCompletedStep(job, 'Preparation')) {\n await runPreparePhase(env, job, leaseOwner, github);\n return;\n }\n\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'running' });\n\n const [rejectedExemplars, pr] = await Promise.all([\n loadRejectedExemplars(env, job),\n github.getPullRequest(job.owner, job.repo, job.prNumber),\n ]);\n const config = (job.configSnapshot ?? defaultRepoConfig) as RepoConfig;\n const failureModelId = config.model?.main ?? 'unconfigured';\n let failureModelProviderPromise: Promise<string | null> | null = null;\n const resolveFailureModelProvider = () => {\n failureModelProviderPromise ??= resolveModelProviderName(env, failureModelId);\n return failureModelProviderPromise;\n };\n const { concurrencyLevel, maxFiles } = await env.settings.getReviewSettings();\n const { files } = await getDiffFiles(env, job, github, config, maxFiles);\n const totalLineCount = files.reduce((sum, file) => sum + file.lineCount, 0);\n const changelogExcerpt = changelogExcerptFromDiff(files);\n const configuredChunkFileLimit = REVIEW_CONCURRENCY_LIMITS[concurrencyLevel];\n const modelChainLength = 1 + (config.model.fallbacks?.length ?? 0);\n const reviewChunkFileLimit = budgetAwareFileLimit(\n tracker.remainingSafeBudget(),\n configuredChunkFileLimit,\n modelChainLength,\n config.review.full_file_context,\n // A second reviewer walks its own chain per file, so fewer files fit in one invocation.\n Boolean(config.model?.secondary),\n );\n if (reviewChunkFileLimit <= 0) {\n throw new Error('Subrequest budget for this invocation was exhausted before starting the next review chunk.');\n }\n const startedAt = env.clock.now();\n let processedThisChunk = 0;\n\n const jobIdsToQuery = [job.id];\n if (job.retryOfJobId) jobIdsToQuery.push(job.retryOfJobId);\n const allExistingReviews = await env.fileReviews.getFileReviewsForJobs(jobIdsToQuery);\n type ExistingReview = (typeof allExistingReviews)[number];\n const currentReviews = new Map<string, ExistingReview>();\n const parentReviews = new Map<string, ExistingReview>();\n for (const review of allExistingReviews) {\n if (review.job_id === job.id) currentReviews.set(review.file_path, review);\n else if (review.file_status === 'done') parentReviews.set(review.file_path, review);\n }\n\n const reviewTasks: Array<Promise<void>> = [];\n let terminalProgress = 0;\n let awaitingAsync = 0;\n\n if (job.retryOfJobId && parentReviews.size > 0) {\n const inheritablePaths = files.flatMap((file) => {\n if (currentReviews.has(file.path)) return [];\n const parent = parentReviews.get(file.path);\n return parent && canInheritParentFileReview(config, parent) ? [file.path] : [];\n });\n\n if (inheritablePaths.length > 0) {\n const inheritedPaths = await env.fileReviews.bulkInheritFileReviews({\n jobId: job.id,\n parentJobId: job.retryOfJobId,\n filePaths: inheritablePaths,\n });\n for (const path of inheritedPaths) {\n const parent = parentReviews.get(path);\n if (parent) currentReviews.set(path, parent);\n }\n terminalProgress += inheritedPaths.length;\n if (inheritedPaths.length > 0) {\n logger.info(`Bulk-inherited ${inheritedPaths.length} parent file reviews for job ${job.id} in one pass`);\n }\n }\n }\n\n const binnedPaths = new Set<string>();\n if (config.review.batch_small_files) {\n const ledger = new Map(files.map((file) => {\n const existing = currentReviews.get(file.path);\n const inheritable = parentReviews.get(file.path);\n return [file.path, {\n handled: Boolean((existing && countsAsHandledFileReview(existing)) || (inheritable && canInheritParentFileReview(config, inheritable))),\n transientErrorCount: existing?.transient_error_count ?? 0,\n }];\n }));\n\n const units = planReviewUnits(files, { enabled: true, fullFileContext: config.review.full_file_context }).flatMap((unit) => narrowUnit(unit, ledger));\n const plannedBins = units.filter((unit) => unit.kind === 'bin');\n let binsDispatched = 0;\n let filesDispatchedInBins = 0;\n\n for (const unit of plannedBins) {\n if (processedThisChunk >= reviewChunkFileLimit) break;\n if (env.clock.now() - startedAt >= REVIEW_CHUNK_WALL_CLOCK_MS) break;\n\n const binFiles = unit.kind === 'bin' ? unit.files : [];\n binFiles.forEach((file) => binnedPaths.add(file.path));\n reviewTasks.push((async () => {\n const terminal = await reviewAndPersistBin(env, job, binFiles, pr, config, totalLineCount, model, resolveFailureModelProvider, rejectedExemplars, changelogExcerpt);\n terminalProgress += terminal;\n })());\n processedThisChunk += 1;\n binsDispatched += 1;\n filesDispatchedInBins += binFiles.length;\n }\n\n if (plannedBins.length > 0) {\n logger.info('Batched review plan', {\n jobId: job.id,\n binsPlanned: plannedBins.length,\n binsDispatched,\n filesInBins: filesDispatchedInBins,\n modelCallsSaved: filesDispatchedInBins - binsDispatched,\n });\n }\n }\n\n for (const file of files) {\n if (binnedPaths.has(file.path)) continue;\n\n const existingReview = currentReviews.get(file.path);\n const awaitingReview = existingReview && isAwaitingAsyncReview(existingReview) ? existingReview : null;\n if (existingReview && countsAsHandledFileReview(existingReview) && !awaitingReview) {\n continue;\n }\n\n if (!awaitingReview && processedThisChunk >= reviewChunkFileLimit) {\n continue;\n }\n\n const inherited = parentReviews.get(file.path);\n let fileContextPromise: Promise<string | null> | null = null;\n const fileContextFor = () => {\n if (!wantsFileContext(file, config.review.full_file_context, {\n compactPrompt: (existingReview?.transient_error_count ?? 0) > 0,\n })) {\n return Promise.resolve(null);\n }\n fileContextPromise ??= loadFileContext(github, job, file, () => tracker.incrementSubrequests(1));\n return fileContextPromise;\n };\n const reviewTask = async () => {\n if (awaitingReview) {\n const poll = await model.pollReviewBatch({\n model: awaitingReview.async_model ?? awaitingReview.model_used,\n requestId: awaitingReview.async_request_id!,\n file,\n config,\n });\n if (poll.status === 'pending') {\n awaitingAsync += 1;\n return;\n }\n if (poll.status === 'failed') {\n logger.warn(`Async batch poll failed for ${file.path}; falling back to synchronous review`, {\n error: poll.error instanceof Error ? poll.error.message : String(poll.error),\n });\n await reviewAndPersistFile(env, job, file, pr, config, totalLineCount, model, resolveFailureModelProvider, existingReview, rejectedExemplars, changelogExcerpt, await fileContextFor());\n terminalProgress += 1;\n return;\n }\n await persistCompletedReview(env, job, file, poll.response);\n terminalProgress += 1;\n return;\n }\n\n if (!inherited) {\n const submitted = await model.submitReviewBatch({\n file,\n fileContext: await fileContextFor(),\n prTitle: pr.title ?? null,\n prDescription: pr.body ?? null,\n changelogExcerpt,\n config,\n totalLineCount,\n compactPrompt: (existingReview?.transient_error_count ?? 0) > 0,\n });\n if (submitted) {\n await env.fileReviews.upsertFileReview(job.id, {\n filePath: file.path,\n fileStatus: 'pending',\n modelUsed: submitted.model,\n modelProvider: null,\n diffLineCount: file.lineCount,\n diffInput: null,\n rawAiOutput: null,\n parsedComments: [],\n inputTokens: null,\n outputTokens: null,\n durationMs: null,\n verdict: null,\n fileSummary: null,\n overallCorrectness: null,\n confidenceScore: null,\n errorMessage: null,\n asyncRequestId: submitted.requestId,\n asyncModel: submitted.model,\n });\n awaitingAsync += 1;\n return;\n }\n await reviewAndPersistFile(env, job, file, pr, config, totalLineCount, model, resolveFailureModelProvider, existingReview, rejectedExemplars, changelogExcerpt, await fileContextFor());\n terminalProgress += 1;\n return;\n }\n\n if (!canInheritParentFileReview(config, inherited)) {\n logger.info(`Ignoring inherited review for ${file.path}; parent model ${inherited.model_used} is not in the current model strategy`);\n await reviewAndPersistFile(env, job, file, pr, config, totalLineCount, model, resolveFailureModelProvider, existingReview, rejectedExemplars, changelogExcerpt, await fileContextFor());\n terminalProgress += 1;\n } else {\n await env.fileReviews.upsertFileReview(job.id, {\n filePath: file.path,\n fileStatus: 'done',\n modelUsed: inherited.model_used,\n modelProvider: inherited.model_provider,\n diffLineCount: inherited.diff_line_count,\n diffInput: inherited.diff_input,\n rawAiOutput: inherited.raw_ai_output,\n parsedComments: inherited.parsed_comments as ParsedReviewComment[],\n inputTokens: inherited.input_tokens,\n outputTokens: inherited.output_tokens,\n durationMs: inherited.duration_ms,\n verdict: inherited.verdict,\n fileSummary: inherited.file_summary,\n overallCorrectness: inherited.overall_correctness,\n confidenceScore: inherited.confidence_score,\n errorMessage: null,\n });\n currentReviews.set(file.path, inherited);\n terminalProgress += 1;\n }\n };\n\n reviewTasks.push(reviewTask());\n if (!awaitingReview) processedThisChunk += 1;\n\n if (env.clock.now() - startedAt >= REVIEW_CHUNK_WALL_CLOCK_MS) {\n break;\n }\n }\n\n const results = await Promise.allSettled(reviewTasks);\n await heartbeatAndCheckSuperseded(env, job.id, leaseOwner);\n\n if (terminalProgress > 0) {\n await env.jobs.resetJobContinuationCount(job.id);\n }\n\n logger.info('Review chunk model usage', {\n jobId: job.id,\n subrequests: tracker.getSubrequestCount(),\n usage: tracker.getTotalUsage(),\n wasted: tracker.getWasted(),\n });\n\n const rejected = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected');\n if (rejected.length > 0) {\n rejected.forEach((result, index) => {\n logger.error(`Review chunk task ${index + 1}/${rejected.length} failed`, result.reason);\n });\n\n const deferrableError = rejected.map(r => r.reason).find(r => env.modelErrors.isRetryableModelError(r) || isSubrequestBudgetError(r));\n if (deferrableError) {\n throw deferrableError;\n }\n\n throw rejected.length === 1\n ? rejected[0].reason\n : new AggregateError(rejected.map((result) => result.reason), `${rejected.length} review chunk tasks failed`);\n }\n\n const latestReviews = await env.fileReviews.getFileReviewsForJobs([job.id]);\n const reviewedPaths = new Set(\n latestReviews.flatMap((review) => (\n countsAsHandledFileReview(review) && !isAwaitingAsyncReview(review) ? [review.file_path] : []\n )),\n );\n const completedCount = files.filter((file) => reviewedPaths.has(file.path)).length;\n\n if (completedCount >= files.length) {\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'done' });\n await enqueueJobPhase(env, job.id, 'finalize', FRESH_INVOCATION_YIELD_SECONDS);\n return;\n }\n\n if (awaitingAsync > 0 && terminalProgress === 0) {\n const pollCount = await env.jobs.markJobContinuationQueued(job.id, ASYNC_BATCH_POLL_DELAY_SECONDS);\n if (pollCount > MAX_JOB_CONTINUATIONS) {\n logger.error(`Async batch reviews did not complete after ${pollCount} polls; degrading to a partial review: ${job.owner}/${job.repo} PR #${job.prNumber}`);\n for (const review of latestReviews.filter(isAwaitingAsyncReview)) {\n await persistFailedFileReview(env, job.id, {\n filePath: review.file_path,\n modelUsed: review.async_model ?? review.model_used,\n diffLineCount: review.diff_line_count,\n errorMessage: 'Async batch review did not complete in time.',\n clearAsync: true,\n });\n }\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'done' });\n throw new NextPhaseError('finalize', FRESH_INVOCATION_YIELD_SECONDS);\n }\n throw new NextPhaseError('review', ASYNC_BATCH_POLL_DELAY_SECONDS);\n }\n\n if (job.checkRunId) {\n try {\n await github.updateCheckRun(job.owner, job.repo, job.checkRunId, {\n title: `Reviewing (${completedCount}/${files.length})`,\n summary: 'Codra is continuing this review in the next queue chunk.',\n });\n } catch (error) {\n logger.warn(`Failed to update progress check run for job ${job.id}; continuing to the next chunk anyway`, error instanceof Error ? error : new Error(String(error)));\n }\n }\n await enqueueJobPhase(env, job.id, 'review', FRESH_INVOCATION_YIELD_SECONDS);\n}\n","import type { FileDiff } from '../diff';\nimport type { ReviewGitProvider } from '../ports';\nimport { logger } from '../logger';\n\nconst VALIDATION_SAMPLES = 5;\n\nexport function contentMatchesDiff(file: FileDiff, content: string): boolean {\n const lines = content.split('\\n');\n const samples: Array<{ newLineNumber: number; content: string }> = [];\n\n for (const hunk of file.hunks) {\n for (const line of hunk.lines) {\n if (line.kind === 'del' || typeof line.newLineNumber !== 'number') continue;\n if (!line.content.trim()) continue;\n samples.push({ newLineNumber: line.newLineNumber, content: line.content });\n }\n }\n if (samples.length === 0) return false;\n\n const step = Math.max(1, Math.floor(samples.length / VALIDATION_SAMPLES));\n for (let i = 0; i < samples.length; i += step) {\n const sample = samples[i];\n if (lines[sample.newLineNumber - 1] !== sample.content) return false;\n }\n return true;\n}\n\nexport async function loadFileContext(\n github: Pick<ReviewGitProvider, 'getRepoFile'>,\n job: { owner: string; repo: string; commitSha: string },\n file: FileDiff,\n onFetch?: () => void,\n): Promise<string | null> {\n if (!github.getRepoFile) return null;\n\n let content: string | null;\n try {\n onFetch?.();\n content = await github.getRepoFile(job.owner, job.repo, file.path, job.commitSha);\n } catch (error) {\n logger.warn(`Could not fetch file content for ${file.path}; reviewing the diff alone`, {\n error: error instanceof Error ? error.message : String(error),\n });\n return null;\n }\n\n if (!content) return null;\n if (!contentMatchesDiff(file, content)) {\n logger.warn(`Fetched content for ${file.path} does not line up with the diff; reviewing the diff alone`, {\n commitSha: job.commitSha,\n });\n return null;\n }\n return content;\n}\n","import { logger } from '../logger';\nimport { defaultRepoConfig, type ParsedReviewComment, type RepoConfig } from '@codraoss/schema';\nimport { shadowEvaluate } from '../finding-gates';\nimport { getDiffFiles } from './diff-cache';\nimport type { ReviewFormatter, ReviewGitProvider, ReviewModel, ReviewRuntime } from '../ports';\nimport {\n type PersistedReviewJob,\n enqueueJobPhase,\n heartbeatAndCheckSuperseded,\n} from './phase-control';\nimport { FRESH_INVOCATION_YIELD_SECONDS } from '../constants';\nimport { sendReviewTelemetry } from './telemetry';\nimport { applyFindingGates } from './gate-pipeline';\n\n/**\n * Why a completed review is less than the whole pull request, for the job record the dashboard reads.\n *\n * Files dropped by the limits count as partial too. They used to be reported only in the PR comment,\n * which no longer carries that line, so without this a truncated run reports plain success -- which is\n * how a 250-file pull request reviewed 25 files and said nothing.\n */\nexport function partialReviewMessage(input: {\n failedFileCount: number;\n reviewedFileCount: number;\n filesOverCap: number;\n}): string | null {\n const plural = (n: number) => (n === 1 ? '' : 's');\n const reasons: string[] = [];\n\n if (input.failedFileCount > 0) {\n reasons.push(`${input.failedFileCount} of ${input.reviewedFileCount} file${plural(input.reviewedFileCount)} could not be reviewed`);\n }\n if (input.filesOverCap > 0) {\n reasons.push(`${input.filesOverCap} file${plural(input.filesOverCap)} left out by the file and diff-size limits`);\n }\n\n return reasons.length > 0 ? `Partial review: ${reasons.join('; ')}.` : null;\n}\n\nexport async function runFinalizePhase(\n env: ReviewRuntime,\n job: PersistedReviewJob,\n leaseOwner: string,\n github: ReviewGitProvider,\n formatter: ReviewFormatter,\n model: ReviewModel,\n) {\n await env.jobs.updateJobStep(job.id, 'Generating Summary', { status: 'running' });\n\n const pr = await github.getPullRequest(job.owner, job.repo, job.prNumber);\n const config = (job.configSnapshot ?? defaultRepoConfig) as RepoConfig;\n const reviewSettings = await env.settings.getReviewSettings();\n const [{ files, skipped: filesOverCap }, initialReviews] = await Promise.all([\n getDiffFiles(env, job, github, config, reviewSettings.maxFiles),\n env.fileReviews.getFileReviewsForJobs([job.id]),\n ]);\n let reviews = initialReviews;\n\n {\n const reviewedPaths = new Set(reviews.map((r) => r.file_path));\n const missingFiles = files.filter((f) => !reviewedPaths.has(f.path));\n\n if (missingFiles.length > 0) {\n logger.warn(`Job ${job.id} reached finalize phase with ${missingFiles.length} missing file reviews. Forcing them to failed state.`);\n await env.fileReviews.bulkMarkFilesFailed(\n job.id,\n missingFiles.map((file) => ({ filePath: file.path, diffLineCount: file.lineCount })),\n { modelUsed: config.model?.main ?? 'unconfigured', errorMessage: 'This file was not reviewed before the review run completed.' },\n );\n\n reviews = await env.fileReviews.getFileReviewsForJobs([job.id]);\n } else if (reviews.length < files.length) {\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'running' });\n await enqueueJobPhase(env, job.id, 'review', FRESH_INVOCATION_YIELD_SECONDS);\n return;\n }\n }\n\n await env.jobs.updateJobStep(job.id, 'Reviewing Files', { status: 'done' });\n\n const reviewedComments = reviews.flatMap((review) => review.parsed_comments as ParsedReviewComment[]);\n const fileSummaries = reviews.map((review) => ({\n path: review.file_path,\n summary: review.file_status === 'failed'\n ? `Review failed: ${review.error_msg ?? 'Unknown file review error'}`\n : (review.file_summary ?? ''),\n verdict: review.file_status === 'failed' ? 'failed' : (review.verdict ?? 'comment'),\n }));\n\n const { concurrencyLevel, maxComments: globalMaxComments } = reviewSettings;\n const effectiveMaxComments = Math.min(config.review.max_comments, globalMaxComments);\n const retryCount = job.retryOfJobId ? 1 : 0;\n\n if (fileSummaries.length > 0 && fileSummaries.every((file) => file.verdict === 'failed')) {\n await env.jobs.updateJobStep(job.id, 'Generating Summary', { status: 'failed', error: 'All files failed to review' });\n\n await sendReviewTelemetry(\n env,\n job,\n files,\n reviews,\n { findingsReported: 0, verdict: 'failed', severityDistribution: {} },\n { concurrencyLevel, retryCount },\n );\n\n throw new Error('All files failed to review');\n }\n\n const hasFailures = fileSummaries.some((file) => file.verdict === 'failed');\n const failedFileCount = fileSummaries.filter((file) => file.verdict === 'failed').length;\n\n await env.jobs.updateJobStep(job.id, 'Verifying Findings', { status: 'running' });\n\n const {\n finalComments,\n dispositions,\n verifyReasons,\n verificationSkipped,\n suppressedComments,\n droppedBySuppression,\n beforeVerifyList,\n droppedByVerification,\n droppedByCap,\n omittedCount,\n droppedByFilters,\n withheldByParser,\n byClaimType,\n } = await applyFindingGates({\n env, job, config, files, model, effectiveMaxComments, reviewedComments, reviews,\n });\n\n\n logger.info('Finding pipeline outcome', {\n jobId: job.id,\n parsed: reviewedComments.length,\n verificationSkipped,\n droppedByFilters,\n droppedBySuppression,\n droppedByVerification,\n droppedByCap,\n posted: finalComments.length,\n withheldByParser,\n byClaimType,\n byChannel: {\n llm: finalComments.filter((c) => c.source !== 'rule').length,\n rule: finalComments.filter((c) => c.source === 'rule').length,\n },\n byRule: reviewedComments.reduce<Record<string, number>>((acc, c) => {\n if (c.source === 'rule' && c.ruleId) acc[c.ruleId] = (acc[c.ruleId] ?? 0) + 1;\n return acc;\n }, {}),\n postedAny: finalComments.length > 0,\n postedPer100Files: files.length > 0\n ? Math.round((finalComments.length / files.length) * 1000) / 10\n : 0,\n });\n\n logger.info('Shadow filter evaluation', {\n jobId: job.id,\n ...shadowEvaluate(beforeVerifyList, finalComments),\n });\n\n // Failed on every skip reason: each one means findings were posted unverified.\n await env.jobs.updateJobStep(job.id, 'Verifying Findings', verificationSkipped\n ? { status: 'failed', error: `Verification did not run (${verificationSkipped}); findings were posted unverified.` }\n : { status: 'done' });\n\n const rawVerdict = formatter.summarizeVerdict([...finalComments, ...suppressedComments], hasFailures);\n\n const everythingWithheld = finalComments.length === 0\n && suppressedComments.length === 0\n && (withheldByParser > 0 || omittedCount > 0);\n const verdictSummary = everythingWithheld && rawVerdict.verdict === 'approve'\n ? { ...rawVerdict, verdict: 'comment' as const }\n : rawVerdict;\n await env.jobs.updateJobStep(job.id, 'Generating Summary', { status: 'done' });\n await heartbeatAndCheckSuperseded(env, job.id, leaseOwner);\n\n const formattedSummary = formatter.formatReviewOverview({\n commitSha: pr.head.sha,\n postedFindings: finalComments.length,\n filesReviewed: files.length,\n linesReviewed: files.reduce((sum, file) => sum + file.lineCount, 0),\n withheldFindings: withheldByParser + droppedByFilters + droppedByVerification,\n filesFailed: failedFileCount,\n });\n\n // Skipped-file counts are dashboard information, not PR content: skips have more than one cause.\n if (filesOverCap > 0) {\n logger.info('Some reviewable files were skipped by the file or diff-size limits', {\n jobId: job.id,\n filesOverCap,\n reviewed: files.length,\n maxFiles: reviewSettings.maxFiles,\n });\n }\n\n const finalizeRetriedPastPost = job.steps.some(\n (step) => step.name === 'Completing' && (step.status === 'running' || step.status === 'done'),\n );\n await env.jobs.updateJobStep(job.id, 'Completing', { status: 'running' });\n const existingReview: { id: number; postedIndices?: number[] } | null = finalizeRetriedPastPost\n ? await github.findBotReviewForCommit(job.owner, job.repo, job.prNumber, pr.head.sha, env.botUsername)\n : null;\n const review = existingReview ?? await github.createReview(job.owner, job.repo, job.prNumber, {\n commitSha: pr.head.sha,\n event: formatter.toReviewEvent(verdictSummary.verdict),\n body: formattedSummary,\n comments: finalComments.map(comment => ({\n path: comment.path,\n line: comment.line ?? undefined,\n side: 'RIGHT' as const,\n position: comment.position ?? undefined,\n body: formatter.formatInlineComment(comment),\n })),\n });\n\n if (review.postedIndices && review.postedIndices.length > 0) {\n const postedFingerprints = review.postedIndices\n .map((index) => finalComments[index]?.fingerprint)\n .filter((fingerprint): fingerprint is string => Boolean(fingerprint));\n await env.fileReviews.markCommentsPosted(job.id, postedFingerprints);\n }\n\n // A clean pass also gets a thumbs-up on the pull request's opening post, so the author sees the\n // outcome without opening the review. Best-effort: reacting is decoration, and losing it must never\n // fail a job that already posted its review. GitHub returns the existing reaction on a repeat, so a\n // retried finalize does not duplicate it.\n if (finalComments.length === 0 && github.addIssueReaction) {\n try {\n await github.addIssueReaction(job.owner, job.repo, job.prNumber, '+1');\n } catch (error) {\n logger.warn('Could not react to the pull request', {\n jobId: job.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n try {\n const withReasons = new Map<string, { disposition: string | null; reason: string | null }>();\n for (const fingerprint of new Set([...dispositions.keys(), ...verifyReasons.keys()])) {\n withReasons.set(fingerprint, {\n disposition: dispositions.get(fingerprint) ?? null,\n reason: verifyReasons.get(fingerprint) ?? null,\n });\n }\n await env.fileReviews.markCommentDispositions(job.id, withReasons);\n } catch (error) {\n logger.warn('Could not record finding dispositions', {\n jobId: job.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n\n const fileInputTokens = reviews.reduce((sum, review) => sum + (review.input_tokens ?? 0), 0);\n const fileOutputTokens = reviews.reduce((sum, review) => sum + (review.output_tokens ?? 0), 0);\n\n const severityDistribution: Record<string, number> = {};\n for (const comment of finalComments) {\n const sev = comment.severity || 'unknown';\n severityDistribution[sev] = (severityDistribution[sev] || 0) + 1;\n }\n\n const partialErrorMessage = partialReviewMessage({\n failedFileCount: hasFailures ? failedFileCount : 0,\n reviewedFileCount: files.length,\n filesOverCap,\n });\n await env.jobs.completeJob(job.id, {\n verdict: verdictSummary.verdict,\n fileCount: files.length,\n commentCount: finalComments.length,\n totalInputTokens: fileInputTokens,\n totalOutputTokens: fileOutputTokens,\n summaryMarkdown: formattedSummary,\n reviewId: review.id,\n summaryModel: null,\n errorMessage: partialErrorMessage,\n });\n logger.info(`Review job completed: ${job.owner}/${job.repo} PR #${job.prNumber}`);\n\n try {\n if (job.checkRunId) {\n await github.updateCheckRun(job.owner, job.repo, job.checkRunId, {\n status: 'completed',\n conclusion: hasFailures ? 'failure' : (verdictSummary.verdict === 'approve' ? 'success' : 'neutral'),\n title: hasFailures ? 'Review partially failed' : (verdictSummary.verdict === 'approve' ? 'LGTM' : 'Comments posted'),\n summary: `${finalComments.length} inline comments across ${files.length} files.${hasFailures ? ` ${failedFileCount} file${failedFileCount === 1 ? '' : 's'} could not be reviewed.` : ''}`,\n });\n await env.jobs.markJobCheckRunCompleted(job.id);\n }\n\n if (config.review.labels !== false) {\n const labels = config.review.labels;\n const labelMap = {\n comment: { name: labels.p1, color: 'f79009' },\n approve: { name: labels.p2, color: '027a48' },\n } as const;\n const label = labelMap[verdictSummary.verdict];\n\n await github.removeIssueLabelsIfPresent(\n job.owner,\n job.repo,\n job.prNumber,\n [labels.p1, labels.p2, labels.p3].filter(possibleLabel => possibleLabel !== label.name),\n );\n\n await github.ensureLabel(job.owner, job.repo, label.name, label.color);\n await github.addIssueLabels(job.owner, job.repo, job.prNumber, [label.name]);\n }\n } catch (error) {\n logger.warn(`Post-review labels/check-run update failed for job ${job.id}; review is posted and job is completed, so leaving it best-effort`, error instanceof Error ? error : new Error(String(error)));\n }\n\n await sendReviewTelemetry(\n env,\n job,\n files,\n reviews,\n { findingsReported: finalComments.length, verdict: verdictSummary.verdict, severityDistribution },\n { concurrencyLevel, retryCount },\n );\n}\n","import { logger } from '../logger';\nimport type { ReviewRuntime } from '../ports';\nimport { type PersistedReviewJob } from './phase-control';\nimport { bareModelId } from './retry-policy';\n\nexport async function sendReviewTelemetry(\n env: ReviewRuntime,\n job: PersistedReviewJob,\n files: Array<{ path: string; lineCount: number }>,\n reviews: Array<{ file_status: string; input_tokens: number | null; output_tokens: number | null; model_used: string }>,\n overrides: { findingsReported: number; verdict: string; severityDistribution: Record<string, number> },\n meta: { concurrencyLevel: string; retryCount: number },\n) {\n try {\n const doneReviews = reviews.filter((r) => r.file_status === 'done');\n\n const cleanModels = Array.from(\n new Set(\n doneReviews.flatMap((r) => {\n const model = bareModelId(r.model_used);\n return model && !model.toLowerCase().includes('test') ? [model] : [];\n }),\n ),\n );\n\n const extractExtension = (filePath: string): string => {\n const name = filePath.split('/').pop() || filePath;\n const dotIndex = name.lastIndexOf('.');\n if (dotIndex <= 0) return '';\n return name.slice(dotIndex + 1).toLowerCase();\n };\n\n await env.telemetry.send({\n linesReviewed: files.reduce((sum, file) => sum + file.lineCount, 0),\n inputTokens: doneReviews.reduce((sum, r) => sum + (r.input_tokens ?? 0), 0),\n outputTokens: doneReviews.reduce((sum, r) => sum + (r.output_tokens ?? 0), 0),\n modelsUsed: cleanModels,\n fileExtensions: Array.from(new Set(files.flatMap((f) => {\n const extension = extractExtension(f.path);\n return extension ? [extension] : [];\n }))),\n triggerType: job.trigger,\n reviewDurationMs: Math.max(0, env.clock.now() - new Date(job.createdAt).getTime()),\n filesReviewed: files.length,\n concurrencyLevel: meta.concurrencyLevel,\n prTotalLinesChanged: files.reduce((sum, file) => sum + file.lineCount, 0),\n retryCount: meta.retryCount,\n ...overrides,\n });\n } catch (e) {\n logger.error('Failed to send telemetry', e instanceof Error ? e : new Error(String(e)));\n }\n}\n\nexport async function loadSuppressedFingerprints(env: Pick<ReviewRuntime, 'fileReviews'>, jobId: string) {\n const posted = new Map<string, Set<string>>();\n const rejected = new Set<string>();\n const postedV2 = new Set<string>();\n const rejectedV2 = new Set<string>();\n\n try {\n for (const row of await env.fileReviews.getSuppressedFindings(jobId)) {\n if (!row.anchored) {\n if (row.fingerprint) rejected.add(row.fingerprint);\n if (row.fingerprint_v2) rejectedV2.add(row.fingerprint_v2);\n continue;\n }\n if (row.fingerprint_v2) postedV2.add(row.fingerprint_v2);\n if (!row.fingerprint || !row.anchor_hash) continue;\n const anchors = posted.get(row.fingerprint) ?? new Set<string>();\n anchors.add(row.anchor_hash);\n posted.set(row.fingerprint, anchors);\n }\n } catch (error) {\n logger.warn('Could not load suppressed findings; posting without cross-run dedupe', {\n jobId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n\n return { posted, rejected, postedV2, rejectedV2 };\n}\n","import { dedupeFindings } from '../model-output';\nimport { verifyFindings } from '../finding-gates';\nimport type { FindingDisposition, ParsedReviewComment, RepoConfig } from '@codraoss/schema';\nimport type { FileDiff } from '../diff';\nimport type { PersistedReviewJob } from './phase-control';\nimport type { ReviewModel, ReviewRuntime } from '../ports';\nimport { loadSuppressedFingerprints } from './telemetry';\nimport { reviewBreadth } from '../prompts/file-review';\nimport { getLanguageForFile } from '../prompts/languages';\n\nexport async function applyFindingGates(params: {\n env: Pick<ReviewRuntime, 'fileReviews'>;\n job: PersistedReviewJob;\n config: RepoConfig;\n files: FileDiff[];\n model: Pick<ReviewModel, 'verifyFindings'>;\n effectiveMaxComments: number;\n reviewedComments: ParsedReviewComment[];\n reviews: Array<{ withheld_counts?: { evidence?: number; claimDenied?: number; contextOnly?: number; absenceRefuted?: number } | null }>;\n}) {\n const { env, job, config, files, model, effectiveMaxComments, reviewedComments, reviews } = params;\n\n const severityRanks: Record<string, number> = { P0: 0, P1: 1, P2: 2, P3: 3, nit: 4 };\n const minRank = severityRanks[config.review.min_severity] ?? 4;\n const minConfidence = config.review.min_confidence ?? 0;\n\n // Precision varies 5.8x by language in the measured corpus, and until now every gate was global.\n // Resolved per finding from its own path, so a mixed-language pull request is judged per file rather\n // than by whatever the repo is mostly written in.\n const languageGates = config.review.language_gates ?? {};\n const gatesByLanguage = new Map(\n Object.entries(languageGates).map(([language, gate]) => [language.toLowerCase(), gate]),\n );\n const thresholdsFor = (path: string) => {\n if (gatesByLanguage.size === 0) return { minRank, minConfidence };\n const language = getLanguageForFile(path)?.language;\n const override = language ? gatesByLanguage.get(language.toLowerCase()) : undefined;\n if (!override) return { minRank, minConfidence };\n return {\n minRank: override.min_severity ? (severityRanks[override.min_severity] ?? 4) : minRank,\n minConfidence: override.min_confidence ?? minConfidence,\n };\n };\n\n const dispositions = new Map<string, FindingDisposition>();\n const verifyReasons = new Map<string, string>();\n const recordDisposition = (comments: ParsedReviewComment[], stage: FindingDisposition) => {\n for (const comment of comments) {\n if (comment.fingerprint && !dispositions.has(comment.fingerprint)) {\n dispositions.set(comment.fingerprint, stage);\n }\n }\n };\n\n let finalComments = reviewedComments.filter((c) => {\n const thresholds = thresholdsFor(c.path);\n if ((severityRanks[c.severity] ?? 4) > thresholds.minRank) {\n recordDisposition([c], 'severity');\n return false;\n }\n if (typeof c.confidenceScore === 'number' && c.confidenceScore < thresholds.minConfidence) {\n recordDisposition([c], 'confidence');\n return false;\n }\n return true;\n });\n\n const suppressed = await loadSuppressedFingerprints(env, job.id);\n const suppressedComments: ParsedReviewComment[] = [];\n const hasSuppressionData = suppressed.rejected.size > 0 || suppressed.posted.size > 0\n || suppressed.rejectedV2.size > 0 || suppressed.postedV2.size > 0;\n if (hasSuppressionData) {\n finalComments = finalComments.filter((c) => {\n const rejected = (c.fingerprint && suppressed.rejected.has(c.fingerprint))\n || (c.fingerprintV2 && suppressed.rejectedV2.has(c.fingerprintV2));\n\n const anchors = c.fingerprint ? suppressed.posted.get(c.fingerprint) : undefined;\n const alreadyPosted = (anchors && c.anchorHash && anchors.has(c.anchorHash))\n || (c.fingerprintV2 && suppressed.postedV2.has(c.fingerprintV2));\n\n if (rejected || alreadyPosted) {\n suppressedComments.push(c);\n return false;\n }\n return true;\n });\n }\n const droppedBySuppression = suppressedComments.length;\n recordDisposition(suppressedComments, 'suppression');\n\n const beforeDedupe = finalComments;\n finalComments = dedupeFindings(finalComments);\n const survivedDedupe = new Set(finalComments);\n recordDisposition(beforeDedupe.filter((c) => !survivedDedupe.has(c)), 'dedupe');\n\n finalComments.sort((a, b) => {\n const rankDiff = (severityRanks[a.severity] ?? 4) - (severityRanks[b.severity] ?? 4);\n if (rankDiff !== 0) return rankDiff;\n return (b.confidenceScore ?? 0) - (a.confidenceScore ?? 0);\n });\n\n const beforeVerifyList = finalComments;\n const verify = await verifyFindings({ job, config, files, comments: finalComments, model, maxCandidates: reviewBreadth(config.review) });\n finalComments = verify.comments;\n const droppedByVerification = verify.dropped.length;\n for (const drop of verify.dropped) recordDisposition([drop.comment], drop.disposition);\n for (const [comment, reason] of verify.reasons) {\n if (comment.fingerprint) verifyReasons.set(comment.fingerprint, reason);\n }\n\n const beforeCapList = finalComments;\n const beforeCap = finalComments.length;\n if (finalComments.length > effectiveMaxComments) {\n finalComments = finalComments.slice(0, effectiveMaxComments);\n }\n const droppedByCap = beforeCap - finalComments.length;\n recordDisposition(beforeCapList.slice(effectiveMaxComments), 'cap');\n const omittedCount = reviewedComments.length - finalComments.length;\n const droppedByFilters = omittedCount - droppedBySuppression - droppedByVerification - droppedByCap;\n\n const withheldByParser = reviews.reduce(\n (sum, review) => sum\n + (review.withheld_counts?.evidence ?? 0)\n + (review.withheld_counts?.claimDenied ?? 0)\n // Counted here too, or a file whose findings were ALL about untouched code looks like a file\n // with nothing to say, and `everythingWithheld` lets the PR be approved silently.\n + (review.withheld_counts?.contextOnly ?? 0)\n + (review.withheld_counts?.absenceRefuted ?? 0),\n 0,\n );\n\n const byClaimType: Record<string, { generated: number; posted: number }> = {};\n for (const comment of reviewedComments) {\n const key = comment.claimType ?? 'unlabelled';\n byClaimType[key] ??= { generated: 0, posted: 0 };\n byClaimType[key].generated += 1;\n }\n for (const comment of finalComments) {\n const key = comment.claimType ?? 'unlabelled';\n byClaimType[key] ??= { generated: 0, posted: 0 };\n byClaimType[key].posted += 1;\n }\n return {\n finalComments,\n dispositions,\n verifyReasons,\n // Non-null means these findings were never checked. The caller records it on the job, so a review\n // that skipped verification stops looking identical to one that passed it.\n verificationSkipped: verify.skipped,\n suppressedComments,\n droppedBySuppression,\n beforeVerifyList,\n droppedByVerification,\n droppedByCap,\n omittedCount,\n droppedByFilters,\n withheldByParser,\n byClaimType,\n };\n}\n"],"mappings":"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","names":["REVIEW_CONCURRENCY_LIMITS","defaultRepoConfig","defaultRepoConfig","defaultRepoConfig","defaultRepoConfig","review","REVIEW_CONCURRENCY_LIMITS"]}
|