Speed up rectification scoring without changing results.

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)
This commit is contained in:
jesse-ux
2026-10-14 02:06:14 +08:00
co-authored by Grok (agent loop)
parent 74384be024
commit 8e0503f9c0
27 changed files with 45977 additions and 92 deletions
@@ -330,17 +330,27 @@ async function readEngineJsonTimed(
const elapsedMs = Date.now() - started;
const data = await response.json().catch(() => null);
if (!response.ok) {
const kind: EngineCallFailureKind = response.status === 429 ? "busy" : "http_error";
const errorText = data && typeof data === "object"
? String((data as Record<string, unknown>).error ?? (data as Record<string, unknown>).message ?? "")
: "";
const errorCode = data && typeof data === "object"
? String((data as Record<string, unknown>).error_code ?? "")
: "";
const tooManyCandidates = response.status === 400
&& (errorCode === "too_many_candidates" || errorText.includes("too_many_candidates"));
const kind: EngineCallFailureKind = response.status === 429
? "busy"
: tooManyCandidates
? "bad_payload"
: "http_error";
logEngineCallFailure({
path,
kind,
elapsedMs,
httpStatus: response.status,
});
const message = data && typeof data === "object"
? String((data as Record<string, unknown>).error ?? (data as Record<string, unknown>).message ?? `Jyotish API ${response.status}`)
: `Jyotish API ${response.status}`;
throw new RectificationEngineError(persistenceCodeForKind(kind), message, {
const message = errorText || `Jyotish API ${response.status}`;
throw new RectificationEngineError(tooManyCandidates ? "too_many_candidates" : persistenceCodeForKind(kind), message, {
kind,
httpStatus: response.status,
retryAfterSeconds: retryAfterFromHeaders(response.headers) ?? undefined,
@@ -105,8 +105,11 @@ const diagnosticReport = JSON.parse(readFileSync(
// 原值(8):docs/research/sealed_holdout_rerun_upstream_sync6_2026_10_07.json
// 新值(8):docs/research/sealed_holdout_rerun_ephemeris_nutation_2026_10_08.json
// 原因(8):章动修正与 scoring-12 改了冻结身份(ERR-110,BUG-1275)。影子重跑不是发布指标,确认门行为与阈值不变。
// 原值(9):docs/research/sealed_holdout_rerun_ephemeris_nutation_2026_10_08.json
// 新值(9):docs/research/sealed_holdout_rerun_scoring_perf_2026_10_13.json
// 原因(9):BUG-1323~1325 改了冻结身份文件,输出不变,ERR-110 另存新记录。确认门行为与阈值不变。
const currentTreeRerun = JSON.parse(readFileSync(
new URL("../../docs/research/sealed_holdout_rerun_ephemeris_nutation_2026_10_08.json", import.meta.url),
new URL("../../docs/research/sealed_holdout_rerun_scoring_perf_2026_10_13.json", import.meta.url),
"utf8",
)) as {
frozen_record: { implementation_sha256: string };
@@ -252,9 +255,10 @@ test("sealed holdout aggregates match the v3 report and still block confirmation
// 原值(5):…upstream_sync5_2026_10_02.json / 新值(5):…astrologer_rulings_batch1_2026_10_03.json / 原因(5):TASK-astrologer-rulings-batch1 重新冻结(同上)。
// 原值(6):…astrologer_rulings_batch1_2026_10_03.json / 新值(6):…upstream_sync6_2026_10_07.json / 原因(6):sync6 的 D5/D11 与 scoring-11 重新冻结(ERR-110,BUG-1260)。
// 原值(7):…upstream_sync6_2026_10_07.json / 新值(7):…ephemeris_nutation_2026_10_08.json / 原因(7):章动修正与 scoring-12 重新冻结(ERR-110,BUG-1275)。
// 原值(8):…ephemeris_nutation_2026_10_08.json / 新值(8):…scoring_perf_2026_10_13.json / 原因(8):BUG-1323~1325 改了冻结身份文件,输出不变,ERR-110 另存新记录。
assert.equal(
productHoldout.current_tree_scorer.source_report,
"docs/research/sealed_holdout_rerun_ephemeris_nutation_2026_10_08.json",
"docs/research/sealed_holdout_rerun_scoring_perf_2026_10_13.json",
);
assert.equal(currentTreeRerun.official_valid_independent_blind, false);
assert.equal(productHoldout.current_tree_scorer.matches_metrics_scorer, false);
@@ -712,3 +712,30 @@ test("busy retries that still fail keep the busy kind", async () => {
}
});
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);
}
});