fix(rectification): simplify delivery card and guard double turns (BUG-595, BUG-596)
Independent Staging Quality Gate / validate (push) Successful in 25m9s
Independent Staging Quality Gate / publish (push) Successful in 37m19s

Make the range card row-select, drop the composer 先这样 control and adopt status bar, keep delivery copy to three sentences with a folded verification report, and skip a second no-message agent run after a terminal delivery turn.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-08 19:56:48 +08:00
co-authored by Cursor
parent 6368ebba0f
commit 7e3b6cdc7a
60 changed files with 1561 additions and 1105 deletions
@@ -50,6 +50,7 @@ import {
RECTIFICATION_ACTION_EXECUTION,
type RectificationRouteAction,
} from "@/lib/rectification-agentic/v9/turn-exit";
import { logRectificationDeliveryTurn } from "@/lib/rectification-agentic/v9/delivery-turn-guard";
export const runtime = "nodejs";
export const maxDuration = 240;
@@ -779,6 +780,11 @@ export async function POST(request: Request) {
try {
if (action === "opening") {
logRectificationDeliveryTurn({
trigger: "opening",
caseId,
terminalNote: false,
});
try {
await persistNextInterviewIfIdle({
accounting: accounting as never,
@@ -842,7 +848,9 @@ export async function POST(request: Request) {
},
});
if (!result.ok) {
if (result.errorCode === "already_delivered") {
send({ type: "done", emitted: true });
} else if (!result.ok) {
send({ type: "error", message: "生时校正暂时不可用,请稍后重试。" });
} else {
// Shared gate owns persistNextInterviewIfIdle then ensureNonTerminalTurnExit;
@@ -23,6 +23,7 @@ import { publicDecisionFields } from "@/lib/rectification-agentic/core/rectifica
import { collectionProgressFromReceipt } from "@/lib/rectification-agentic/v9/evidence-model";
import { slimDecisionReceipt } from "@/lib/rectification-agentic/v9/case-receipt-projection";
import { rangeDeliveryForSnapshot } from "@/lib/rectification-agentic/v9/divergence-panel";
import { latestResultToolProjection } from "@/mastra/rectification-v9-tools";
export const runtime = "nodejs";
@@ -166,6 +167,7 @@ function publicLatestResult(
) {
if (!latest) return null;
const projected = overlayPublicDecision(latest, decision);
const toolProjection = latestResultToolProjection(latest, decision);
const rangeDelivery = rangeDeliveryForSnapshot({
decisionReceipt: projected.decisionReceipt,
candidates: projected.candidates,
@@ -173,6 +175,7 @@ function publicLatestResult(
representative_time: projected.representativeTime ?? decision.representativeTime,
credibleRange: projected.credible_range ?? decision.credibleRange,
credible_range: projected.credible_range ?? decision.credibleRange,
skill_verification_report: toolProjection.skill_verification_report,
});
const withDelivery = {
...projected,
@@ -11,6 +11,7 @@ import { jsonForSupabaseSetupFailure } from "@/lib/api/service-unavailable";
import { createAdminSupabaseClient } from "@/lib/supabase/admin";
import { createServerSupabaseClient } from "@/lib/supabase/server";
import { getRectificationV9RegenerationAgent } from "@/mastra/agentic-rectification";
import { logRectificationDeliveryTurn } from "@/lib/rectification-agentic/v9/delivery-turn-guard";
export const runtime = "nodejs";
export const maxDuration = 240;
@@ -128,6 +129,12 @@ export async function POST(request: Request, context: RouteContext) {
turnId,
accounting,
}, skillPackage);
logRectificationDeliveryTurn({
trigger: "regenerate",
caseId,
resultId: turnId,
terminalNote: false,
});
const result = await regenerateV9AssistantTurn({
userId: user.id,
caseId,