fix(rectification): treat a yes discriminator as covering that domain's collect
A choice-card yes/weak_yes already told us the domain happened, so skip the same-domain "which year" collect without writing the ledger. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1048,9 +1048,15 @@ async function persistExhaustionCollect(input: {
|
||||
hostNarration: string;
|
||||
focus?: ConversationFocus | null;
|
||||
}> {
|
||||
const catalog = rectificationFollowupCatalog(
|
||||
input.dossier.latestResult ?? null,
|
||||
input.dossier.evidence,
|
||||
);
|
||||
const followup = exhaustionSpokenCollectFollowup({
|
||||
evidence: input.dossier.evidence,
|
||||
declinedTopics: input.dossier.conversationSummary.declinedSkippedTopics,
|
||||
answeredProbes: catalog.answeredProbes,
|
||||
eventProbes: catalog.eventProbes,
|
||||
});
|
||||
const range = nonConvergingRangeNarration({
|
||||
...input.decision,
|
||||
|
||||
@@ -282,6 +282,7 @@ export function rectificationFollowupCatalog(
|
||||
contrastPacket: contrastPacketFromLatestResult(latest ?? null, evidence),
|
||||
topCandidateTimes,
|
||||
askedProbeKeys: askedKeys,
|
||||
answeredProbes: inference?.answered_probes ?? [],
|
||||
eventProbes: refinement.discriminating_event_probes,
|
||||
eventClarificationProbes: refinement.event_clarification_probes,
|
||||
evidenceCollectionProbes: refinement.evidence_collection_probes,
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
* Known-event quality probes (exam went badly for a year already
|
||||
* in the ledger) are not reverse-inference cards. Dasha existence
|
||||
* probes skip a year already in the ledger, not the whole domain.
|
||||
* A yes/weak_yes discriminator answer covers that domain's spoken
|
||||
* collect without writing the ledger. no/unsure does not.
|
||||
* Contrast-packet ranking must use the same year rule: a dated
|
||||
* unstructured probe stays eligible when the domain already has a
|
||||
* different year. Information gain is recomputed on the current
|
||||
@@ -223,6 +225,47 @@ function hasConfirmedDomain(evidence: readonly MethodFollowupEvidence[], domain:
|
||||
return evidence.some((item) => item.status === "confirmed" && item.domain === domain);
|
||||
}
|
||||
|
||||
export type AnsweredProbeCoverageRow = Readonly<{
|
||||
semantic_key: string;
|
||||
probe_id?: string;
|
||||
answer_class: string;
|
||||
classified_from?: string;
|
||||
}>;
|
||||
|
||||
function domainForAnsweredProbe(
|
||||
answer: AnsweredProbeCoverageRow,
|
||||
eventProbes: readonly DiscriminatingEventProbe[],
|
||||
): string | null {
|
||||
const keys = new Set(
|
||||
[answer.semantic_key, answer.probe_id].filter((key): key is string => Boolean(key?.trim())),
|
||||
);
|
||||
if (keys.size === 0) return null;
|
||||
for (const probe of eventProbes) {
|
||||
const semantic = probe.semantic_key?.trim() ?? "";
|
||||
if (!semantic || !keys.has(semantic)) continue;
|
||||
const domain = probe.domain?.trim();
|
||||
if (domain) return domain;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Domains where a discriminator was answered yes/weak_yes. Lookup is by probe identity, not key prefix. */
|
||||
export function domainsAnsweredYes(
|
||||
answeredProbes: readonly AnsweredProbeCoverageRow[] | undefined,
|
||||
eventProbes: readonly DiscriminatingEventProbe[] | undefined,
|
||||
): Set<string> {
|
||||
const domains = new Set<string>();
|
||||
if (!answeredProbes?.length) return domains;
|
||||
const catalog = eventProbes ?? [];
|
||||
for (const answer of answeredProbes) {
|
||||
if (answer.answer_class !== "yes" && answer.answer_class !== "weak_yes") continue;
|
||||
if (answer.classified_from && answer.classified_from !== "choice") continue;
|
||||
const domain = domainForAnsweredProbe(answer, catalog);
|
||||
if (domain) domains.add(domain);
|
||||
}
|
||||
return domains;
|
||||
}
|
||||
|
||||
function evidenceYear(item: MethodFollowupEvidence): number | null {
|
||||
const raw = item.occurredFrom || item.occurredTo;
|
||||
if (!raw || raw.length < 4 || !/^\d{4}/.test(raw)) return null;
|
||||
@@ -1070,19 +1113,26 @@ function datedCollectDomainBlocked(
|
||||
domain: (typeof DATED_COLLECT_ORDER)[number],
|
||||
evidence: readonly MethodFollowupEvidence[],
|
||||
declined: ReadonlySet<string>,
|
||||
answeredYes: ReadonlySet<string> = new Set(),
|
||||
): boolean {
|
||||
if (domain === "health_pressure") {
|
||||
return declinedHealth(declined) || hasConfirmedHealth(evidence);
|
||||
return declinedHealth(declined)
|
||||
|| hasConfirmedHealth(evidence)
|
||||
|| answeredYes.has("health_pressure")
|
||||
|| answeredYes.has("health");
|
||||
}
|
||||
return declined.has(domain) || hasConfirmedDomain(evidence, domain);
|
||||
return declined.has(domain)
|
||||
|| hasConfirmedDomain(evidence, domain)
|
||||
|| answeredYes.has(domain);
|
||||
}
|
||||
|
||||
export function nextDatedCollectFollowup(
|
||||
evidence: readonly MethodFollowupEvidence[],
|
||||
declined: ReadonlySet<string>,
|
||||
answeredYes: ReadonlySet<string> = new Set(),
|
||||
): MethodFollowup | null {
|
||||
for (const domain of DATED_COLLECT_ORDER) {
|
||||
if (datedCollectDomainBlocked(domain, evidence, declined)) continue;
|
||||
if (datedCollectDomainBlocked(domain, evidence, declined, answeredYes)) continue;
|
||||
const next = datedCollectFollowup(domain, evidence);
|
||||
if (next) return next;
|
||||
}
|
||||
@@ -1125,9 +1175,12 @@ function otherCollectFollowup(evidence: readonly MethodFollowupEvidence[]): Meth
|
||||
export function exhaustionSpokenCollectFollowup(input: {
|
||||
evidence: readonly MethodFollowupEvidence[];
|
||||
declinedTopics?: readonly Readonly<Record<string, unknown>>[];
|
||||
answeredProbes?: readonly AnsweredProbeCoverageRow[];
|
||||
eventProbes?: readonly DiscriminatingEventProbe[];
|
||||
}): MethodFollowup | null {
|
||||
const declined = declinedDomains(input.declinedTopics ?? []);
|
||||
const dated = nextDatedCollectFollowup(input.evidence, declined);
|
||||
const answeredYes = domainsAnsweredYes(input.answeredProbes, input.eventProbes);
|
||||
const dated = nextDatedCollectFollowup(input.evidence, declined, answeredYes);
|
||||
if (dated) return dated;
|
||||
if (
|
||||
!declined.has("occupation")
|
||||
@@ -1506,6 +1559,7 @@ export function buildMethodFollowupPlan(input: {
|
||||
eventClarificationProbes?: readonly DiscriminatingEventProbe[];
|
||||
evidenceCollectionProbes?: readonly DiscriminatingEventProbe[];
|
||||
askedProbeKeys?: readonly string[];
|
||||
answeredProbes?: readonly AnsweredProbeCoverageRow[];
|
||||
closedCollectFocuses?: readonly Readonly<Record<string, unknown>>[];
|
||||
birthDate?: string | null;
|
||||
accepted?: boolean;
|
||||
@@ -1559,10 +1613,18 @@ export function buildMethodFollowupPlan(input: {
|
||||
const ask = (why: string, varga: string, extra = "") =>
|
||||
agentHint(why, varga, extra, input.evidence);
|
||||
const declined = declinedDomains(input.declinedTopics ?? []);
|
||||
const answeredYes = domainsAnsweredYes(input.answeredProbes, [
|
||||
...(input.eventProbes ?? []),
|
||||
...(input.eventClarificationProbes ?? []),
|
||||
...(input.evidenceCollectionProbes ?? []),
|
||||
]);
|
||||
const dashaCovered = input.evidence.some(isConfirmedDated);
|
||||
const relationshipCovered = hasConfirmedDomain(input.evidence, "relationship");
|
||||
const careerCovered = hasConfirmedDomain(input.evidence, "career");
|
||||
const familyCovered = hasConfirmedDomain(input.evidence, "family");
|
||||
const relationshipCovered = hasConfirmedDomain(input.evidence, "relationship")
|
||||
|| answeredYes.has("relationship");
|
||||
const careerCovered = hasConfirmedDomain(input.evidence, "career")
|
||||
|| answeredYes.has("career");
|
||||
const familyCovered = hasConfirmedDomain(input.evidence, "family")
|
||||
|| answeredYes.has("family");
|
||||
const financeCovered = hasConfirmedDomain(input.evidence, "finance");
|
||||
const healthCovered = hasConfirmedHealth(input.evidence);
|
||||
const occupationCovered = hasConfirmedDomain(input.evidence, "occupation")
|
||||
@@ -2120,7 +2182,7 @@ export function buildMethodFollowupPlan(input: {
|
||||
&& precisionCard?.choice_frame
|
||||
) {
|
||||
next = precisionCard;
|
||||
} else if ((datedCollect = nextDatedCollectFollowup(input.evidence, declined))) {
|
||||
} else if ((datedCollect = nextDatedCollectFollowup(input.evidence, declined, answeredYes))) {
|
||||
next = makeFollowup(datedCollect);
|
||||
} else if (!occupationCovered) {
|
||||
if (meetsAcceptanceEventQuality(input.evidence)) {
|
||||
|
||||
Reference in New Issue
Block a user