feat(rectification): submit varga-resolution implementation for review

Review-only snapshot for BUG-1115 through BUG-1117; not merge-ready. New opening and append-turn PostgreSQL permission failures remain blocked. Persisted joint replay has zero completed questions; segment ordering remains off by default. The existing offline replay JSON is retained stale and unchanged after a denied overwrite, including its CRLF line endings. Browser/provider validation and final serial gates remain pending. No deployment, role permission changes, or staging/main push.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
jesse-ux
2026-10-01 08:04:04 +08:00
committed by Jesse_Chen
co-authored by Claude Code
parent 75166802d8
commit 66f087b588
130 changed files with 428156 additions and 231 deletions
@@ -12,6 +12,7 @@ export type PersistedRectificationTurn = Readonly<{
status: string;
question?: unknown;
offer_result_id?: string | null;
segment_consistency?: unknown;
receipt?: Readonly<{
status: string;
phases: readonly string[];
@@ -32,6 +32,7 @@ import {
import { RECTIFICATION_STOPPED_NOTICE } from "@/lib/rectification-surface-state";
import { rectificationTimelineRows } from "@/lib/rectification-timeline-adapter";
import { vargaSentenceFromMethods } from "@/lib/rectification-varga-sentence";
import { SIGN_LABELS, type SegmentSummary } from "@/lib/rectification-agentic/core/segment-summary";
export type RenderMessage = ChatMessageView & {
renderKey: string;
@@ -42,6 +43,7 @@ export type RenderMessage = ChatMessageView & {
activityTrace?: readonly AgentActivityTraceItem[];
question?: TurnQuestion;
candidateOffer?: Readonly<{ resultId: string }>;
segmentConsistency?: SegmentSummary;
};
export type RectificationMessageActions = Readonly<{
@@ -186,6 +188,17 @@ function RectificationMessageEntryView({
stoppedNotice={message.stopped ? RECTIFICATION_STOPPED_NOTICE : undefined}
/>
)}
{message.segmentConsistency?.no_rectification_needed && (
<section className="rectification-candidates rectification-range-delivery" aria-label="盘型一致,不用校正">
<strong>盘型一致,不用校正</strong>
<ul className="rectification-range-delivery__shared">
{message.segmentConsistency.charts.map((chart) => (
<li key={chart.chart}>{chart.chart} 上升:{chart.sign === null ? "未核验" : SIGN_LABELS[chart.sign]}(全窗一致)</li>
))}
</ul>
<p className="rectification-range-delivery__boundary">完整出生窗口内目标盘型一致;不等于确认了某一分钟。</p>
</section>
)}
{/* BUG-1056: copy and feedback only; the rectification reply has no rewrite action. */}
{showActions && (
<ChatMessageActions
@@ -15,9 +15,12 @@ import {
switchToRectifiedConfirmCopy,
} from "@/lib/rectification-agentic/birth-time-provenance";
import { RectificationVerificationReport } from "@/components/rectification-verification-report";
import { RectificationSegmentDelivery } from "@/components/rectification-segment-delivery";
function columnHasTraits(column: RangeDeliveryProjection["columns"][number]): boolean {
return Boolean(column.traits.d9 || column.traits.d10 || column.traits.nakshatra.length > 0);
import { deliveryWindowFromReceipt, protectVargaReport } from "@/lib/rectification-agentic/v9/delivery-window";
function columnHasTraits(column: RangeDeliveryProjection["columns"][number], vargaBlocked: boolean): boolean {
return Boolean((!vargaBlocked && (column.traits.d9 || column.traits.d10)) || column.traits.nakshatra.length > 0);
}
export function RectificationRangeDelivery({
@@ -46,9 +49,11 @@ export function RectificationRangeDelivery({
? `${RECTIFICATION_USER_COPY.rangeDeliveryTitle} ${rangeLabel}(${rangeDeliveryEventCopy(eventCount)})`
: `${RECTIFICATION_USER_COPY.rangeDeliveryTitle} ${rangeLabel}`
: RECTIFICATION_USER_COPY.rangeDeliveryTitle;
const markdown = delivery?.verification_markdown
?? result.verificationReportMarkdown;
const sharedTraits = delivery?.shared_traits ?? [];
const windowMinutes = deliveryWindowFromReceipt(result.decisionReceipt) ?? delivery?.window_minutes ?? null;
const vargaBlocked = windowMinutes === null || windowMinutes > 61;
const markdown = protectVargaReport(delivery?.verification_markdown ?? result.verificationReportMarkdown,
vargaBlocked, delivery?.product_delivery?.targets ?? delivery?.segment_summary?.targets);
const sharedTraits = vargaBlocked ? [] : delivery?.shared_traits ?? [];
// BUG-691: while the birth record disagrees with the range the adopt entry says what
// it actually does — swap the charting clock — instead of the neutral 「更像这个」.
const recordConflict = delivery?.record_conflict ?? null;
@@ -61,6 +66,13 @@ export function RectificationRangeDelivery({
const showPercents = rangeDeliveryShowsPercents(columns);
const indistinct = columns.length >= 2 && !showPercents;
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}
candidateDate={result.candidates.find((candidate) => candidate.candidateId === delivery.representative_candidate_id)?.candidate_date} />;
}
return (
<section className="rectification-candidates rectification-range-delivery" aria-label="生时校正区间">
<div className="rectification-candidates-heading">
@@ -71,7 +83,9 @@ export function RectificationRangeDelivery({
</span>
) : null}
</div>
{caption ? (
{vargaBlocked ? (
<p className="rectification-range-delivery__narrow">{windowMinutes === null ? "完整窗口范围暂不可用;暂不推荐分盘解读。" : "分盘解读需要更准的出生时间"}</p>
) : caption ? (
<p className="rectification-range-delivery__narrow">{caption}</p>
) : null}
{delivery?.provenance_line ? (
@@ -119,10 +133,10 @@ export function RectificationRangeDelivery({
{column.probability_percent}%
</p>
) : null}
{columnHasTraits(column) ? (
{columnHasTraits(column, vargaBlocked) ? (
<div className="rectification-range-delivery__block">
{column.traits.d9 ? <p>{column.traits.d9}</p> : null}
{column.traits.d10 ? <p>{column.traits.d10}</p> : null}
{!vargaBlocked && column.traits.d9 ? <p>{column.traits.d9}</p> : null}
{!vargaBlocked && column.traits.d10 ? <p>{column.traits.d10}</p> : null}
{column.traits.nakshatra.map((line) => (
<p key={line}>{line}</p>
))}
@@ -0,0 +1,70 @@
"use client";
import { SIGN_LABELS, TIER_LABELS, type SegmentSummary } from "@/lib/rectification-agentic/core/segment-summary";
import type { RangeDeliveryProjection } from "@/lib/rectification-agentic/v9/divergence-panel";
import { RECTIFICATION_USER_COPY, REPRESENTATIVE_MINUTE_DISCLAIMER } from "@/lib/rectification-agentic/user-copy";
import { SWITCH_TO_RECTIFIED_LABEL } from "@/lib/rectification-agentic/birth-time-provenance";
import { RectificationVerificationReport } from "@/components/rectification-verification-report";
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<{
summary: SegmentSummary | null;
delivery: RangeDeliveryProjection;
rangeLabel: string | null;
selection?: Readonly<{ resultId: string; candidateId: string; date: string; time: string; contract: "segment-v1" }> | null;
resultId: string;
busy: boolean;
accepting: boolean;
onAccept: (candidateId: string) => void;
adoptLocked?: boolean;
markdown?: string | null;
candidateDate?: string;
}>) {
const adoption = summary?.adoption_minute;
const candidateId = delivery.representative_candidate_id;
const adopted = Boolean(adoption?.date && selection?.contract === "segment-v1" && selection.resultId === resultId
&& selection.candidateId === candidateId && selection.date === adoption.date && selection.time === adoption.time);
const charts = summary?.charts.filter((chart) => chart.tier !== "blocked") ?? [];
const blocked = summary?.charts.some((chart) => chart.tier === "blocked") === true;
const reportBlocked = !summary || blocked;
return (
<section className="rectification-candidates rectification-range-delivery" aria-label="生时校正盘型">
<div className="rectification-candidates-heading"><strong>用哪张盘解读</strong></div>
<ul className="rectification-range-delivery__shared">
{charts.map((chart) => (
<li key={chart.chart}>
{chart.chart} 上升:{chart.sign === null ? "分不开" : SIGN_LABELS[chart.sign]}
({TIER_LABELS[chart.tier]}{chart.tied ? ",头段并列" : ""})
{chart.tier !== "certain" && chart.alternatives.length > 0
? `,备选:${chart.alternatives.map((sign) => SIGN_LABELS[sign]).join("、")}` : ""}
</li>
))}
</ul>
{blocked ? <p className="rectification-range-delivery__narrow">分盘解读需要更准的出生时间</p> : null}
{rangeLabel ? <p className="rectification-range-delivery__muted">分钟范围 {rangeLabel}</p> : null}
{!summary && delivery.product_delivery ? <p className="rectification-range-delivery__indistinct">{unknownProductDeliveryCopy(delivery.product_delivery)}</p> : null}
{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 ? (
<>
<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}
aria-pressed={adopted} onClick={() => onAccept(candidateId)}>
{accepting ? RECTIFICATION_USER_COPY.rangeDeliveryAdopting : adopted ? RECTIFICATION_USER_COPY.rangeDeliveryAdopted
: delivery.record_conflict ? SWITCH_TO_RECTIFIED_LABEL : `改用 ${charts.map((chart) => chart.chart).join(" + ")} 盘解读`}
</button>
{delivery.record_conflict && !adopted ? (
<p className="rectification-range-delivery__switch-note">
选它之后,排盘会从出生记录时间 {delivery.record_conflict.reported_time} 换成 {adoption.date ? `${adoption.date} ` : ""}{adoption.time}。
</p>
) : null}
</>
) : 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,
!summary ? "完整窗口扫描暂不可用;不能提供分盘解读推荐。" : undefined)} />
</section>
);
}