import type { FileChangeWithContent } from '@shared/types'; import type { FileChangeSummary } from '@shared/types/review'; export type ReviewRejectBlockReason = | 'missing-on-disk' | 'content-unavailable' | 'manual-ledger-review' | 'baseline-unavailable'; type ReviewContentAvailability = Pick< FileChangeWithContent, 'contentSource' | 'originalFullContent' | 'modifiedFullContent' >; export function hasReviewSnippetText(file: Pick): boolean { return file.snippets.some( (snippet) => !snippet.isError && (snippet.oldString.length > 0 || snippet.newString.length > 0) ); } export function getLastWriteSnippetContent( file: Pick ): string | null { const writeSnippets = file.snippets.filter( (snippet) => !snippet.isError && (snippet.type === 'write-new' || snippet.type === 'write-update') ); if (writeSnippets.length === 0) return null; return writeSnippets[writeSnippets.length - 1]?.newString ?? null; } export function getResolvedReviewModifiedContent( file: Pick, fileContent: Pick | null ): string | null { return fileContent?.modifiedFullContent ?? getLastWriteSnippetContent(file); } export function isReviewFileMissingOnDisk( fileContent: Pick | null ): boolean { return fileContent ? fileContent.modifiedFullContent == null : false; } export function isReviewTextContentUnavailable( file: Pick, fileContent: Pick | null ): boolean { return ( fileContent?.contentSource === 'unavailable' && getResolvedReviewModifiedContent(file, fileContent) === null ); } export function requiresManualLedgerReview(file: Pick): boolean { return file.snippets.some( (snippet) => !!snippet.ledger && (!!snippet.ledger.beforeState?.unavailableReason || !!snippet.ledger.afterState?.unavailableReason) && (snippet.ledger.originalFullContent == null || snippet.ledger.modifiedFullContent == null) ); } export function getReviewRejectBlockReason( file: Pick, fileContent: ReviewContentAvailability | null ): ReviewRejectBlockReason | null { if (isReviewFileMissingOnDisk(fileContent)) return 'missing-on-disk'; if (isReviewTextContentUnavailable(file, fileContent)) return 'content-unavailable'; if (requiresManualLedgerReview(file)) return 'manual-ledger-review'; if (!fileContent) { return file.snippets.length > 0 && !hasReviewSnippetText(file) ? 'baseline-unavailable' : null; } const modified = getResolvedReviewModifiedContent(file, fileContent); if (modified == null) return 'baseline-unavailable'; if (file.isNewFile) return fileContent.originalFullContent === '' ? null : 'baseline-unavailable'; return fileContent.originalFullContent == null ? 'baseline-unavailable' : null; } export function isReviewRejectable( file: Pick, fileContent: ReviewContentAvailability | null ): boolean { return getReviewRejectBlockReason(file, fileContent) === null; } export function shouldRenderCurrentDiskContextPreview( file: Pick, fileContent: ReviewContentAvailability | null ): boolean { return ( fileContent?.contentSource === 'disk-current' && fileContent.modifiedFullContent != null && getReviewRejectBlockReason(file, fileContent) === 'baseline-unavailable' ); }