Cache work that depends only on the candidate minute, drop unread response fields, and reject windows above 241 candidates before any chart is built. Eleven canonical scenario hashes match 77057a819. Sealed and reported-offset replays match the 2026-10-08 records (changed_trial_count 8 and 581) and are stored as scoring_perf_2026_10_13.
Co-Authored-By: Grok (agent loop)
742 lines
27 KiB
TypeScript
742 lines
27 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
RectificationEngineError,
|
|
RECTIFICATION_ENGINE_BUSY_BACKOFF_BUDGET_MS,
|
|
RECTIFICATION_ENGINE_BUSY_RETRY_LIMIT,
|
|
engineRequestBody,
|
|
mergeVedastroValidateIntoReceipt,
|
|
nextEngineBusyBackoffMs,
|
|
runV9CandidateScore,
|
|
runV9Diagnostics,
|
|
runV9VedastroValidate,
|
|
sanitizeAskedProbeKeysForEngine,
|
|
setEngineBusyWaitForTests,
|
|
toEngineEvents,
|
|
type V9EngineScoreResult,
|
|
} from "../src/lib/rectification-agentic/v9/engine-client.ts";
|
|
import { askedSemanticKeysForEngine } from "../src/lib/rectification-agentic/v9/inference-adapter.ts";
|
|
|
|
const RANGE = { start_time: "04:50", end_time: "05:10" };
|
|
const CANDIDATE_ID = "88888888-8888-4888-8888-888888888881";
|
|
const SECOND_CANDIDATE_ID = "88888888-8888-4888-8888-888888888882";
|
|
const THIRD_CANDIDATE_ID = "88888888-8888-4888-8888-888888888883";
|
|
|
|
const DECISION_RECEIPT = {
|
|
receipt_version: "candidate-decision-receipt-v2",
|
|
contract_version: "v2",
|
|
event_contract_version: "rectification-event-contract-v2",
|
|
policy_version: "rectification-candidate-policy-v2",
|
|
decision_policy_version: "rectification-candidate-policy-v2",
|
|
display_allowed: true,
|
|
selection_allowed: true,
|
|
acceptance_allowed: true,
|
|
propose_allowed: true,
|
|
confirmation_allowed: false,
|
|
accept_allowed: true,
|
|
confirm_allowed: false,
|
|
representative_candidate_id: CANDIDATE_ID,
|
|
representative_time: "04:50",
|
|
overall_confidence: "high",
|
|
margin_percent: 42.5,
|
|
};
|
|
|
|
const EXECUTION_LEDGER = [
|
|
{ ledger_version: "rectification-execution-ledger-v2", stage: "technique_layer", method: "d1-rashi", status: "executed", source: "python-engine" },
|
|
{ ledger_version: "rectification-execution-ledger-v2", stage: "technique_layer", method: "gochara", status: "executed", source: "python-engine" },
|
|
{ ledger_version: "rectification-execution-ledger-v2", stage: "technique_layer", method: "d24-chaturvimshamsha", status: "not_executed", source: "python-engine" },
|
|
];
|
|
|
|
const ENGINE_SCORE_RESPONSE_V2 = {
|
|
success: true,
|
|
endpoint: "rectification_v5_score",
|
|
result_id: "e4fbf2e0-85dc-5b42-a5a3-34e5dd4b7e62",
|
|
algorithm_version: "rectification-event-contract-v2",
|
|
event_contract_version: "rectification-event-contract-v2",
|
|
decision_policy_version: "rectification-candidate-policy-v2",
|
|
execution_ledger_version: "rectification-execution-ledger-v2",
|
|
candidate_decisions: [
|
|
{ candidate_id: CANDIDATE_ID, time: "04:50", rank: 1, relative_support: 57, tied_minute_count: 1 },
|
|
{ candidate_id: SECOND_CANDIDATE_ID, time: "04:51", rank: 3, relative_support: 18, tied_minute_count: 2 },
|
|
{ candidate_id: THIRD_CANDIDATE_ID, time: "04:52", rank: 2, relative_support: 25, tied_minute_count: 2 },
|
|
],
|
|
decision_receipt: DECISION_RECEIPT,
|
|
execution_ledger: EXECUTION_LEDGER,
|
|
diagnostics: {
|
|
primary_secondary_margin_percent: 99,
|
|
leave_one_event_out_retention_rate: 1,
|
|
},
|
|
candidate_scores: [
|
|
{ time: "04:59", score: 999999 },
|
|
],
|
|
};
|
|
|
|
const ENGINE_DIAGNOSTICS_RESPONSE_V2 = {
|
|
success: true,
|
|
endpoint: "rectification_v5_diagnostics",
|
|
result_id: "e4fbf2e0-85dc-5b42-a5a3-34e5dd4b7e62",
|
|
algorithm_version: "rectification-event-contract-v2",
|
|
event_contract_version: "rectification-event-contract-v2",
|
|
decision_policy_version: "rectification-candidate-policy-v2",
|
|
execution_ledger_version: "rectification-execution-ledger-v2",
|
|
candidate_decisions: ENGINE_SCORE_RESPONSE_V2.candidate_decisions,
|
|
diagnostics: {
|
|
primary_cluster_retention_rate: 0.86,
|
|
leave_one_event_out_retention_rate: 0.81,
|
|
leave_one_domain_out_retention_rate: 0.9,
|
|
date_sensitivity_retention_rate: 0.72,
|
|
neighbor_support_minutes: 2,
|
|
primary_secondary_margin_percent: 42.5,
|
|
unstable_event_ids: [],
|
|
most_discriminating_layers: ["vimsottari_dasha"],
|
|
candidate_splits: [{ candidate_id: SECOND_CANDIDATE_ID, width: 6 }],
|
|
},
|
|
missing_layers: ["KP_cusps"],
|
|
decision_receipt: DECISION_RECEIPT,
|
|
execution_ledger: EXECUTION_LEDGER,
|
|
};
|
|
|
|
function stubEngine(response: unknown, status = 200) {
|
|
const previous = globalThis.fetch;
|
|
globalThis.fetch = (async () => ({
|
|
ok: status >= 200 && status < 300,
|
|
status,
|
|
json: async () => response,
|
|
})) as unknown as typeof fetch;
|
|
return () => {
|
|
globalThis.fetch = previous;
|
|
};
|
|
}
|
|
|
|
const SNAPSHOT = {
|
|
birth_date: "1997-08-08",
|
|
latitude: 36.420487,
|
|
longitude: 114.209936,
|
|
timezone_offset: 8,
|
|
birth_time_source: "family_exact",
|
|
};
|
|
|
|
const EVIDENCE = [
|
|
{
|
|
id: "00000000-0000-4000-8000-000000000001",
|
|
sourceTurnId: "33333333-3333-4333-8333-333333333333",
|
|
subject: "self",
|
|
eventKind: "education_start",
|
|
domain: "education",
|
|
occurredFrom: "2016-09-01",
|
|
occurredTo: "2016-09-30",
|
|
datePrecision: "month",
|
|
summary: "大学入学",
|
|
status: "confirmed",
|
|
supersedesEvidenceId: null,
|
|
createdAt: "2026-08-12T10:00:06.000Z",
|
|
dateSource: "user_quote",
|
|
dateReliability: "month_exact",
|
|
dateCorroboration: "录取通知书",
|
|
dateConflictStatus: "none",
|
|
},
|
|
];
|
|
|
|
test("toEngineEvents preserves Event Contract v2 detailed kind and provenance", () => {
|
|
const events = toEngineEvents(EVIDENCE);
|
|
assert.equal(events.length, 1);
|
|
assert.equal(events[0]!.domain, "education");
|
|
assert.equal(events[0]!.event_kind, "education_start");
|
|
assert.equal(events[0]!.date_start, "2016-09-01");
|
|
assert.equal(events[0]!.precision, "month");
|
|
assert.equal(events[0]!.date_source, "user_quote");
|
|
assert.equal(events[0]!.date_reliability, "month_exact");
|
|
assert.equal(events[0]!.date_corroboration, "录取通知书");
|
|
assert.equal(events[0]!.date_conflict_status, "none");
|
|
assert.equal(events[0]!.source_turn_id, EVIDENCE[0]!.sourceTurnId);
|
|
assert.equal(events[0]!.subject, "self");
|
|
});
|
|
|
|
test("toEngineEvents keeps all scoreable detailed v2 kinds and drops background kinds", () => {
|
|
const detailed = [
|
|
"education_completion",
|
|
"career_entry",
|
|
"promotion",
|
|
"career_pressure",
|
|
"relationship_commitment",
|
|
"relationship_separation",
|
|
"finance_gain",
|
|
"finance_loss",
|
|
"self_health_event",
|
|
"family_event",
|
|
].map((eventKind, index) => ({
|
|
...EVIDENCE[0]!,
|
|
id: `00000000-0000-4000-8000-${String(index + 2).padStart(12, "0")}`,
|
|
eventKind,
|
|
domain: eventKind.startsWith("education") ? "education"
|
|
: eventKind.startsWith("career") || eventKind === "promotion" ? "career"
|
|
: eventKind.startsWith("relationship") ? "relationship"
|
|
: eventKind.startsWith("finance") ? "finance"
|
|
: eventKind === "family_event" ? "family"
|
|
: "health",
|
|
}));
|
|
const background = [
|
|
{ ...EVIDENCE[0]!, id: "00000000-0000-4000-8000-000000000100", eventKind: "other", domain: "other" },
|
|
];
|
|
assert.deepEqual(toEngineEvents([...detailed, ...background]).map((event) => event.event_kind), detailed.map((event) => event.eventKind));
|
|
const family = toEngineEvents([{
|
|
...EVIDENCE[0]!,
|
|
id: "00000000-0000-4000-8000-000000000200",
|
|
eventKind: "family_event",
|
|
domain: "family",
|
|
subject: "self",
|
|
}]);
|
|
assert.equal(family[0]!.subject, "family");
|
|
assert.deepEqual(toEngineEvents([{
|
|
...EVIDENCE[0]!,
|
|
id: "00000000-0000-4000-8000-000000000201",
|
|
eventKind: "appearance_note",
|
|
domain: "appearance",
|
|
datePrecision: "unknown",
|
|
occurredFrom: null,
|
|
occurredTo: null,
|
|
}]), []);
|
|
const datedAppearance = toEngineEvents([{
|
|
...EVIDENCE[0]!,
|
|
id: "00000000-0000-4000-8000-000000000202",
|
|
eventKind: "birthmark_or_scar",
|
|
domain: "marks",
|
|
datePrecision: "year",
|
|
occurredFrom: "2010-01-01",
|
|
occurredTo: "2010-12-31",
|
|
}]);
|
|
assert.equal(datedAppearance[0]!.event_kind, "birthmark_or_scar");
|
|
assert.equal(datedAppearance[0]!.domain, "marks");
|
|
const occupation = toEngineEvents([{
|
|
...EVIDENCE[0]!,
|
|
id: "00000000-0000-4000-8000-000000000203",
|
|
eventKind: "occupation_note",
|
|
domain: "occupation",
|
|
datePrecision: "year",
|
|
occurredFrom: "2019-01-01",
|
|
occurredTo: "2019-12-31",
|
|
}]);
|
|
assert.equal(occupation[0]!.event_kind, "occupation_note");
|
|
assert.equal(occupation[0]!.domain, "occupation");
|
|
const horary = toEngineEvents([{
|
|
...EVIDENCE[0]!,
|
|
id: "00000000-0000-4000-8000-000000000204",
|
|
eventKind: "horary_query",
|
|
domain: "horary",
|
|
datePrecision: "day",
|
|
occurredFrom: "2024-01-15",
|
|
occurredTo: "2024-01-15",
|
|
}]);
|
|
assert.equal(horary[0]!.event_kind, "horary_query");
|
|
assert.equal(horary[0]!.domain, "horary");
|
|
assert.deepEqual(toEngineEvents([{
|
|
...EVIDENCE[0]!,
|
|
id: "00000000-0000-4000-8000-000000000205",
|
|
eventKind: "horary_query",
|
|
domain: "horary",
|
|
datePrecision: "unknown",
|
|
occurredFrom: null,
|
|
occurredTo: null,
|
|
}]), []);
|
|
});
|
|
|
|
test("runV9CandidateScore strictly consumes server candidate decisions and v2 receipt", async () => {
|
|
const restore = stubEngine(ENGINE_SCORE_RESPONSE_V2);
|
|
try {
|
|
const result: V9EngineScoreResult = await runV9CandidateScore({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
});
|
|
assert.deepEqual(result.candidates, [
|
|
{ candidateId: CANDIDATE_ID, time: "04:50", rank: 1, relativeSupport: 57, tiedMinuteCount: 1 },
|
|
{ candidateId: SECOND_CANDIDATE_ID, time: "04:51", rank: 3, relativeSupport: 18, tiedMinuteCount: 2 },
|
|
{ candidateId: THIRD_CANDIDATE_ID, time: "04:52", rank: 2, relativeSupport: 25, tiedMinuteCount: 2 },
|
|
], "Web must preserve server rank/support/tie instead of deriving or sorting them");
|
|
assert.equal(result.representativeCandidateId, CANDIDATE_ID);
|
|
assert.equal(result.representativeTime, "04:50");
|
|
assert.equal(result.selectionAllowed, true);
|
|
assert.equal(result.confirmationAllowed, false);
|
|
assert.equal(result.overallConfidence, "high");
|
|
assert.equal(result.policyVersion, "rectification-candidate-policy-v2");
|
|
assert.deepEqual(result.decisionReceipt, DECISION_RECEIPT);
|
|
assert.deepEqual(result.executionLedger, EXECUTION_LEDGER);
|
|
assert.deepEqual(result.executedMethods, ["d1-rashi", "gochara"], "methods come only from executed server ledger entries");
|
|
assert.equal(JSON.stringify(result).includes("999999"), false, "raw internal score must not enter the TS projection");
|
|
} finally {
|
|
restore();
|
|
}
|
|
});
|
|
|
|
test("runV9CandidateScore fails closed on a mismatched question contract version", async () => {
|
|
const restore = stubEngine({
|
|
...ENGINE_SCORE_RESPONSE_V2,
|
|
decision_receipt: { ...DECISION_RECEIPT, question_contract_version: "probe-question-v0" },
|
|
});
|
|
try {
|
|
await assert.rejects(
|
|
runV9CandidateScore({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
}),
|
|
(error: unknown) => error instanceof RectificationEngineError && error.code === "engine_invalid_v2_receipt",
|
|
);
|
|
} finally {
|
|
restore();
|
|
}
|
|
});
|
|
|
|
test("runV9CandidateScore fails closed without a v2 decision receipt", async () => {
|
|
const missingReceipt = Object.fromEntries(
|
|
Object.entries(ENGINE_SCORE_RESPONSE_V2).filter(([key]) => key !== "decision_receipt"),
|
|
);
|
|
const restore = stubEngine(missingReceipt);
|
|
try {
|
|
await assert.rejects(
|
|
runV9CandidateScore({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
}),
|
|
(error: unknown) => error instanceof RectificationEngineError && error.code === "engine_invalid_v2_receipt",
|
|
);
|
|
} finally {
|
|
restore();
|
|
}
|
|
});
|
|
|
|
test("runV9CandidateScore fails closed on invalid server candidate UUIDs", async () => {
|
|
const restore = stubEngine({
|
|
...ENGINE_SCORE_RESPONSE_V2,
|
|
candidate_decisions: [{ ...ENGINE_SCORE_RESPONSE_V2.candidate_decisions[0], candidate_id: "04:50" }],
|
|
});
|
|
try {
|
|
await assert.rejects(
|
|
runV9CandidateScore({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
}),
|
|
(error: unknown) => error instanceof RectificationEngineError && error.code === "engine_invalid_candidate_decisions",
|
|
);
|
|
} finally {
|
|
restore();
|
|
}
|
|
});
|
|
|
|
test("runV9CandidateScore fails closed when no evidence is scoreable", async () => {
|
|
const restore = stubEngine(ENGINE_SCORE_RESPONSE_V2);
|
|
try {
|
|
await assert.rejects(
|
|
runV9CandidateScore({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents([{ ...EVIDENCE[0]!, eventKind: "other", domain: "other" }]),
|
|
}),
|
|
(error: unknown) => error instanceof RectificationEngineError && error.code === "no_scorable_evidence",
|
|
);
|
|
} finally {
|
|
restore();
|
|
}
|
|
});
|
|
|
|
test("runV9Diagnostics maps diagnostics and receipt-owned confirmation", async () => {
|
|
const restore = stubEngine(ENGINE_DIAGNOSTICS_RESPONSE_V2);
|
|
try {
|
|
const result = await runV9Diagnostics({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
});
|
|
assert.equal(result.canConfirmExactMinute, false);
|
|
assert.equal(result.missingLayers.join(","), "KP_cusps");
|
|
assert.equal(result.diagnostics.primary_secondary_margin_percent, 42.5);
|
|
assert.deepEqual(result.executedMethods, ["d1-rashi", "gochara"]);
|
|
assert.equal(result.policyVersion, "rectification-candidate-policy-v2");
|
|
} finally {
|
|
restore();
|
|
}
|
|
});
|
|
|
|
test("engine http failures surface as safe engine errors, never raw stack traces", async () => {
|
|
const restore = stubEngine({ error: "invalid event kind" }, 400);
|
|
try {
|
|
await assert.rejects(
|
|
runV9CandidateScore({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
}),
|
|
(error: unknown) => error instanceof Error && error.message.includes("invalid event kind"),
|
|
);
|
|
} finally {
|
|
restore();
|
|
}
|
|
});
|
|
|
|
test("vedastro-validate propagates the engine minute confirmation signal into the combined gate input", async () => {
|
|
const restore = stubEngine({
|
|
success: true,
|
|
endpoint: "rectification_v5_vedastro_validate",
|
|
status: "pass",
|
|
passed: true,
|
|
can_confirm_exact_minute: true,
|
|
minute_sensitive_validation: { status: "pass", discriminated: true },
|
|
event_validation: {
|
|
status: "pass",
|
|
search_events_primary_supports_local_winner: true,
|
|
},
|
|
});
|
|
try {
|
|
const result = await runV9VedastroValidate({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
candidateTimes: ["04:50", "04:51"],
|
|
});
|
|
assert.equal(result.status, "passed");
|
|
assert.equal(result.canConfirmExactMinute, true);
|
|
assert.equal(result.searchEventsSupportsLocalWinner, true);
|
|
const merged = mergeVedastroValidateIntoReceipt(
|
|
{ ...DECISION_RECEIPT, gates: { exact_confirmation: { external_validation_status: "passed" } } },
|
|
result,
|
|
);
|
|
const exact = (merged.gates as { exact_confirmation: Record<string, unknown> }).exact_confirmation;
|
|
assert.equal(exact.external_validation_status, "passed");
|
|
assert.equal(
|
|
(exact.vedastro_event_validation as { search_events_primary_supports_local_winner: boolean })
|
|
.search_events_primary_supports_local_winner,
|
|
true,
|
|
);
|
|
assert.equal(merged.confirmation_allowed, false);
|
|
} finally {
|
|
restore();
|
|
}
|
|
});
|
|
|
|
test("vedastro-validate cannot pass the minute gate when the overall validation failed", async () => {
|
|
const restore = stubEngine({
|
|
status: "failed",
|
|
can_confirm_exact_minute: true,
|
|
minute_sensitive_validation: { status: "passed" },
|
|
event_validation: { search_events_primary_supports_local_winner: true },
|
|
});
|
|
try {
|
|
const result = await runV9VedastroValidate({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
candidateTimes: ["04:50", "04:51"],
|
|
});
|
|
assert.equal(result.status, "failed");
|
|
assert.equal(result.minuteSensitiveStatus, "failed");
|
|
assert.equal(result.canConfirmExactMinute, false);
|
|
const merged = mergeVedastroValidateIntoReceipt(
|
|
{ ...DECISION_RECEIPT, gates: { exact_confirmation: { external_validation_status: "not_evaluated" } } },
|
|
result,
|
|
);
|
|
const exact = (merged.gates as { exact_confirmation: Record<string, unknown> }).exact_confirmation;
|
|
assert.equal(exact.external_validation_status, "failed");
|
|
} finally {
|
|
restore();
|
|
}
|
|
});
|
|
|
|
test("vedastro-validate keeps safe timeout, HTTP, and invalid-response failure codes", async () => {
|
|
const input = {
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
candidateTimes: ["04:50", "04:51"] as const,
|
|
};
|
|
const previous = globalThis.fetch;
|
|
try {
|
|
globalThis.fetch = (async () => {
|
|
throw new DOMException("secret upstream detail", "TimeoutError");
|
|
}) as unknown as typeof fetch;
|
|
const timeout = await runV9VedastroValidate(input);
|
|
assert.equal(timeout.status, "failed");
|
|
assert.deepEqual(timeout.failure, { code: "timeout" });
|
|
assert.doesNotMatch(JSON.stringify(timeout.raw), /secret upstream detail/);
|
|
|
|
globalThis.fetch = (async () => ({
|
|
ok: false,
|
|
status: 503,
|
|
json: async () => ({ error: "private upstream URL and token" }),
|
|
})) as unknown as typeof fetch;
|
|
const requestFailure = await runV9VedastroValidate(input);
|
|
assert.equal(requestFailure.status, "failed");
|
|
assert.deepEqual(requestFailure.failure, { code: "engine_request_failed" });
|
|
assert.doesNotMatch(JSON.stringify(requestFailure.raw), /private upstream URL|token/);
|
|
|
|
globalThis.fetch = (async () => ({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => null,
|
|
})) as unknown as typeof fetch;
|
|
const invalid = await runV9VedastroValidate(input);
|
|
assert.equal(invalid.status, "failed");
|
|
assert.deepEqual(invalid.failure, { code: "engine_invalid_response" });
|
|
} finally {
|
|
globalThis.fetch = previous;
|
|
}
|
|
});
|
|
|
|
const VARGA_SPLIT_HASH = "04:45-05:15:04:47,04:51,04:53,04:59,05:00,05:06,05:08,05:13,05:15:varga.d9.04:47|04:51/05:00|05:06|04:59|04:53/05:08|05:13|05:15";
|
|
|
|
test("engineRequestBody drops varga split hashes and keeps short semantic keys", () => {
|
|
assert.equal(VARGA_SPLIT_HASH.length, 128);
|
|
const receipt = {
|
|
inference_state: {
|
|
answered_probes: [{
|
|
probe_id: "contrast:varga.d9.style",
|
|
semantic_key: "varga.d9.style",
|
|
candidate_split_hash: VARGA_SPLIT_HASH,
|
|
answer_class: "yes",
|
|
classified_from: "choice",
|
|
}],
|
|
},
|
|
};
|
|
const semantic = askedSemanticKeysForEngine(receipt, []);
|
|
assert.deepEqual(semantic, ["varga.d9.style"]);
|
|
assert.equal(semantic.includes(VARGA_SPLIT_HASH), false);
|
|
const body = engineRequestBody({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
askedProbeKeys: [VARGA_SPLIT_HASH, "varga.d9.style", "k".repeat(201)],
|
|
});
|
|
const keys = body.asked_probe_keys as string[];
|
|
assert.ok(Array.isArray(keys));
|
|
assert.equal(keys.includes(VARGA_SPLIT_HASH), false);
|
|
assert.equal(keys.some((key) => key.includes(":varga.")), false);
|
|
assert.ok(keys.every((key) => key.length <= 120));
|
|
assert.deepEqual(sanitizeAskedProbeKeysForEngine([VARGA_SPLIT_HASH, "varga.d9.style"]), ["varga.d9.style"]);
|
|
});
|
|
|
|
test("engineRequestBody forwards a slow-compare column_times subset", () => {
|
|
const body = engineRequestBody({
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
columnTimes: ["14:46", "14:47", "14:46", "not-a-clock"],
|
|
});
|
|
assert.deepEqual(body.column_times, ["14:46", "14:47"]);
|
|
});
|
|
|
|
const SCORE_INPUT = {
|
|
baselineBirthSnapshot: SNAPSHOT,
|
|
candidateRange: RANGE,
|
|
events: toEngineEvents(EVIDENCE),
|
|
};
|
|
|
|
function stubEngineSequence(
|
|
responses: Array<{
|
|
status?: number;
|
|
body?: unknown;
|
|
retryAfter?: string;
|
|
throwError?: Error;
|
|
}>,
|
|
) {
|
|
const previous = globalThis.fetch;
|
|
let index = 0;
|
|
globalThis.fetch = (async () => {
|
|
const next = responses[index] ?? responses.at(-1);
|
|
index += 1;
|
|
if (!next) throw new Error("no stubbed engine response");
|
|
if (next.throwError) throw next.throwError;
|
|
const status = next.status ?? 200;
|
|
return {
|
|
ok: status >= 200 && status < 300,
|
|
status,
|
|
headers: new Headers(next.retryAfter ? { "Retry-After": next.retryAfter } : {}),
|
|
json: async () => next.body,
|
|
};
|
|
}) as unknown as typeof fetch;
|
|
return {
|
|
calls: () => index,
|
|
restore() {
|
|
globalThis.fetch = previous;
|
|
},
|
|
};
|
|
}
|
|
|
|
function captureWarnings<T>(run: () => Promise<T>): Promise<{ result: PromiseSettledResult<T>; warnings: unknown[] }> {
|
|
const warnings: unknown[] = [];
|
|
const original = console.warn;
|
|
console.warn = (...args: unknown[]) => {
|
|
warnings.push(args);
|
|
};
|
|
return run()
|
|
.then((value) => ({ result: { status: "fulfilled" as const, value }, warnings }))
|
|
.catch((reason: unknown) => ({ result: { status: "rejected" as const, reason }, warnings }))
|
|
.finally(() => {
|
|
console.warn = original;
|
|
});
|
|
}
|
|
|
|
test("busy backoff is a named cap of two waits and six seconds", () => {
|
|
assert.equal(RECTIFICATION_ENGINE_BUSY_RETRY_LIMIT, 2);
|
|
assert.equal(RECTIFICATION_ENGINE_BUSY_BACKOFF_BUDGET_MS, 6_000);
|
|
assert.equal(nextEngineBusyBackoffMs({
|
|
retryAfterSeconds: 2,
|
|
retriesUsed: 0,
|
|
backoffUsedMs: 0,
|
|
}), 2_000);
|
|
assert.equal(nextEngineBusyBackoffMs({
|
|
retryAfterSeconds: 2,
|
|
retriesUsed: 1,
|
|
backoffUsedMs: 2_000,
|
|
}), 2_000);
|
|
assert.equal(nextEngineBusyBackoffMs({
|
|
retryAfterSeconds: 2,
|
|
retriesUsed: 2,
|
|
backoffUsedMs: 4_000,
|
|
}), null);
|
|
assert.equal(nextEngineBusyBackoffMs({
|
|
retryAfterSeconds: 10,
|
|
retriesUsed: 0,
|
|
backoffUsedMs: 0,
|
|
}), 6_000);
|
|
assert.equal(nextEngineBusyBackoffMs({
|
|
retryAfterSeconds: 2,
|
|
retriesUsed: 1,
|
|
backoffUsedMs: 6_000,
|
|
}), null);
|
|
const client = readFileSync(
|
|
new URL("../src/lib/rectification-agentic/v9/engine-client.ts", import.meta.url),
|
|
"utf8",
|
|
);
|
|
assert.match(client, /RECTIFICATION_ENGINE_BUSY_RETRY_LIMIT = 2/);
|
|
assert.match(client, /RECTIFICATION_ENGINE_BUSY_BACKOFF_BUDGET_MS = 6_000/);
|
|
assert.match(client, /retriesUsed >= RECTIFICATION_ENGINE_BUSY_RETRY_LIMIT/);
|
|
});
|
|
|
|
test("score classifies 429, 500, timeout, and bad JSON separately and only 429 retries", async () => {
|
|
const waits: number[] = [];
|
|
setEngineBusyWaitForTests(async (ms) => {
|
|
waits.push(ms);
|
|
});
|
|
try {
|
|
const busyThenOk = stubEngineSequence([
|
|
{ status: 429, body: { error: "ERR_COMPUTE_BUSY" }, retryAfter: "2" },
|
|
{ status: 200, body: ENGINE_SCORE_RESPONSE_V2 },
|
|
]);
|
|
const busyOk = await captureWarnings(() => runV9CandidateScore(SCORE_INPUT));
|
|
busyThenOk.restore();
|
|
assert.equal(busyOk.result.status, "fulfilled");
|
|
if (busyOk.result.status === "fulfilled") {
|
|
assert.equal(busyOk.result.value.representativeTime, "04:50");
|
|
}
|
|
assert.equal(waits.length, 1);
|
|
assert.equal(waits[0], 2_000);
|
|
assert.equal(busyThenOk.calls(), 2);
|
|
const busyLog = JSON.stringify(busyOk.warnings[0]);
|
|
assert.match(busyLog, /rectification_engine_failed/);
|
|
assert.match(busyLog, /"busy"/);
|
|
assert.match(busyLog, /"httpStatus":429/);
|
|
assert.doesNotMatch(busyLog, /1997-08-08|latitude|token|JWT/);
|
|
|
|
waits.length = 0;
|
|
const http = stubEngineSequence([{ status: 500, body: { error: "boom" } }]);
|
|
const httpResult = await captureWarnings(() => runV9CandidateScore(SCORE_INPUT));
|
|
http.restore();
|
|
assert.equal(httpResult.result.status, "rejected");
|
|
if (httpResult.result.status === "rejected") {
|
|
const error = httpResult.result.reason;
|
|
assert.ok(error instanceof RectificationEngineError);
|
|
assert.equal(error.kind, "http_error");
|
|
assert.equal(error.httpStatus, 500);
|
|
}
|
|
assert.equal(waits.length, 0);
|
|
assert.equal(http.calls(), 1);
|
|
assert.match(JSON.stringify(httpResult.warnings[0]), /"http_error"/);
|
|
|
|
const timeout = stubEngineSequence([{
|
|
throwError: Object.assign(new Error("aborted"), { name: "TimeoutError" }),
|
|
}]);
|
|
const timeoutResult = await captureWarnings(() => runV9CandidateScore(SCORE_INPUT));
|
|
timeout.restore();
|
|
assert.equal(timeoutResult.result.status, "rejected");
|
|
if (timeoutResult.result.status === "rejected") {
|
|
const error = timeoutResult.result.reason;
|
|
assert.ok(error instanceof RectificationEngineError);
|
|
assert.equal(error.kind, "timeout");
|
|
}
|
|
assert.equal(waits.length, 0);
|
|
assert.match(JSON.stringify(timeoutResult.warnings[0]), /"timeout"/);
|
|
|
|
const bad = stubEngineSequence([{ status: 200, body: null }]);
|
|
const badResult = await captureWarnings(() => runV9CandidateScore(SCORE_INPUT));
|
|
bad.restore();
|
|
assert.equal(badResult.result.status, "rejected");
|
|
if (badResult.result.status === "rejected") {
|
|
const error = badResult.result.reason;
|
|
assert.ok(error instanceof RectificationEngineError);
|
|
assert.equal(error.kind, "bad_payload");
|
|
assert.equal(error.code, "engine_invalid_response");
|
|
}
|
|
assert.equal(waits.length, 0);
|
|
assert.match(JSON.stringify(badResult.warnings[0]), /"bad_payload"/);
|
|
} finally {
|
|
setEngineBusyWaitForTests(null);
|
|
}
|
|
});
|
|
|
|
test("busy retries that still fail keep the busy kind", async () => {
|
|
const waits: number[] = [];
|
|
setEngineBusyWaitForTests(async (ms) => {
|
|
waits.push(ms);
|
|
});
|
|
const stub = stubEngineSequence([
|
|
{ status: 429, body: { error: "ERR_COMPUTE_BUSY" }, retryAfter: "2" },
|
|
{ status: 429, body: { error: "ERR_COMPUTE_BUSY" }, retryAfter: "2" },
|
|
{ status: 429, body: { error: "ERR_COMPUTE_BUSY" }, retryAfter: "2" },
|
|
]);
|
|
try {
|
|
const result = await captureWarnings(() => runV9CandidateScore(SCORE_INPUT));
|
|
assert.equal(result.result.status, "rejected");
|
|
if (result.result.status === "rejected") {
|
|
const error = result.result.reason;
|
|
assert.ok(error instanceof RectificationEngineError);
|
|
assert.equal(error.kind, "busy");
|
|
}
|
|
assert.equal(waits.length, 2);
|
|
assert.equal(stub.calls(), 3);
|
|
} finally {
|
|
stub.restore();
|
|
setEngineBusyWaitForTests(null);
|
|
}
|
|
});
|
|
|
|
test("400 too_many_candidates is a bad request and is not retried", async () => {
|
|
const waits: number[] = [];
|
|
setEngineBusyWaitForTests(async (ms) => {
|
|
waits.push(ms);
|
|
});
|
|
const stub = stubEngineSequence([{
|
|
status: 400,
|
|
body: { error: "too_many_candidates: limit=241 count=1440", error_code: "ERR_BAD_REQUEST" },
|
|
}]);
|
|
try {
|
|
const result = await captureWarnings(() => runV9CandidateScore(SCORE_INPUT));
|
|
assert.equal(result.result.status, "rejected");
|
|
if (result.result.status === "rejected") {
|
|
const error = result.result.reason;
|
|
assert.ok(error instanceof RectificationEngineError);
|
|
assert.equal(error.kind, "bad_payload");
|
|
assert.equal(error.code, "too_many_candidates");
|
|
assert.equal(error.httpStatus, 400);
|
|
}
|
|
assert.equal(waits.length, 0);
|
|
assert.equal(stub.calls(), 1);
|
|
} finally {
|
|
stub.restore();
|
|
setEngineBusyWaitForTests(null);
|
|
}
|
|
});
|
|
|