fix: harden dynamic rectification protocol

This commit is contained in:
Jesse_Chen
2026-07-19 00:16:14 +08:00
parent ab0351ad0f
commit 883d8dcba2
6 changed files with 147 additions and 4 deletions
@@ -1,19 +1,20 @@
import type { CandidateResult } from "./birth-time-evidence.ts";
export type DynamicStopInput = {
readonly result: CandidateResult;
readonly result: CandidateResult | null;
readonly effectiveAnswer: boolean;
readonly previousResult: CandidateResult | null;
readonly priorPlateauCount: number;
readonly usefulOpportunityCount: number;
readonly repeatedOnly: boolean;
readonly effectiveAnswerCount: number;
readonly forcedReason: "user_finished" | "generation_unavailable" | null;
};
export type DynamicStopDecision =
| {
readonly kind: "finish";
readonly reason: "high_confidence" | "safety_cap" | "plateau" | "no_information_gain" | "repeated_partition";
readonly reason: "user_finished" | "generation_unavailable" | "high_confidence" | "safety_cap" | "plateau" | "no_information_gain" | "repeated_partition";
readonly plateauCount: number;
}
| { readonly kind: "continue"; readonly plateauCount: number };
@@ -34,10 +35,11 @@ export function materiallyChanged(
}
export function decideDynamicStop(input: DynamicStopInput): DynamicStopDecision {
const plateauCount = input.effectiveAnswer
const plateauCount = input.effectiveAnswer && input.result !== null
? materiallyChanged(input.previousResult, input.result) ? 0 : input.priorPlateauCount + 1
: input.priorPlateauCount;
if (input.result.confidence === "high") return { kind: "finish", reason: "high_confidence", plateauCount };
if (input.forcedReason !== null) return { kind: "finish", reason: input.forcedReason, plateauCount };
if (input.result?.confidence === "high") return { kind: "finish", reason: "high_confidence", plateauCount };
if (input.effectiveAnswerCount >= 10) return { kind: "finish", reason: "safety_cap", plateauCount };
if (plateauCount >= 2) return { kind: "finish", reason: "plateau", plateauCount };
if (input.usefulOpportunityCount === 0) return { kind: "finish", reason: "no_information_gain", plateauCount };
@@ -81,6 +81,16 @@ export type DynamicJourneyProgress = z.infer<typeof dynamicJourneyProgressSchema
export type { PublicDynamicChoiceQuestion, TimeRange };
export const dynamicJourneyTurnStateSchema = z.object({
journeyProtocol: z.literal("dynamic-choice-v2"),
turnVersion: z.number().int().nonnegative(),
nextAction: dynamicNextActionSchema,
progress: dynamicJourneyProgressSchema,
permissions: journeyPermissionsSchema,
}).strict().readonly();
export type DynamicJourneyTurnState = z.infer<typeof dynamicJourneyTurnStateSchema>;
export const journeyTurnStateSchema = z.object({
turnVersion: z.number().int().nonnegative(),
nextAction: nextActionSchema,
@@ -18,10 +18,12 @@ export {
questionSpecSchema,
dynamicJourneyProgressSchema,
dynamicNextActionSchema,
dynamicJourneyTurnStateSchema,
} from "./birth-time-journey-turn-protocol.ts";
export type {
DynamicJourneyProgress,
DynamicNextAction,
DynamicJourneyTurnState,
EvidenceDraft,
JourneyPermissions,
JourneyProgress,