600 lines
23 KiB
TypeScript
600 lines
23 KiB
TypeScript
/**
|
|
* Shared Case → decideRectification adapter.
|
|
*
|
|
* GET refresh, turn_decision, choice/stop, and interview cards must feed
|
|
* the same inputs. Snapshot selection_allowed is storage, not policy.
|
|
*/
|
|
|
|
import {
|
|
buildCandidateContrastPacket,
|
|
datedDomainsFromEvidence,
|
|
inspectDiscriminatorProbes,
|
|
mentionedVargaKeysFromLedgerEvidence,
|
|
volunteeredDomainsFromEvidence,
|
|
withCompletedContrastOptions,
|
|
type CandidateContrastPacket,
|
|
type CandidateDiscriminatorProbe,
|
|
type EngineContrastProbe,
|
|
} from "../core/candidate-contrast-packet.ts";
|
|
import {
|
|
decideRectification,
|
|
publicDecisionFields,
|
|
type HoldoutValidationStatus,
|
|
type RectificationDecision,
|
|
} from "../core/rectification-decision.ts";
|
|
import type { InferenceState } from "../core/types.ts";
|
|
import {
|
|
askedDiscriminatorKeys,
|
|
authoritativeCandidateProjection,
|
|
previousInferenceFromReceipt,
|
|
} from "./inference-adapter";
|
|
import {
|
|
blockingMethodsCovered,
|
|
buildMethodFollowupPlan,
|
|
datedMethodCollectOpen,
|
|
} from "./method-followup";
|
|
import { buildConfirmationGate } from "./confirmation-gate";
|
|
import {
|
|
MIN_ACCEPTANCE_DOMAINS,
|
|
MIN_ACCEPTANCE_EVENTS,
|
|
trainingScoreableGate,
|
|
} from "./evidence-model";
|
|
import { refinementFromDecisionReceipt, type DiscriminatingEventProbe } from "./refinement-packet";
|
|
import { windowScanFromDecisionReceipt } from "./varga-observations";
|
|
import type { DroppedProbe } from "./probe-question-contract.ts";
|
|
import { evidenceLedgerFingerprint } from "./tool-service";
|
|
import {
|
|
candidateSnapshotSource,
|
|
storedSnapshotIsCurrent,
|
|
} from "../core/snapshot-source.ts";
|
|
|
|
export type DecisionDossier = Readonly<{
|
|
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;
|
|
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<{
|
|
candidateId?: string;
|
|
time: string;
|
|
rank?: number;
|
|
relativeSupport?: number;
|
|
posterior_score?: number;
|
|
tiedMinuteCount?: number;
|
|
}>[];
|
|
representativeTime?: string | null;
|
|
evidenceLedgerFingerprint?: string | null;
|
|
candidateRangeFingerprint?: string | null;
|
|
policyVersion?: string | null;
|
|
algorithmVersion?: string | null;
|
|
} | null;
|
|
case: {
|
|
acceptedTime: string | null;
|
|
status?: string;
|
|
};
|
|
turns?: readonly Readonly<{ role: string; text: string | null }>[];
|
|
}>;
|
|
|
|
export function candidateScoresFromDossier(latest: DecisionDossier["latestResult"]) {
|
|
const projection = authoritativeCandidateProjection(latest);
|
|
if (projection.scores.length > 0) return projection.scores;
|
|
if (previousInferenceFromReceipt(latest?.decisionReceipt ?? null)) return [];
|
|
return (latest?.candidates ?? []).flatMap((item) => (
|
|
typeof item.time === "string" && item.time.trim()
|
|
? [{
|
|
time: item.time,
|
|
score: item.posterior_score ?? item.relativeSupport ?? 0,
|
|
}]
|
|
: []
|
|
));
|
|
}
|
|
|
|
const CLOCK_TIME = /^(?:[01]\d|2[0-3]):[0-5]\d$/;
|
|
|
|
export function discriminatorCandidateTimes(
|
|
latest: DecisionDossier["latestResult"],
|
|
): string[] {
|
|
const inference = previousInferenceFromReceipt(latest?.decisionReceipt ?? null);
|
|
const fromInference = [...new Set(
|
|
(inference?.candidates ?? [])
|
|
.filter((item) => item.status !== "eliminated")
|
|
.map((item) => item.time)
|
|
.filter((time) => CLOCK_TIME.test(time)),
|
|
)];
|
|
if (fromInference.length >= 2) return fromInference;
|
|
return candidateScoresFromDossier(latest).map((item) => item.time);
|
|
}
|
|
|
|
function canAskHoldout(input: {
|
|
events?: readonly Readonly<{ usage: string; year: number | null }>[];
|
|
oosBlindPrompts?: readonly unknown[] | null;
|
|
}): boolean {
|
|
if ((input.oosBlindPrompts?.length ?? 0) > 0) return true;
|
|
return Boolean(input.events?.some((item) => item.usage === "holdout" && item.year !== null));
|
|
}
|
|
|
|
function holdoutValidationStatus(input: {
|
|
events?: readonly Readonly<{ usage: string; year: number | null }>[];
|
|
holdoutPassed?: boolean | null;
|
|
resultStatus?: string | null;
|
|
oosBlindPrompts?: readonly unknown[] | null;
|
|
}): HoldoutValidationStatus {
|
|
if (input.holdoutPassed === true) return "passed";
|
|
if (input.holdoutPassed === false || input.resultStatus === "validation_failed") {
|
|
return "failed";
|
|
}
|
|
if (canAskHoldout(input)) return "not_started";
|
|
return "unavailable";
|
|
}
|
|
|
|
function holdoutStatusFromInference(
|
|
inference: ReturnType<typeof previousInferenceFromReceipt>,
|
|
oosBlindPrompts?: readonly unknown[] | null,
|
|
) {
|
|
if (!inference) return "unavailable" as const;
|
|
return holdoutValidationStatus({
|
|
events: inference.events,
|
|
holdoutPassed: inference.holdout_passed,
|
|
resultStatus: inference.result_status,
|
|
oosBlindPrompts,
|
|
});
|
|
}
|
|
|
|
function holdoutStatusFromState(
|
|
state: InferenceState,
|
|
oosBlindPrompts?: readonly unknown[] | null,
|
|
) {
|
|
return holdoutValidationStatus({
|
|
events: state.events,
|
|
holdoutPassed: state.holdout_passed,
|
|
resultStatus: state.result_status,
|
|
oosBlindPrompts,
|
|
});
|
|
}
|
|
|
|
export function contrastPacketFromLatestResult(
|
|
latest: DecisionDossier["latestResult"] | undefined,
|
|
evidence: DecisionDossier["evidence"] = [],
|
|
): CandidateContrastPacket {
|
|
const windowScan = windowScanFromDecisionReceipt(latest?.decisionReceipt ?? null);
|
|
const refinement = refinementFromDecisionReceipt(latest?.decisionReceipt ?? null);
|
|
const inference = previousInferenceFromReceipt(latest?.decisionReceipt ?? null);
|
|
const answered = new Set((inference?.answered_probes ?? []).map((item) => item.probe_id));
|
|
const fromInference: EngineContrastProbe[] = (inference?.probes ?? []).flatMap((probe) => {
|
|
if (answered.has(probe.id) || probe.information_gain <= 0) return [];
|
|
if (probe.source === "known_event_quality") return [];
|
|
return [{
|
|
semantic_key: probe.semantic_key,
|
|
candidate_split_hash: probe.candidate_split_hash,
|
|
domain: probe.domain,
|
|
year: probe.year > 0 ? probe.year : undefined,
|
|
user_meaning: probe.question,
|
|
information_gain: probe.information_gain,
|
|
expected_outcomes: probe.expected_outcomes,
|
|
candidate_ids: probe.candidate_ids,
|
|
...(probe.choice_kind === "varga_style"
|
|
|| probe.choice_kind === "event_quality"
|
|
|| probe.choice_kind === "existence"
|
|
? { choice_kind: probe.choice_kind }
|
|
: {}),
|
|
...(probe.style_options?.length ? { style_options: probe.style_options } : {}),
|
|
}];
|
|
});
|
|
const merged = mergeEngineProbes(
|
|
fromInference,
|
|
refinement.discriminating_event_probes.map((probe) => ({
|
|
semantic_key: probe.semantic_key,
|
|
candidate_split_hash: probe.candidate_split_hash,
|
|
domain: probe.domain,
|
|
year: probe.year,
|
|
user_meaning: probe.user_meaning,
|
|
information_gain: probe.information_gain,
|
|
expected_outcomes: probe.expected_outcomes,
|
|
candidate_ids: probe.candidate_ids,
|
|
choice_kind: probe.choice_kind,
|
|
style_options: probe.style_options,
|
|
})),
|
|
);
|
|
return buildCandidateContrastPacket({
|
|
candidateSetVersion: inference?.candidate_set_id ?? latest?.resultId ?? "none",
|
|
calculationResultId: latest?.resultId ?? null,
|
|
engineProbes: merged,
|
|
vargaDifferences: [
|
|
...(windowScan?.d9_candidates_differ && windowScan.d9_sign_names.length >= 2
|
|
? [{ layer: "d9", signs: windowScan.d9_sign_names }]
|
|
: []),
|
|
...(windowScan?.d10_candidates_differ && windowScan.d10_sign_names.length >= 2
|
|
? [{ layer: "d10", signs: windowScan.d10_sign_names }]
|
|
: []),
|
|
],
|
|
candidateTimes: discriminatorCandidateTimes(latest ?? null),
|
|
transitions: windowScan?.transitions ?? [],
|
|
askedKeys: askedDiscriminatorKeys(latest?.decisionReceipt, evidence),
|
|
volunteeredDomains: volunteeredDomainsFromEvidence(evidence),
|
|
providedDomains: datedDomainsFromEvidence(evidence),
|
|
});
|
|
}
|
|
|
|
function mergeEngineProbes(
|
|
...groups: ReadonlyArray<readonly EngineContrastProbe[] | undefined>
|
|
): EngineContrastProbe[] {
|
|
const byKey = new Map<string, EngineContrastProbe>();
|
|
for (const group of groups) {
|
|
for (const probe of group ?? []) {
|
|
const key = probe.semantic_key?.trim() ?? "";
|
|
if (!key) continue;
|
|
const current = byKey.get(key);
|
|
if (!current || (probe.information_gain ?? 0) > (current.information_gain ?? 0)) {
|
|
byKey.set(key, probe);
|
|
}
|
|
}
|
|
}
|
|
return [...byKey.values()];
|
|
}
|
|
|
|
export function contrastPacketFromDossier(dossier: DecisionDossier): CandidateContrastPacket {
|
|
return contrastPacketFromLatestResult(dossier.latestResult, dossier.evidence);
|
|
}
|
|
|
|
export function rectificationFollowupCatalog(
|
|
latest: DecisionDossier["latestResult"] | undefined,
|
|
evidence: DecisionDossier["evidence"] = [],
|
|
) {
|
|
const receipt = latest?.decisionReceipt ?? null;
|
|
const refinement = refinementFromDecisionReceipt(receipt);
|
|
const inference = previousInferenceFromReceipt(receipt);
|
|
return {
|
|
contrastPacket: contrastPacketFromLatestResult(latest ?? null, evidence),
|
|
topCandidateTimes: discriminatorCandidateTimes(latest ?? null),
|
|
askedProbeKeys: askedDiscriminatorKeys(receipt, evidence),
|
|
eventProbes: refinement.discriminating_event_probes,
|
|
eventClarificationProbes: refinement.event_clarification_probes,
|
|
evidenceCollectionProbes: refinement.evidence_collection_probes,
|
|
precisionStage: refinement.precision_stage?.current ?? null,
|
|
nakshatraBoundary: refinement.nakshatra_boundary,
|
|
oosBlindPrompts: refinement.oos_blind_prompts,
|
|
holdoutEvents: (inference?.events ?? [])
|
|
.filter((item) => item.usage === "holdout")
|
|
.map((item) => ({ domain: item.domain, year: item.year })),
|
|
};
|
|
}
|
|
|
|
function contrastPacketFromState(state: InferenceState): CandidateContrastPacket {
|
|
const answered = new Set(state.answered_probes.map((item) => item.probe_id));
|
|
return buildCandidateContrastPacket({
|
|
candidateSetVersion: state.candidate_set_id,
|
|
calculationResultId: null,
|
|
engineProbes: state.probes
|
|
.filter((item) => !answered.has(item.id))
|
|
.map((item) => ({
|
|
semantic_key: item.semantic_key,
|
|
candidate_split_hash: item.candidate_split_hash,
|
|
domain: item.domain,
|
|
year: item.year,
|
|
user_meaning: item.question,
|
|
information_gain: item.information_gain,
|
|
expected_outcomes: item.expected_outcomes,
|
|
candidate_ids: item.candidate_ids,
|
|
...(item.choice_kind === "varga_style"
|
|
|| item.choice_kind === "event_quality"
|
|
|| item.choice_kind === "existence"
|
|
? { choice_kind: item.choice_kind }
|
|
: {}),
|
|
...(item.style_options?.length ? { style_options: item.style_options } : {}),
|
|
})),
|
|
candidateTimes: state.candidates
|
|
.filter((item) => item.status !== "eliminated")
|
|
.map((item) => item.time),
|
|
askedKeys: state.answered_probes.map((item) => item.semantic_key),
|
|
});
|
|
}
|
|
|
|
function scoreableSnapshotCurrentFromDossier(
|
|
dossier: DecisionDossier,
|
|
options: { currentEvidenceFingerprint?: string | null } | undefined,
|
|
inference: ReturnType<typeof previousInferenceFromReceipt>,
|
|
): boolean {
|
|
const latest = dossier.latestResult;
|
|
if (!latest) return true;
|
|
const stored = candidateSnapshotSource({
|
|
birthProfileFingerprint: latest.candidateRangeFingerprint ?? "",
|
|
scoreableEvidenceFingerprint: latest.evidenceLedgerFingerprint ?? "",
|
|
inferenceRevision: inference?.revision ?? 0,
|
|
candidateSetVersion: inference?.candidate_set_id ?? latest.resultId ?? "",
|
|
scoringPolicyVersion: String(latest.policyVersion ?? latest.algorithmVersion ?? ""),
|
|
});
|
|
const currentFingerprint = options?.currentEvidenceFingerprint
|
|
?? evidenceLedgerFingerprint(dossier.evidence as never);
|
|
const current = candidateSnapshotSource({
|
|
...stored,
|
|
scoreableEvidenceFingerprint: currentFingerprint ?? "",
|
|
});
|
|
return storedSnapshotIsCurrent(stored, current);
|
|
}
|
|
|
|
function mergeDroppedProbes(
|
|
...groups: readonly (readonly DroppedProbe[] | undefined)[]
|
|
): DroppedProbe[] {
|
|
const byKey = new Map<string, DroppedProbe>();
|
|
for (const group of groups) {
|
|
for (const item of group ?? []) {
|
|
if (!byKey.has(item.semantic_key)) byKey.set(item.semantic_key, item);
|
|
}
|
|
}
|
|
return [...byKey.values()];
|
|
}
|
|
|
|
export function followupAsksRenderableDiscriminator(
|
|
followup: { intent?: string; choice_frame?: unknown } | null | undefined,
|
|
): boolean {
|
|
return followup?.intent === "distinguish_candidates" && Boolean(followup.choice_frame);
|
|
}
|
|
|
|
export type DecideFromDossierOptions = Readonly<{
|
|
currentEvidenceFingerprint?: string | null;
|
|
birthDate?: string | null;
|
|
}>;
|
|
|
|
function decisionBudgetFromInference(inference: InferenceState | null | undefined) {
|
|
const rounds = inference?.rounds ?? [];
|
|
let plateauRounds = 0;
|
|
for (let index = rounds.length - 1; index >= 0; index -= 1) {
|
|
if (rounds[index]?.kind !== "low_information") break;
|
|
plateauRounds += 1;
|
|
}
|
|
return {
|
|
// The existing convergence evaluator budgets informative rounds. Keep the
|
|
// authoritative decision aligned with that persisted interpretation.
|
|
inferenceRounds: rounds.filter((item) => item.kind === "informative").length,
|
|
effectiveAnswerCount: inference?.answered_probes.length ?? 0,
|
|
plateauRounds,
|
|
};
|
|
}
|
|
|
|
function completedProbeForSemanticKey(
|
|
semanticKey: string,
|
|
packet: CandidateContrastPacket | null | undefined,
|
|
eventProbes: readonly DiscriminatingEventProbe[] | undefined,
|
|
): CandidateDiscriminatorProbe | null {
|
|
const fromPacket = (packet?.probes ?? []).find((item) => item.semanticKey === semanticKey);
|
|
if (fromPacket) {
|
|
const completed = withCompletedContrastOptions(fromPacket);
|
|
return completed.ok ? completed.probe : fromPacket;
|
|
}
|
|
const event = (eventProbes ?? []).find((item) => item.semantic_key === semanticKey);
|
|
if (!event) return null;
|
|
const built = buildCandidateContrastPacket({
|
|
candidateSetVersion: event.candidate_set_version ?? event.candidate_split_hash ?? "event",
|
|
calculationResultId: null,
|
|
engineProbes: [{
|
|
semantic_key: event.semantic_key,
|
|
candidate_split_hash: event.candidate_split_hash,
|
|
domain: event.domain,
|
|
year: event.year,
|
|
user_meaning: event.user_meaning,
|
|
question: event.user_meaning,
|
|
information_gain: event.information_gain,
|
|
expected_outcomes: event.expected_outcomes,
|
|
...(event.choice_kind ? { choice_kind: event.choice_kind } : {}),
|
|
...(event.style_options?.length ? { style_options: event.style_options } : {}),
|
|
}],
|
|
candidateTimes: [...(event.candidate_ids ?? [])],
|
|
});
|
|
const matched = built.probes.find((item) => item.semanticKey === semanticKey) ?? built.probes[0] ?? null;
|
|
if (!matched) return null;
|
|
const completed = withCompletedContrastOptions(matched);
|
|
return completed.ok ? completed.probe : matched;
|
|
}
|
|
|
|
function discriminatorProbeIfFollowupCanAsk(input: {
|
|
dossier: DecisionDossier;
|
|
inspected: ReturnType<typeof inspectDiscriminatorProbes>;
|
|
contrastPacket?: CandidateContrastPacket;
|
|
askedKeys?: readonly string[];
|
|
birthDate?: string | null;
|
|
}): {
|
|
probe: CandidateDiscriminatorProbe | null;
|
|
dropped: DroppedProbe[];
|
|
} {
|
|
const catalog = rectificationFollowupCatalog(input.dossier.latestResult, input.dossier.evidence);
|
|
const plan = buildMethodFollowupPlan({
|
|
evidence: input.dossier.evidence,
|
|
declinedTopics: input.dossier.conversationSummary.declinedSkippedTopics,
|
|
closedCollectFocuses: input.dossier.conversationSummary.declinedSkippedTopics,
|
|
sessionOutcome: "discriminate_candidates",
|
|
...catalog,
|
|
...(input.contrastPacket ? { contrastPacket: input.contrastPacket } : {}),
|
|
...(input.askedKeys ? { askedProbeKeys: input.askedKeys } : {}),
|
|
...(input.birthDate ? { birthDate: input.birthDate } : {}),
|
|
candidatesSeparated: false,
|
|
});
|
|
const dropped = mergeDroppedProbes(input.inspected.dropped, plan.dropped_probes);
|
|
if (!followupAsksRenderableDiscriminator(plan.next_followup)) {
|
|
return { probe: null, dropped };
|
|
}
|
|
const key = plan.next_followup?.semantic_key;
|
|
const packet = input.contrastPacket ?? catalog.contrastPacket;
|
|
const matched = key
|
|
? completedProbeForSemanticKey(key, packet, catalog.eventProbes)
|
|
: null;
|
|
return { probe: matched ?? input.inspected.selected, dropped };
|
|
}
|
|
|
|
export function decideFromDossier(
|
|
dossier: DecisionDossier,
|
|
options?: DecideFromDossierOptions,
|
|
): RectificationDecision {
|
|
const inference = previousInferenceFromReceipt(dossier.latestResult?.decisionReceipt ?? null);
|
|
const oosBlindPrompts = refinementFromDecisionReceipt(
|
|
dossier.latestResult?.decisionReceipt ?? null,
|
|
).oos_blind_prompts;
|
|
const trainingGate = trainingScoreableGate(dossier.evidence);
|
|
const collecting = buildMethodFollowupPlan({
|
|
evidence: dossier.evidence,
|
|
declinedTopics: dossier.conversationSummary.declinedSkippedTopics,
|
|
closedCollectFocuses: dossier.conversationSummary.declinedSkippedTopics,
|
|
sessionOutcome: "collect_evidence",
|
|
});
|
|
const latest = dossier.latestResult;
|
|
const decisionBudget = decisionBudgetFromInference(inference);
|
|
const snapshotCurrent = scoreableSnapshotCurrentFromDossier(dossier, options, inference);
|
|
const confirmationGate = buildConfirmationGate({
|
|
engineConfirmationAllowed: latest?.confirmationAllowed === true,
|
|
candidates: (latest?.candidates ?? []).map((candidate) => ({
|
|
time: candidate.time,
|
|
rank: candidate.rank ?? Number.MAX_SAFE_INTEGER,
|
|
tiedMinuteCount: candidate.tiedMinuteCount ?? Number.MAX_SAFE_INTEGER,
|
|
})),
|
|
decisionReceipt: latest?.decisionReceipt ?? null,
|
|
});
|
|
const askedKeys = askedDiscriminatorKeys(dossier.latestResult?.decisionReceipt, dossier.evidence);
|
|
const mentionedKeys = mentionedVargaKeysFromLedgerEvidence(dossier.evidence);
|
|
const topCandidateTimes = discriminatorCandidateTimes(dossier.latestResult);
|
|
const inspected = inspectDiscriminatorProbes(contrastPacketFromDossier(dossier), {
|
|
askedKeys,
|
|
mentionedKeys,
|
|
topCandidateTimes,
|
|
});
|
|
const gated = discriminatorProbeIfFollowupCanAsk({
|
|
dossier,
|
|
inspected,
|
|
askedKeys,
|
|
birthDate: options?.birthDate,
|
|
});
|
|
return {
|
|
...decideRectification({
|
|
methodCoverageAll: blockingMethodsCovered(collecting.methods),
|
|
trainingGateOpen: trainingGate.open,
|
|
confirmationAllowed: confirmationGate.confirmation_allowed,
|
|
userStopped: dossier.case.status === "paused",
|
|
snapshotCurrent,
|
|
candidateScores: candidateScoresFromDossier(dossier.latestResult),
|
|
discriminatorProbe: gated.probe,
|
|
holdoutValidation: holdoutStatusFromInference(inference, oosBlindPrompts),
|
|
accepted: Boolean(dossier.case.acceptedTime),
|
|
inferenceCredibleRange: inference?.credible_range ?? null,
|
|
engineAcceptAllowed: latest?.selectionAllowed === true
|
|
|| latest?.decisionReceipt?.accept_allowed === true,
|
|
engineProposeAllowed: latest?.decisionReceipt?.propose_allowed === true,
|
|
datedMethodCollectOpen: datedMethodCollectOpen(collecting.methods),
|
|
...decisionBudget,
|
|
}),
|
|
droppedProbes: gated.dropped,
|
|
};
|
|
}
|
|
|
|
export function decideAfterInferenceChange(input: {
|
|
dossier: DecisionDossier;
|
|
state: InferenceState | null;
|
|
userStopped: boolean;
|
|
birthDate?: string | null;
|
|
}): RectificationDecision {
|
|
const collecting = buildMethodFollowupPlan({
|
|
evidence: input.dossier.evidence,
|
|
declinedTopics: input.dossier.conversationSummary.declinedSkippedTopics,
|
|
closedCollectFocuses: input.dossier.conversationSummary.declinedSkippedTopics,
|
|
sessionOutcome: "collect_evidence",
|
|
});
|
|
if (!input.state) {
|
|
return decideRectification({
|
|
methodCoverageAll: blockingMethodsCovered(collecting.methods),
|
|
trainingGateOpen: trainingScoreableGate(input.dossier.evidence).open,
|
|
candidateScores: [],
|
|
userStopped: input.userStopped,
|
|
...decisionBudgetFromInference(null),
|
|
});
|
|
}
|
|
const training = input.state.events.filter((item) => item.usage === "training");
|
|
const trainingDomains = new Set(training.map((item) => item.domain));
|
|
const contrastPacket = contrastPacketFromState(input.state);
|
|
const topCandidateTimes = input.state.candidates
|
|
.filter((item) => item.status !== "eliminated")
|
|
.map((item) => item.time);
|
|
const inspected = inspectDiscriminatorProbes(contrastPacket, {
|
|
mentionedKeys: mentionedVargaKeysFromLedgerEvidence(input.dossier.evidence),
|
|
askedKeys: input.state.answered_probes.map((item) => item.semantic_key),
|
|
topCandidateTimes,
|
|
});
|
|
const gated = discriminatorProbeIfFollowupCanAsk({
|
|
dossier: input.dossier,
|
|
inspected,
|
|
contrastPacket,
|
|
askedKeys: input.state.answered_probes.map((item) => item.semantic_key),
|
|
birthDate: input.birthDate,
|
|
});
|
|
return {
|
|
...decideRectification({
|
|
methodCoverageAll: blockingMethodsCovered(collecting.methods),
|
|
trainingGateOpen: training.length >= MIN_ACCEPTANCE_EVENTS
|
|
&& trainingDomains.size >= MIN_ACCEPTANCE_DOMAINS,
|
|
candidateScores: input.state.candidates
|
|
.filter((item) => item.status !== "eliminated")
|
|
.map((item) => ({ time: item.time, score: item.posterior_score })),
|
|
discriminatorProbe: gated.probe,
|
|
holdoutValidation: holdoutStatusFromState(
|
|
input.state,
|
|
refinementFromDecisionReceipt(input.dossier.latestResult?.decisionReceipt ?? null).oos_blind_prompts,
|
|
),
|
|
inferenceCredibleRange: input.state.credible_range,
|
|
userStopped: input.userStopped,
|
|
accepted: Boolean(input.dossier.case.acceptedTime),
|
|
engineAcceptAllowed: input.dossier.latestResult?.selectionAllowed === true
|
|
|| input.dossier.latestResult?.decisionReceipt?.accept_allowed === true,
|
|
engineProposeAllowed: input.dossier.latestResult?.decisionReceipt?.propose_allowed === true,
|
|
datedMethodCollectOpen: datedMethodCollectOpen(collecting.methods),
|
|
...decisionBudgetFromInference(input.state),
|
|
}),
|
|
droppedProbes: gated.dropped,
|
|
};
|
|
}
|
|
|
|
export function overlayPublicDecision<T extends object>(
|
|
snapshot: T,
|
|
decision: RectificationDecision,
|
|
): T & ReturnType<typeof publicDecisionFields> & {
|
|
selectionAllowed: boolean;
|
|
validated: boolean;
|
|
completionStatus: ReturnType<typeof publicDecisionFields>["completion_status"];
|
|
} {
|
|
const fields = publicDecisionFields(decision);
|
|
const projection = authoritativeCandidateProjection(snapshot as DecisionDossier["latestResult"]);
|
|
const inconsistent = !projection.consistent;
|
|
return {
|
|
...snapshot,
|
|
...fields,
|
|
...(projection.fromInference
|
|
? {
|
|
candidates: projection.candidates,
|
|
representativeTime: projection.representativeTime,
|
|
representative_time: projection.representativeTime,
|
|
credibleRange: projection.credibleRange,
|
|
credible_range: projection.credibleRange,
|
|
}
|
|
: {}),
|
|
...(inconsistent ? { can_adopt: false, selection_allowed: false } : {}),
|
|
selectionAllowed: inconsistent ? false : fields.selection_allowed,
|
|
validated: fields.validated,
|
|
completionStatus: fields.completion_status,
|
|
};
|
|
}
|