feat: add evidence audit panel shell

This commit is contained in:
732642856
2026-07-21 15:11:21 +08:00
parent 3427b30fd4
commit 19b8491b18
4 changed files with 60 additions and 1 deletions
+6
View File
@@ -149,6 +149,12 @@ button:disabled { cursor: default; opacity: .45; }
.message-markdown > *:first-child { margin-top: 0; }
.message-markdown > *:last-child { margin-bottom: 0; }
.claim-boundary-badge { width: fit-content; margin: 0 0 var(--space-2); padding: 4px 9px; border: 1px solid color-mix(in srgb, var(--color-action) 24%, transparent); border-radius: 999px; background: var(--color-action-soft); color: var(--color-action-hover); font-size: 12px; line-height: 1.45; }
.evidence-audit-panel { margin: 0 0 var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: color-mix(in srgb, var(--color-canvas-soft) 68%, transparent); }
.evidence-audit-panel summary { cursor: pointer; padding: var(--space-2) var(--space-3); color: var(--color-ink-secondary); font-size: 13px; }
.evidence-audit-table { display: grid; gap: 1px; padding: 0 var(--space-3) var(--space-3); }
.evidence-audit-row { display: grid; grid-template-columns: minmax(120px, 1fr) auto minmax(160px, 1.2fr); gap: var(--space-3); align-items: center; padding: var(--space-2) 0; border-top: 1px solid color-mix(in srgb, var(--color-border) 64%, transparent); font-size: 12px; }
.evidence-audit-row b { color: var(--color-action-hover); font-weight: 600; }
.evidence-audit-row small { color: var(--color-ink-tertiary); line-height: 1.45; }
.message-markdown p { margin: 0 0 14px; }
.message-markdown ul, .message-markdown ol { margin: 10px 0 16px; padding-left: 24px; }
.message-markdown li { margin: 6px 0; }
+2 -1
View File
@@ -1,5 +1,6 @@
import { ChatMessageContent } from "@/components/chat-message-content";
import { ClaimBoundaryBadge } from "@/components/claim-boundary-badge";
import { EvidenceAuditPanel } from "@/components/evidence-audit-panel";
import type { ChatMessageView } from "@/lib/chat-message-view";
export function AgentAvatar() {
@@ -24,7 +25,7 @@ export function ChatMessageRow({ message }: { readonly message: ChatMessageView
{message.role === "assistant" ? (
message.state === "thinking"
? <div className="thinking"><i /><i /><i /></div>
: <><ClaimBoundaryBadge status={message.techniqueTruth} /><ChatMessageContent text={message.text} /></>
: <><ClaimBoundaryBadge status={message.techniqueTruth} /><EvidenceAuditPanel claimStatus={message.techniqueTruth} /><ChatMessageContent text={message.text} /></>
) : <p>{message.text}</p>}
</div>
</div>
@@ -0,0 +1,33 @@
type AuditRow = {
label: string;
status: "required" | "partial" | "blocked";
boundary: string;
};
const defaultAuditRows: AuditRow[] = [
{ label: "D1 / Natal", status: "required", boundary: "基础命盘层" },
{ label: "Varga: D9 / D10 / D2 / D11", status: "required", boundary: "按问题域调用,不混用" },
{ label: "Vimshottari Dasha", status: "required", boundary: "阶段证据之一" },
{ label: "Narayana Dasha", status: "required", boundary: "应期/校时必须交叉" },
{ label: "Transit / Gochara", status: "partial", boundary: "精确日/月仍需 holdout" },
{ label: "Shadbala / Ashtakavarga", status: "partial", boundary: "组件 parity 未全闭环" },
{ label: "Functional Benefic/Malefic", status: "required", boundary: "不能只看自然吉凶" },
{ label: "MEVG / Real Case Calibration", status: "blocked", boundary: "外部校准不足时降级" },
];
export function EvidenceAuditPanel({ claimStatus }: { readonly claimStatus?: string }) {
return (
<details className="evidence-audit-panel">
<summary> · {claimStatus || "unknown"}</summary>
<div className="evidence-audit-table" role="table" aria-label="Technique Audit Table">
{defaultAuditRows.map((row) => (
<div className="evidence-audit-row" role="row" key={row.label}>
<span role="cell">{row.label}</span>
<b role="cell" data-status={row.status}>{row.status}</b>
<small role="cell">{row.boundary}</small>
</div>
))}
</div>
</details>
);
}
@@ -0,0 +1,19 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
const rowSource = readFileSync(new URL("../src/components/chat-message-row.tsx", import.meta.url), "utf8");
const panelSource = readFileSync(new URL("../src/components/evidence-audit-panel.tsx", import.meta.url), "utf8");
const globalStyles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
test("assistant messages include a collapsible Technique Audit Table shell", () => {
assert.match(rowSource, /EvidenceAuditPanel/);
assert.match(rowSource, /claimStatus=\{message\.techniqueTruth\}/);
assert.match(panelSource, /Technique Audit Table/);
assert.match(panelSource, /D1 \/ Natal/);
assert.match(panelSource, /Narayana Dasha/);
assert.match(panelSource, /Functional Benefic\/Malefic/);
assert.match(panelSource, /MEVG \/ Real Case Calibration/);
assert.match(panelSource, /组件 parity 未全闭环/);
assert.match(globalStyles, /\.evidence-audit-panel/);
});