fix(rectification): keep one delivery and adopt with the birth date

Accept and GET now decide with the same birth snapshot as the answer, so an under-age probe cannot flip can_adopt. The exit skips a second write when this reply already delivered. The card stays without an adopt button when adoption is not allowed, and a refusal shows the Chinese sentence.
This commit is contained in:
jesse-ux
2026-10-06 16:03:57 +08:00
committed by Jesse_Chen
parent da226db1d4
commit 6e16dc5e16
34 changed files with 1100 additions and 182 deletions
@@ -16,6 +16,7 @@ import {
} from "@/lib/rectification-agentic/birth-time-provenance";
import { RectificationVerificationReport } from "@/components/rectification-verification-report";
import { RectificationSegmentDelivery } from "@/components/rectification-segment-delivery";
import { sessionOutcomeAllowsAdopt } from "@/lib/rectification-agentic/core/rectification-decision";
import { deliveryWindowFromReceipt, protectVargaReport } from "@/lib/rectification-agentic/v9/delivery-window";
@@ -41,6 +42,7 @@ export function RectificationRangeDelivery({
const eventCount = delivery?.event_count ?? 0;
const selected = result.selectedTime;
const busy = Boolean(acceptingCandidateId) || readonly || adoptLocked;
const adoptAllowed = sessionOutcomeAllowsAdopt(result.sessionOutcome) && result.canAdopt === true;
const columns = delivery?.columns ?? [];
const rangeLabel = result.credibleIntervals?.length ? windowIntervalsLabel(result.credibleIntervals, result.declaredBirthDate)
: range ? `${range[0]}–${range[1]}` : null;
@@ -69,7 +71,7 @@ export function RectificationRangeDelivery({
if (delivery && (delivery.product_delivery || delivery.segment_summary)) {
return <RectificationSegmentDelivery summary={delivery.segment_summary ?? null} delivery={delivery} rangeLabel={rangeLabel}
selection={result.segmentSelection} resultId={result.resultId} busy={Boolean(busy)} accepting={Boolean(acceptingCandidateId)} onAccept={onAccept}
adoptLocked={adoptLocked} markdown={markdown}
adoptLocked={adoptLocked} markdown={markdown} canAdopt={adoptAllowed}
candidateDate={result.candidates.find((candidate) => candidate.candidateId === delivery.representative_candidate_id)?.candidate_date} />;
}
@@ -125,7 +127,7 @@ export function RectificationRangeDelivery({
className={`rectification-range-delivery__column${adopted ? " is-adopted" : ""}`}
>
<div className="rectification-range-delivery__time">{candidateDate ? `${candidateDate}${relativeDay} ${column.time}` : column.time}</div>
{candidateDate ? <p className="rectification-range-delivery__muted">采用后,排盘使用这一日期和时间;原填报日期保留。</p> : null}
{adoptAllowed && !adoptLocked && candidateDate ? <p className="rectification-range-delivery__muted">采用后,排盘使用这一日期和时间;原填报日期保留。</p> : null}
{showPercents ? (
<p className="rectification-range-delivery__likelihood">
{RECTIFICATION_USER_COPY.rangeDeliveryRelativeLikelihood}
@@ -152,25 +154,31 @@ export function RectificationRangeDelivery({
<p>{column.window_line}</p>
</>
)}
<button
type="button"
className="rectification-range-delivery__accept"
aria-pressed={adopted}
disabled={busy || adopted}
onClick={() => onAccept(column.candidate_id)}
>
{adopting
? RECTIFICATION_USER_COPY.rangeDeliveryAdopting
: adopted
? RECTIFICATION_USER_COPY.rangeDeliveryAdopted
: recordConflict
? SWITCH_TO_RECTIFIED_LABEL
: RECTIFICATION_USER_COPY.rangeDeliveryMoreLikeThis}
</button>
{recordConflict && !adopted ? (
<p className="rectification-range-delivery__switch-note">
{switchToRectifiedConfirmCopy(recordConflict.reported_time, column.time)}
</p>
{adoptAllowed && !adoptLocked ? (
<>
<button
type="button"
className="rectification-range-delivery__accept"
aria-pressed={adopted}
disabled={busy || adopted}
onClick={() => onAccept(column.candidate_id)}
>
{adopting
? RECTIFICATION_USER_COPY.rangeDeliveryAdopting
: adopted
? RECTIFICATION_USER_COPY.rangeDeliveryAdopted
: recordConflict
? SWITCH_TO_RECTIFIED_LABEL
: RECTIFICATION_USER_COPY.rangeDeliveryMoreLikeThis}
</button>
{recordConflict && !adopted ? (
<p className="rectification-range-delivery__switch-note">
{switchToRectifiedConfirmCopy(recordConflict.reported_time, column.time)}
</p>
) : null}
</>
) : adopted ? (
<p className="rectification-range-delivery__muted">{RECTIFICATION_USER_COPY.rangeDeliveryAdopted}</p>
) : null}
</li>
);
@@ -8,7 +8,7 @@ import { RectificationVerificationReport } from "@/components/rectification-veri
import { protectVargaReport } from "@/lib/rectification-agentic/v9/delivery-window";
import { unknownProductDeliveryCopy } from "@/lib/rectification-agentic/v9/product-delivery";
export function RectificationSegmentDelivery({ summary, delivery, rangeLabel, selection, resultId, busy, accepting, onAccept, adoptLocked, markdown, candidateDate }: Readonly<{
export function RectificationSegmentDelivery({ summary, delivery, rangeLabel, selection, resultId, busy, accepting, onAccept, adoptLocked, markdown, candidateDate, canAdopt = false }: Readonly<{
summary: SegmentSummary | null;
delivery: RangeDeliveryProjection;
rangeLabel: string | null;
@@ -20,6 +20,7 @@ export function RectificationSegmentDelivery({ summary, delivery, rangeLabel, se
adoptLocked?: boolean;
markdown?: string | null;
candidateDate?: string;
canAdopt?: boolean;
}>) {
const adoption = summary?.adoption_minute;
const candidateId = delivery.representative_candidate_id;
@@ -33,6 +34,7 @@ export function RectificationSegmentDelivery({ summary, delivery, rangeLabel, se
const charts = summary?.charts.filter((chart) => chart.tier !== "blocked") ?? [];
const blocked = summary?.charts.some((chart) => chart.tier === "blocked") === true;
const reportBlocked = !summary || blocked;
const showAdopt = Boolean(canAdopt && adoption && candidateId && !adoptLocked);
return (
<section className="rectification-candidates rectification-range-delivery" aria-label="生时校正盘型">
<div className="rectification-candidates-heading"><strong>用哪张盘解读</strong></div>
@@ -52,10 +54,10 @@ export function RectificationSegmentDelivery({ summary, delivery, rangeLabel, se
{summary && !summary.scan_complete ? <p className="rectification-range-delivery__muted">完整窗口扫描暂不可用;候选盘型不能证明全窗一致。{candidateDate ? `候选代表日期 ${candidateDate};原填报日期保留。` : ""}</p> : null}
{adoptLocked ? <p className="rectification-range-delivery__narrow">{RECTIFICATION_USER_COPY.rangeDeliveryAdoptLocked}</p> : null}
{delivery.provenance_line ? <p className="rectification-range-delivery__provenance">{delivery.provenance_line}</p> : null}
{adoption && candidateId ? (
{showAdopt && adoption && candidateId ? (
<>
<p className="rectification-range-delivery__muted">采用后按 {adoption.date ? `${adoption.date} ` : ""}{adoption.time} 排盘;原填报资料保留。</p>
<button type="button" className="rectification-range-delivery__accept" disabled={busy || adoptLocked || adopted}
<button type="button" className="rectification-range-delivery__accept" disabled={busy || adopted}
aria-pressed={adopted} onClick={() => onAccept(candidateId)}>
{accepting ? RECTIFICATION_USER_COPY.rangeDeliveryAdopting : adopted ? RECTIFICATION_USER_COPY.rangeDeliveryAdopted
: readopt && priorAdoption ? `改用 ${readopt}`
@@ -70,6 +72,8 @@ export function RectificationSegmentDelivery({ summary, delivery, rangeLabel, se
</p>
) : null}
</>
) : adopted ? (
<p className="rectification-range-delivery__muted">{RECTIFICATION_USER_COPY.rangeDeliveryAdopted}</p>
) : null}
<p className="rectification-range-delivery__boundary">{delivery.boundary || REPRESENTATIVE_MINUTE_DISCLAIMER}</p>
<RectificationVerificationReport markdown={protectVargaReport(markdown === undefined ? delivery.verification_markdown : markdown, reportBlocked, delivery.product_delivery?.targets ?? summary?.targets,