fix(rectification): scan chart boundaries in the user's ayanamsa (BUG-1307, BUG-1309)
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
* Opening scan for the nearest chart change. Failures keep the original
|
||||
* two-sentence opening. Question order stays on its own switch.
|
||||
*/
|
||||
import { resolveAyanamsa } from "../ayanamsa.ts";
|
||||
import { requestCandidateIntervals, type DatedCandidateRange } from "./core/candidate-window.ts";
|
||||
import {
|
||||
CHART_LAYERS,
|
||||
@@ -25,18 +26,22 @@ export function openingFactsFromBoundaryPayload(
|
||||
if (typeof distance !== "number" || !Number.isInteger(distance) || distance < 0 || distance > 720) {
|
||||
return { kind: "unavailable" };
|
||||
}
|
||||
const layers = Array.isArray(row.layers)
|
||||
? row.layers.flatMap((item) => {
|
||||
const layer = item && typeof item === "object" ? (item as { layer?: unknown }).layer : null;
|
||||
return typeof layer === "string" && LAYERS.has(layer) ? [layer] : [];
|
||||
})
|
||||
: [];
|
||||
const rawLayers = Array.isArray(row.layers) ? row.layers : [];
|
||||
const layers = rawLayers.flatMap((item) => {
|
||||
const layer = item && typeof item === "object" ? (item as { layer?: unknown }).layer : null;
|
||||
return typeof layer === "string" && LAYERS.has(layer) ? [layer] : [];
|
||||
});
|
||||
const d1Changed = rawLayers.some((item) => {
|
||||
const layer = item && typeof item === "object" ? (item as { layer?: unknown }).layer : null;
|
||||
return layer === "d1";
|
||||
});
|
||||
const topics = topicsForLayers(layers);
|
||||
const at = (payload as { at?: unknown }).at;
|
||||
return {
|
||||
kind: "near",
|
||||
distanceMinutes: distance,
|
||||
topics,
|
||||
d1Changed,
|
||||
...(typeof at === "string" ? { at: at.slice(0, 5) } : {}),
|
||||
askFirst: askFirst && topics.length > 0 && distance <= NEAREST_BOUNDARY_PROBE_THRESHOLD_MINUTES,
|
||||
};
|
||||
@@ -83,6 +88,7 @@ export async function fetchOpeningBoundary(input: {
|
||||
target_charts: ["D1"],
|
||||
boundary_charts: BOUNDARY_CHARTS,
|
||||
recorded_time: recorded,
|
||||
ayanamsa: resolveAyanamsa(input.snapshot),
|
||||
}),
|
||||
});
|
||||
if (!response.ok) return { kind: "unavailable" };
|
||||
|
||||
@@ -78,12 +78,15 @@ export function withFirstDatedCollectInvite(stem: string): string {
|
||||
* right below it, so the body does not list them again.
|
||||
*
|
||||
* BUG-1307: when the server already measured a chart change, one extra sentence
|
||||
* sits between those two. The 3-sentence cap is unchanged.
|
||||
* sits between those two. The 3-sentence cap is unchanged. A natal-ascendant
|
||||
* change is named in that same sentence, before the other life topics.
|
||||
*/
|
||||
export type OpeningBoundaryFacts = Readonly<{
|
||||
kind: "period_only" | "unavailable" | "stable" | "near";
|
||||
distanceMinutes?: number | null;
|
||||
topics?: readonly string[];
|
||||
/** Natal ascendant (layer d1) changed. Not a spoken life topic. */
|
||||
d1Changed?: boolean;
|
||||
/** Only when the question-order switch is on and this boundary is inside it. */
|
||||
askFirst?: boolean;
|
||||
}>;
|
||||
@@ -100,9 +103,10 @@ export function openingBoundarySentence(facts: OpeningBoundaryFacts | null | und
|
||||
if ((OPENING_BOUNDARY_TOPICS as readonly string[]).includes(topic) && !topics.includes(topic)) topics.push(topic);
|
||||
}
|
||||
const place = distance < 1 ? "不到 1 分钟" : `差 ${distance} 分钟`;
|
||||
const natal = facts.d1Changed ? ",这里连本命上升都换" : "";
|
||||
const sides = topics.length ? `,两边不一样的是${topics.join("、")}` : "";
|
||||
const tail = facts.askFirst && topics.length ? ",接下来先问这几件。" : "。";
|
||||
return `你填的时间离最近会换的地方${place}${sides}${tail}`;
|
||||
return `你填的时间离最近会换的地方${place}${natal}${sides}${tail}`;
|
||||
}
|
||||
|
||||
export function openingSpokenBody(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { resolveAyanamsa } from "../../ayanamsa.ts";
|
||||
import { requestCandidateIntervals, type DatedCandidateRange } from "../core/candidate-window.ts";
|
||||
import type { SegmentMinute, TargetChart } from "../core/segment-summary.ts";
|
||||
import { parseSegmentMinutes } from "./segment-contract.ts";
|
||||
@@ -15,7 +16,8 @@ export async function scanCaseSegments(input: {
|
||||
method: "POST", headers: { "Content-Type": "application/json" }, signal: AbortSignal.timeout(60000),
|
||||
body: JSON.stringify({ start_at: interval.start_at, end_at: interval.end_at,
|
||||
latitude: input.snapshot.latitude, longitude: input.snapshot.longitude,
|
||||
timezone_offset: input.snapshot.timezone_offset, target_charts: input.targets }),
|
||||
timezone_offset: input.snapshot.timezone_offset, target_charts: input.targets,
|
||||
ayanamsa: resolveAyanamsa(input.snapshot) }),
|
||||
});
|
||||
if (!response.ok) throw new Error("segment_scan_unavailable");
|
||||
const data = await response.json();
|
||||
@@ -23,7 +25,7 @@ export async function scanCaseSegments(input: {
|
||||
const start = Date.parse(`${interval.start_at}:00Z`);
|
||||
const expectedWidth = (Date.parse(`${interval.end_at}:00Z`) - start) / 60000 + 1;
|
||||
if (!minutes || data.start_at !== interval.start_at || data.end_at !== interval.end_at
|
||||
|| data.ayanamsa !== "raman" || data.node_mode !== "mean" || data.step_minutes !== 1
|
||||
|| data.ayanamsa !== resolveAyanamsa(input.snapshot) || data.node_mode !== "mean" || data.step_minutes !== 1
|
||||
|| data.window_minutes !== expectedWidth || minutes.length !== expectedWidth
|
||||
|| minutes.some((minute, index) => minute.offset !== index
|
||||
|| `${minute.date}T${minute.time}` !== new Date(start + index * 60000).toISOString().slice(0, 16)
|
||||
|
||||
Reference in New Issue
Block a user