/* * 원본 번호 범위와 Twin 커서를 맞춘 **현재 판정**이다. * * 이 계산은 봉투 수와 저널 행 수를 비교하지 않는다. 재전송과 backfill 때문에 그 둘은 같은 * 사실을 여러 번 셀 수 있다. 여기서 답하는 것은 번호를 매기는 원본에서만 가능한, 더 좁고 * 검증 가능한 질문이다: "원본의 양 끝과 우리가 처음/마지막으로 받은 번호가 이어지는가?" */ export type ReferenceRange = { first?: number; last?: number } export type ReferenceReconciliation = { sourceFirst?: number sourceLast?: number cursorSeq?: number firstSeqSeen?: number headMissing?: number behind?: number status: 'aligned' | 'gap' | 'unknown' reason?: string } export function reconcileReferenceSequence(args: { source: ReferenceRange | undefined cursorSeq?: number firstSeqSeen?: number }): ReferenceReconciliation { const { source, cursorSeq, firstSeqSeen } = args const sourceFirst = source?.first const sourceLast = source?.last const base = { sourceFirst, sourceLast, cursorSeq, firstSeqSeen } if (sourceFirst === undefined || sourceLast === undefined) { return { ...base, status: 'unknown', reason: '원본이 번호 범위의 양 끝을 주지 않아 대사할 수 없습니다' } } if (cursorSeq === undefined) { return { ...base, status: 'unknown', reason: 'Twin이 아직 이 단위의 번호를 받은 적이 없습니다' } } if (cursorSeq < sourceFirst) { return { ...base, status: 'unknown', reason: 'Twin 커서가 원본의 첫 번호보다 앞입니다. 원본 번호가 다시 매겨졌거나 다른 단위의 커서입니다' } } const behind = Math.max(0, sourceLast - cursorSeq) /* 처음 받은 번호를 적지 않았으면 앞머리는 "없음"이 아니라 "모름"이다. */ const headMissing = firstSeqSeen === undefined ? undefined : Math.max(0, firstSeqSeen - sourceFirst) if (behind || headMissing) { return { ...base, ...(behind ? { behind } : {}), ...(headMissing ? { headMissing } : {}), status: 'gap' } } if (firstSeqSeen === undefined) { return { ...base, status: 'unknown', reason: '처음 받은 번호가 기록되지 않아 앞머리 연속성은 확인할 수 없습니다' } } return { ...base, status: 'aligned' } }