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>
This commit is contained in:
Jesse_Chen
2026-09-02 02:27:50 +08:00
co-authored by Cursor
parent 8617eb565c
commit ed9497e76a
35 changed files with 652 additions and 118 deletions
@@ -8,6 +8,7 @@ import {
workingRectificationTime,
type RectificationBoardDiff,
} from "@/lib/rectification-board-model";
import { engineMeaningToDisplayCopy } from "@/lib/rectification-agentic/user-copy";
import {
workingRectificationHouseTable,
type RectificationCandidateResult,
@@ -80,7 +81,7 @@ function RectificationBoardBody({
</button>
)}
</div>
{stage ? <p className="rectification-board__stage">{stage}</p> : null}
{stage ? <p className="rectification-board__stage">{engineMeaningToDisplayCopy(stage)}</p> : null}
</header>
<div className="rectification-board__body">
<p className="sr-only" role="status" aria-live="polite" aria-atomic="true">{diff.liveMessage}</p>
@@ -154,7 +155,7 @@ function RectificationBoardBody({
<details className="technique-audit">
<summary></summary>
<div className="rectification-board__disclosure">
<p>{contrast.user_meaning}</p>
<p>{engineMeaningToDisplayCopy(contrast.user_meaning)}</p>
<ul>
{contrast.intervals.map((interval) => (
<li key={`${interval.start}-${interval.end}`}>
@@ -174,16 +175,16 @@ function RectificationBoardBody({
</summary>
<ul className="rectification-board__disclosure">
{ledger.map((item) => (
<li key={item.summary}>{item.user_meaning}</li>
<li key={item.summary}>{engineMeaningToDisplayCopy(item.user_meaning)}</li>
))}
</ul>
{agreement ? <p className="rectification-board__disclosure">{agreement.user_meaning}</p> : null}
{agreement ? <p className="rectification-board__disclosure">{engineMeaningToDisplayCopy(agreement.user_meaning)}</p> : null}
</details>
)}
{nakshatra?.near_boundary && nakshatra.user_meaning && (
<details className="technique-audit">
<summary></summary>
<p className="rectification-board__disclosure">{nakshatra.user_meaning}</p>
<p className="rectification-board__disclosure">{engineMeaningToDisplayCopy(nakshatra.user_meaning)}</p>
</details>
)}
</div>
@@ -7,6 +7,7 @@ import {
} 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,
@@ -95,7 +96,7 @@ export function ConfirmationGateDisclosure({
<tr key={blocker.id}>
<th scope="row">{GATE_LABELS[blocker.id]}</th>
<td data-status={gateStatusToken(blocker.status)}>{gateStatusLabel(blocker.status)}</td>
<td>{blocker.user_meaning}</td>
<td>{engineMeaningToDisplayCopy(blocker.user_meaning)}</td>
</tr>
))}
</tbody>