fix(rectification): drop same-year probes after a dated answer (BUG-592)
Point-choice used one compare packet, so a May existence card and the same-domain year card both stayed eligible. Hard-exclude same domain+year; keep adjacent years downranked. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -17,6 +17,8 @@ import {
|
||||
isRenderableProbe,
|
||||
type DroppedProbe,
|
||||
rankDiscriminatorScore,
|
||||
sameYearProbeAsked,
|
||||
SEMANTIC_YEAR_KEY,
|
||||
} from "../v9/probe-question-contract.ts";
|
||||
|
||||
export type ContrastChoiceKind = "existence" | "varga_style" | "event_quality";
|
||||
@@ -405,6 +407,18 @@ export function vargaLayerFromSemanticKey(key: string): string | null {
|
||||
return key.match(/^varga\.(d\d+)/)?.[1] ?? null;
|
||||
}
|
||||
|
||||
function probeDomainYear(probe: Pick<CandidateDiscriminatorProbe, "domain" | "year" | "semanticKey">): {
|
||||
domain: string;
|
||||
year: number;
|
||||
} | null {
|
||||
if (probe.domain && probe.year && probe.year > 0) {
|
||||
return { domain: probe.domain, year: probe.year };
|
||||
}
|
||||
const match = probe.semanticKey.match(SEMANTIC_YEAR_KEY);
|
||||
if (!match) return null;
|
||||
return { domain: match[1], year: Number(match[2]) };
|
||||
}
|
||||
|
||||
export function inspectDiscriminatorProbes(
|
||||
packet: CandidateContrastPacket | null | undefined,
|
||||
options?: {
|
||||
@@ -461,6 +475,15 @@ export function inspectDiscriminatorProbes(
|
||||
});
|
||||
return [];
|
||||
}
|
||||
const domainYear = probeDomainYear(completed.probe);
|
||||
if (domainYear && sameYearProbeAsked(asked, domainYear.domain, domainYear.year)) {
|
||||
dropped.push({
|
||||
semantic_key: completed.probe.semanticKey,
|
||||
information_gain: completed.probe.informationGain,
|
||||
reason: "same_year_asked",
|
||||
});
|
||||
return [];
|
||||
}
|
||||
const layer = vargaLayerFromSemanticKey(completed.probe.semanticKey);
|
||||
const askedAlready = asked.has(completed.probe.semanticKey)
|
||||
|| asked.has(completed.probe.candidateSplitHash)
|
||||
|
||||
@@ -110,6 +110,8 @@ import {
|
||||
informationGainAmongActive,
|
||||
isRenderableProbe,
|
||||
rankDiscriminatorScore,
|
||||
sameYearProbeAsked,
|
||||
SEMANTIC_YEAR_KEY,
|
||||
EXISTENCE_STYLE_OPTIONS,
|
||||
type DroppedProbe,
|
||||
type ProbeStyleOption,
|
||||
@@ -142,6 +144,7 @@ import {
|
||||
} from "./window-widen.ts";
|
||||
import { DATE_RELIABILITY_PROMPT, pendingDateReliabilityEvidence } from "./date-reliability.ts";
|
||||
|
||||
export { sameYearProbeAsked };
|
||||
|
||||
export const METHOD_FOLLOWUP_IDS = [
|
||||
"dasha_events",
|
||||
@@ -335,18 +338,18 @@ function existenceNearbyYears(domain: string): number {
|
||||
return EXISTENCE_NEARBY_YEARS[domain] ?? 0;
|
||||
}
|
||||
|
||||
const SEMANTIC_YEAR = /^([a-z_]+)\.((?:19|20)\d{2})(?:\.|$)/;
|
||||
|
||||
export function existenceProbeAsked(
|
||||
askedKeys: ReadonlySet<string> | readonly string[],
|
||||
domain: string,
|
||||
year: number,
|
||||
): boolean {
|
||||
if (!domain || !year) return false;
|
||||
if (sameYearProbeAsked(askedKeys, domain, year)) return true;
|
||||
const asked = askedKeys instanceof Set ? askedKeys : new Set(askedKeys);
|
||||
const nearby = existenceNearbyYears(domain);
|
||||
if (nearby <= 0) return false;
|
||||
for (const key of asked) {
|
||||
const match = key.match(SEMANTIC_YEAR);
|
||||
const match = key.match(SEMANTIC_YEAR_KEY);
|
||||
if (!match || match[1] !== domain) continue;
|
||||
const askedYear = Number(match[2]);
|
||||
if (Number.isInteger(askedYear) && Math.abs(askedYear - year) <= nearby) return true;
|
||||
@@ -806,7 +809,7 @@ function remainingConflictProbes(
|
||||
if (
|
||||
askedKeys.has(semantic)
|
||||
|| (split && askedKeys.has(split))
|
||||
|| existenceProbeAsked(askedKeys, probe.domain, probe.year)
|
||||
|| sameYearProbeAsked(askedKeys, probe.domain, probe.year)
|
||||
) continue;
|
||||
rows.push(probe);
|
||||
}
|
||||
@@ -889,6 +892,9 @@ function renderableEventProbe(
|
||||
if (!rankedGain) {
|
||||
return { row: null, dropped: droppedFromProbe(key, probe.information_gain ?? 0, "no_split_among_active") };
|
||||
}
|
||||
if (sameYearProbeAsked(askedKeys, probe.domain, probe.year)) {
|
||||
return { row: null, dropped: droppedFromProbe(key, probe.information_gain ?? 0, "same_year_asked") };
|
||||
}
|
||||
const layer = vargaLayerFromSemanticKey(key);
|
||||
const asked = askedKeys.has(key)
|
||||
|| Boolean(probe.candidate_split_hash && askedKeys.has(probe.candidate_split_hash))
|
||||
@@ -960,10 +966,17 @@ function renderableContrastProbe(
|
||||
dropped: droppedFromProbe(working.semanticKey, working.informationGain, "no_split_among_active"),
|
||||
};
|
||||
}
|
||||
if (sameYearProbeAsked(askedKeys, working.domain, working.year)) {
|
||||
return {
|
||||
row: null,
|
||||
dropped: droppedFromProbe(working.semanticKey, working.informationGain, "same_year_asked"),
|
||||
};
|
||||
}
|
||||
const layer = vargaLayerFromSemanticKey(working.semanticKey);
|
||||
const asked = askedKeys.has(working.semanticKey)
|
||||
|| askedKeys.has(working.candidateSplitHash)
|
||||
|| askedKeys.has(working.probeId)
|
||||
|| existenceProbeAsked(askedKeys, working.domain ?? "", working.year ?? 0)
|
||||
|| (layer ? vargaLayerCovered(mentionedKeys, layer) : false);
|
||||
return {
|
||||
row: {
|
||||
|
||||
@@ -36,7 +36,8 @@ export type ProbeRejectReason =
|
||||
| "yearless_ungrounded_contrast"
|
||||
| "no_split_among_active"
|
||||
| "unanchored_varga_style"
|
||||
| "frameless_distinguish";
|
||||
| "frameless_distinguish"
|
||||
| "same_year_asked";
|
||||
|
||||
export type StyleOptionsResult =
|
||||
| { ok: true; options: ProbeStyleOption[] }
|
||||
@@ -312,6 +313,23 @@ export function discriminatorPriority(input: {
|
||||
return input.informationGain * novelty * coverage - penalty;
|
||||
}
|
||||
|
||||
export const SEMANTIC_YEAR_KEY = /^([a-z_]+)\.((?:19|20)\d{2})(?:\.|$)/;
|
||||
|
||||
export function sameYearProbeAsked(
|
||||
askedKeys: ReadonlySet<string> | readonly string[],
|
||||
domain: string | null | undefined,
|
||||
year: number | null | undefined,
|
||||
): boolean {
|
||||
if (!domain || !year) return false;
|
||||
const asked = askedKeys instanceof Set ? askedKeys : new Set(askedKeys);
|
||||
for (const key of asked) {
|
||||
const match = key.match(SEMANTIC_YEAR_KEY);
|
||||
if (!match || match[1] !== domain) continue;
|
||||
if (Number(match[2]) === year) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function rankDiscriminatorScore(input: {
|
||||
informationGain: number;
|
||||
asked?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user