Files
Jyotisha/frontend/src/components/technique-audit-disclosure.tsx
T
Jesse_ChenandCursor ed9497e76a fix(voice): speak first, then keep Level 2 skeleton and boundary semantics
Natal answers were opening on parameter tables, and rectification turns were one-sentence legal copy. Centralize user-facing strings, keep representative-minute and question-slot red lines, and stop duplicating the opening collect prompt as a second assistant message.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 02:27:50 +08:00

108 lines
3.3 KiB
TypeScript

"use client";
import {
displayTechniqueAuditLabel,
displayTechniqueAuditNote,
techniqueAuditStatusLabel,
} from "@/lib/consultation-technique-audit";
import type { TechniqueAuditRow } from "@/lib/consultation-agent-events";
import type { ConfirmationGate } from "@/lib/rectification-agentic/v9/confirmation-gate";
import { engineMeaningToDisplayCopy } from "@/lib/rectification-agentic/user-copy";
export function TechniqueAuditDisclosure({
rows,
}: {
readonly rows: readonly TechniqueAuditRow[];
}) {
if (rows.length === 0) return null;
return (
<details className="technique-audit">
<summary>
本轮技法
<small>{rows.length} 项</small>
</summary>
<div className="technique-audit-panel">
<table>
<caption className="sr-only">本轮技法审计</caption>
<thead>
<tr>
<th scope="col">技法</th>
<th scope="col">状态</th>
<th scope="col">说明</th>
</tr>
</thead>
<tbody>
{rows.map((row, index) => (
<tr key={`${row.technique}-${index}`}>
<th scope="row">{displayTechniqueAuditLabel(row.technique)}</th>
<td data-status={row.status}>{techniqueAuditStatusLabel(row.status)}</td>
<td>{displayTechniqueAuditNote(row.note)}</td>
</tr>
))}
</tbody>
</table>
</div>
</details>
);
}
const GATE_LABELS: Readonly<Record<ConfirmationGate["blockers"][number]["id"], string>> = {
vedastro_minute_sensitive: "官方分钟层",
adjacent_minutes_indistinguishable: "相邻分钟",
public_aa_holdout: "公开密封集",
};
function gateStatusLabel(status: string): string {
if (status === "passed") return "已通过";
if (status === "not_evaluated") return "尚未跑通";
if (status === "not_ready") return "未达标";
if (status === "failed") return "未通过";
return "未通过";
}
function gateStatusToken(status: string): "executed" | "blocked" {
return status === "passed" ? "executed" : "blocked";
}
export function ConfirmationGateDisclosure({
gate,
}: {
readonly gate: ConfirmationGate;
}) {
return (
<details className="technique-audit">
<summary>
唯一分钟确认门
<small>
{gate.confirmation_allowed
? "已允许"
: gate.unique_minute_path === "closed_at_representative"
? "以代表性时间收口"
: "未允许"}
</small>
</summary>
<div className="technique-audit-panel">
<table>
<caption className="sr-only">唯一分钟确认门</caption>
<thead>
<tr>
<th scope="col">条件</th>
<th scope="col">状态</th>
<th scope="col">说明</th>
</tr>
</thead>
<tbody>
{gate.blockers.map((blocker) => (
<tr key={blocker.id}>
<th scope="row">{GATE_LABELS[blocker.id]}</th>
<td data-status={gateStatusToken(blocker.status)}>{gateStatusLabel(blocker.status)}</td>
<td>{engineMeaningToDisplayCopy(blocker.user_meaning)}</td>
</tr>
))}
</tbody>
</table>
</div>
</details>
);
}