Files
Jyotisha/frontend/src/lib/rectification-agentic/v9/interview-state.ts
T
Jesse_Chen 02d4c91626
Independent Staging Quality Gate / validate (push) Successful in 11m40s
Independent Staging Quality Gate / publish (push) Successful in 9m36s
fix(rectification): stop occupation coverage from locking questions and the range exit
Occupation answers were stored as career, so coverage never closed, yearless
cards never fired, and the decision layer suppressed an engine-allowed range.
Normalize occupation-collect writes, allow yearless cards once training is
open, offer a range when no distinguish card remains, and align the public
house table to the representative minute.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 03:05:28 +08:00

105 lines
3.7 KiB
TypeScript

/**
* Public interview projection for the Case GET payload.
*
* The tap card is shown only when the follow-up is a discriminator
* (candidates already diverge or holdout) and the Agent wrote choice copy.
* Card identity is the persisted focus UUID plus the inference revision.
*/
import { previousInferenceFromReceipt } from "./inference-adapter";
import {
decideFromDossier,
rectificationFollowupCatalog,
} from "./decision-from-dossier";
import { evidenceLedgerFingerprint } from "./tool-service";
import { projectRectificationChoiceCard } from "./method-followup";
import {
internalObservationsFromWindowScan,
windowScanFromDecisionReceipt,
} from "./varga-observations";
import type { RectificationChoiceCard } from "./choice-card";
export function choiceCardFromCaseDossier(dossier: {
evidence: readonly Readonly<{
id?: string;
status: string;
domain: string;
datePrecision: string;
occurredFrom: string | null;
occurredTo: string | null;
eventKind?: string | null;
summary?: string | null;
}>[];
conversationSummary: {
activeFocus: {
id?: string;
questionId?: string;
intent: string;
targetDomain: string | null;
targetKind: string | null;
expectedAnswerSchema?: Readonly<Record<string, unknown>> | null;
} | null;
declinedSkippedTopics: readonly Readonly<Record<string, unknown>>[];
};
latestResult: {
resultId?: string;
decisionReceipt: Readonly<Record<string, unknown>> | null;
selectionAllowed?: boolean;
confirmationAllowed?: boolean;
candidates?: readonly Readonly<{
time: string;
rank?: number;
tiedMinuteCount?: number;
relativeSupport?: number;
}>[];
evidenceLedgerFingerprint?: string | null;
} | null;
case: {
acceptedTime: string | null;
status?: string;
};
turns?: readonly Readonly<{ role: string; text: string | null }>[];
}): RectificationChoiceCard | null {
const catalog = rectificationFollowupCatalog(dossier.latestResult, dossier.evidence);
const observations = internalObservationsFromWindowScan(
windowScanFromDecisionReceipt(dossier.latestResult?.decisionReceipt ?? null),
);
const inference = previousInferenceFromReceipt(dossier.latestResult?.decisionReceipt ?? null);
const decision = decideFromDossier(dossier, {
currentEvidenceFingerprint: evidenceLedgerFingerprint(dossier.evidence as never),
});
const latestAssistantText = [...(dossier.turns ?? [])]
.reverse()
.find((turn) => turn.role === "assistant")
?.text ?? null;
return projectRectificationChoiceCard({
evidence: dossier.evidence,
activeFocus: dossier.conversationSummary.activeFocus,
declinedTopics: dossier.conversationSummary.declinedSkippedTopics,
closedCollectFocuses: dossier.conversationSummary.declinedSkippedTopics,
observations,
sessionOutcome: decision.sessionOutcome,
...catalog,
precisionStage: decision.precisionStage === "collect_events"
? "collect_events"
: decision.precisionStage === "ready_to_adopt"
? "ready_to_adopt"
: catalog.precisionStage,
accepted: Boolean(dossier.case.acceptedTime),
selectionAllowed: decision.selectionAllowed,
proposeAllowed: decision.proposeAllowed,
confirmationAllowed: decision.canConfirmExactMinute,
caseRevision: inference?.revision ?? 0,
candidateScores: decision.separation.ranked.map((item) => ({
time: item.time,
score: item.score,
})),
userStopped: dossier.case.status === "paused",
latestAssistantText,
candidatesSeparated: decision.separation.sufficient,
holdoutValidation: decision.holdoutValidation,
});
}
export { decideFromDossier, overlayPublicDecision } from "./decision-from-dossier";