fix: prevent repeated birth-time assessment loops

This commit is contained in:
732642856
2026-07-21 22:12:06 +08:00
parent cfa34bac32
commit 7ad0e40675
4 changed files with 28 additions and 4 deletions
+21
View File
@@ -86,6 +86,19 @@ type Profile = BirthTimeDraft & {
districtCode: string;
rectificationCaseId: string;
};
function birthTimeAssessmentSignature(value: BirthTimeDraft) {
return [
value.date,
value.time,
value.reportedTime,
value.birthTimeSource,
value.birthTimePeriod,
value.birthTimeClue,
value.uncertaintyBeforeMinutes,
value.uncertaintyAfterMinutes,
].join("|");
}
type ChartLibraryRecord = {
id: string;
role: "self" | "other";
@@ -741,6 +754,7 @@ export default function Home() {
const uiPreview = useRef(false);
const uiPreviewMode = useRef<string | null>(null);
const birthTimeRevisionPending = useRef(false);
const lastBirthTimeAssessmentSignature = useRef("");
const birthTimeGuided = useBirthTimeGuidedJourney({
journey: birthTimeJourney,
preview: process.env.NODE_ENV === "development" && uiPreview.current,
@@ -1542,6 +1556,7 @@ export default function Home() {
rectificationCaseId: result.caseId,
};
setBirthTimeJourney(result);
lastBirthTimeAssessmentSignature.current = birthTimeAssessmentSignature(nextProfile);
setBirthTimeError("");
setProfile(assessedProfile);
setProfileDraft(assessedProfile);
@@ -1603,6 +1618,12 @@ export default function Home() {
try {
await persistProfile(profileDraft);
if (birthTimeRevisionPending.current) {
if (lastBirthTimeAssessmentSignature.current === birthTimeAssessmentSignature(profileDraft)) {
birthTimeRevisionPending.current = false;
setBirthTimeError("出生资料未变化,重新评估会重复同一结果。请补充不同领域且能注明年月的关键经历,再继续校时。");
setOnboardingStep("rectification");
return;
}
setBirthTimeAssessmentPhase("assessing");
const assessedProfile = await assessSavedBirthTime(profileDraft);
birthTimeRevisionPending.current = false;
@@ -124,8 +124,8 @@ function TerminalAction({ controller, error, path }: {
return (
<div className="birth-time-new-assessment">
<b></b>
<p></p>
<button className="button-secondary birth-time-guided-action" disabled={controller.pending} onClick={controller.editBirthTimeDetails} type="button"></button>
<p></p>
<button className="button-secondary birth-time-guided-action" disabled={controller.pending} onClick={controller.editBirthTimeDetails} type="button"></button>
{error ? <p className="form-error" role="alert">{error}</p> : null}
<small></small>
</div>
@@ -16,6 +16,7 @@ import {
} from "@/lib/birth-time-guided-effect-coordinator";
import type { StableActionIdentityRegistry } from "@/lib/birth-time-guided-effect-coordinator";
import { runBirthTimeScoringPoll, scoringPollDelay } from "@/lib/birth-time-guided-polling";
import { birthTimeUserError } from "@/lib/birth-time-user-error";
type AutomaticEffectsInput = {
readonly journey: JourneyClientResponse | null;
@@ -82,7 +83,7 @@ export function useBirthTimeAutomaticJourneyEffects(input: AutomaticEffectsInput
if (publishCurrentJourney({ expected, current: latest.current, next, publish: onJourney })) latest.current = next;
}).catch((caught: unknown) => {
if (latest.current?.caseId === expected.caseId && latest.current.turnVersion === expected.turnVersion) {
setError(caught instanceof Error ? caught.message : "暂时无法生成下一题,请重试。");
setError(birthTimeUserError(caught));
}
});
}, [actionRegistry, generationIdentity, generationRequests, generationRun, latest, onJourney, preview, setError]);
@@ -114,7 +115,7 @@ export function useBirthTimeAutomaticJourneyEffects(input: AutomaticEffectsInput
latest.current = result.turn;
if (result.kind === "exhausted") setError("评分仍在进行。你可以稍后继续,或重新检查状态。");
}).catch((caught: unknown) => {
if (!controller.signal.aborted) setError(caught instanceof Error ? caught.message : "暂时无法读取评分进度,请稍后重试。");
if (!controller.signal.aborted) setError(birthTimeUserError(caught));
});
});
return () => { cancelStart(); controller.abort(); };
@@ -18,4 +18,6 @@ test("all guided journey mutations normalize implementation errors", () => {
const source = readFileSync(new URL("../src/hooks/use-birth-time-guided-journey.ts", import.meta.url), "utf8");
assert.ok((source.match(/setError\(birthTimeUserError\(caught\)\)/g) ?? []).length >= 1);
assert.equal(source.includes("setError(caught.message"), false);
const automatic = readFileSync(new URL("../src/hooks/use-birth-time-automatic-journey-effects.ts", import.meta.url), "utf8");
assert.equal((automatic.match(/setError\(birthTimeUserError\(caught\)\)/g) ?? []).length, 2);
});