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:
committed by
Jesse_Chen
co-authored by
Claude Code
parent
75166802d8
commit
66f087b588
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user