fix(consult): allow reported-time precision and persist receipts
Independent Staging Quality Gate / validate (push) Successful in 12m1s
Independent Staging Quality Gate / publish (push) Successful in 9m50s

This commit is contained in:
Jesse_Chen
2026-08-16 16:57:12 +08:00
parent a8279623aa
commit f12a43ad1d
12 changed files with 87 additions and 45 deletions
+3 -2
View File
@@ -825,8 +825,9 @@ export async function POST(request: Request) {
if (!prepared.serverChart) throw new Error("server_chart_truth_missing");
const toolInput = consultationInputSchema.parse({
...prepared.serverChart.toolInput,
// Unverified use is still a normal chart calculation with a hard answer
// boundary. It must never reactivate the retired rectification questionnaire.
// A user-reported concrete minute is a normal chart calculation. Keep its
// provenance, but let server evidence—not rectification purchase state—own
// precise-timing permission. Never reactivate the retired questionnaire.
entryMode: "direct_chart",
question: resolvedQuestion.modelQuestion,
theme: parsed.data.theme,
+2 -7
View File
@@ -1,4 +1,4 @@
import type { AgentExecutionReceipt, PublicActivityPhase } from "./consultation-agent-events.ts";
import type { AgentExecutionReceipt, PublicActivityPhase, WorkflowReceipt } from "./consultation-agent-events.ts";
export type AgentActivityView = Readonly<{
phase: PublicActivityPhase;
@@ -11,12 +11,7 @@ export type ChatMessage = {
readonly suggestions?: readonly string[];
readonly techniqueTruth?: string;
readonly agentExecutionReceipt?: AgentExecutionReceipt;
readonly workflowReceipt?: {
readonly route: string;
readonly status: string;
readonly preciseTiming: string;
readonly missingLayers: readonly string[];
};
readonly workflowReceipt?: WorkflowReceipt;
};
export type ChatMessageView = ChatMessage & {
@@ -1,5 +1,10 @@
import { z } from "zod";
import { agentExecutionReceiptSchema, type AgentExecutionReceipt } from "./consultation-agent-events.ts";
import {
agentExecutionReceiptSchema,
workflowReceiptSchema,
type AgentExecutionReceipt,
type WorkflowReceipt,
} from "./consultation-agent-events.ts";
import { consultationDomainSchema, type ConsultationDomain } from "./consultation-domain-registry.ts";
const chatMessageSchema = z.object({
@@ -8,12 +13,7 @@ const chatMessageSchema = z.object({
suggestions: z.array(z.string().max(200)).max(3).optional(),
techniqueTruth: z.string().max(120).optional(),
agentExecutionReceipt: agentExecutionReceiptSchema.optional(),
workflowReceipt: z.object({
route: z.string().max(120),
status: z.string().max(120),
preciseTiming: z.string().max(120),
missingLayers: z.array(z.string().max(120)).max(30),
}).strict().optional(),
workflowReceipt: workflowReceiptSchema.optional(),
}).strict();
export const chatSessionWriteSchema = z.object({
@@ -44,12 +44,7 @@ export type ChatSessionWrite = Readonly<{
suggestions?: readonly string[];
techniqueTruth?: string;
agentExecutionReceipt?: AgentExecutionReceipt;
workflowReceipt?: Readonly<{
route: string;
status: string;
preciseTiming: string;
missingLayers: readonly string[];
}>;
workflowReceipt?: WorkflowReceipt;
}>[];
session_type: "consultation" | "birth_time_rectification";
rectification_case_id: string | null;
+10 -3
View File
@@ -1,5 +1,5 @@
import { z } from "zod";
import { consultationDomainSchema } from "./consultation-domain-registry.ts";
import { consultationDomainSchema, type ConsultationDomain } from "./consultation-domain-registry.ts";
export const publicActivityPhaseSchema = z.enum([
"loading-method",
@@ -9,14 +9,21 @@ export const publicActivityPhaseSchema = z.enum([
]);
export type PublicActivityPhase = z.infer<typeof publicActivityPhaseSchema>;
export const workflowReceiptSchema = z.object({
export type WorkflowReceipt = Readonly<{
route: string;
status: string;
preciseTiming: string;
missingLayers: readonly string[];
domains?: readonly ConsultationDomain[];
}>;
export const workflowReceiptSchema: z.ZodType<WorkflowReceipt> = z.object({
route: z.string().max(120),
status: z.string().max(120),
preciseTiming: z.string().max(120),
missingLayers: z.array(z.string().max(120)).max(30),
domains: z.array(consultationDomainSchema).min(1).max(6).optional(),
}).strict();
export type WorkflowReceipt = z.infer<typeof workflowReceiptSchema>;
const executionStepSchema = z.object({
sequence: z.number().int().min(1).max(32),
@@ -34,7 +34,6 @@ export function applyBirthTimeModeToWorkflowContext<
...context.consumer_context,
answer_policy: {
...context.consumer_context.answer_policy,
can_answer_precise_timing: false,
birth_time_confidence: "unverified_reported_time",
candidate_is_confirmed: false,
},
+1 -1
View File
@@ -73,7 +73,7 @@ function deepFreeze<T>(value: T): DeepReadonly<T> {
}
function precisionBoundaryFor(mode: ConsultationBirthTimeMode) {
return mode === "verified_chart" ? "server_evidence_required" as const : "precise_timing_blocked" as const;
return mode === "general_no_birth_time" ? "precise_timing_blocked" as const : "server_evidence_required" as const;
}
export function validateConsultationPlan(