报告创建请求、校正入口和同名类型改从 contracts 推导;同义的对象判断与 sha256 改走共享函数。调用处用 import 别名保留原名,使 diff 低于 1500 行。行为、哈希和状态字符串不变。 Co-Authored-By: Grok (agent loop)
52 lines
2.6 KiB
TypeScript
52 lines
2.6 KiB
TypeScript
import { parseAscendantSignature, type SegmentMinute, type AscendantSignature } from "../core/segment-summary.ts";
|
|
import { asRecord as record } from "@/lib/guards";
|
|
|
|
export function segmentCandidateFields(row: Record<string, unknown> | null): { signature?: AscendantSignature; rawScore?: number } {
|
|
const signature = parseAscendantSignature(row?.signature);
|
|
const rawScore = row?.raw_score;
|
|
return {
|
|
...(signature ? { signature } : {}),
|
|
...(typeof rawScore === "number" && Number.isFinite(rawScore) ? { rawScore } : {}),
|
|
};
|
|
}
|
|
|
|
/** The frozen engine returns scores/features separately from public decisions. */
|
|
export function candidateSegmentMetadata(data: Record<string, unknown>): {
|
|
byTime: ReadonlyMap<string, { signature?: AscendantSignature; rawScore?: number }>;
|
|
minutes: readonly SegmentMinute[];
|
|
} {
|
|
const scores = new Map<string, number>();
|
|
for (const item of Array.isArray(data.candidate_scores) ? data.candidate_scores : []) {
|
|
const row = record(item);
|
|
if (typeof row?.time === "string" && typeof row.score === "number" && Number.isFinite(row.score)) scores.set(row.time, row.score);
|
|
}
|
|
const snapshot = record(data.candidate_feature_snapshot);
|
|
const byTime = new Map<string, { signature?: AscendantSignature; rawScore?: number }>();
|
|
const minutes: SegmentMinute[] = [];
|
|
for (const item of Array.isArray(snapshot?.features) ? snapshot.features : []) {
|
|
const row = record(item);
|
|
if (!row || typeof row.time !== "string") continue;
|
|
const vargas = record(row.varga_ascendants);
|
|
const signature = parseAscendantSignature({ D1: row.ascendant_sign_index, D9: vargas?.D9, D10: vargas?.D10 });
|
|
const rawScore = scores.get(row.time);
|
|
byTime.set(row.time, { ...(signature ? { signature } : {}), ...(rawScore !== undefined ? { rawScore } : {}) });
|
|
if (signature) minutes.push({ offset: minutes.length * 2, time: row.time, signs: signature });
|
|
}
|
|
return { byTime, minutes };
|
|
}
|
|
|
|
export function parseSegmentMinutes(value: unknown): readonly SegmentMinute[] | null {
|
|
if (!Array.isArray(value) || !value.length) return null;
|
|
const minutes: SegmentMinute[] = [];
|
|
for (const item of value) {
|
|
const row = record(item);
|
|
const signs = parseAscendantSignature(row?.signs);
|
|
if (!row || typeof row.offset !== "number" || !Number.isInteger(row.offset)
|
|
|| typeof row.time !== "string" || !/^(?:[01]\d|2[0-3]):[0-5]\d$/.test(row.time) || !signs) return null;
|
|
minutes.push({ offset: row.offset, time: row.time, signs,
|
|
...(typeof row.date === "string" && /^\d{4}-\d{2}-\d{2}$/.test(row.date) ? { date: row.date } : {}),
|
|
});
|
|
}
|
|
return minutes;
|
|
}
|