fix(rectification): anchor candidate windows to civil dates across midnight
Independent Staging Quality Gate / validate (push) Successful in 13m27s
Independent Staging Quality Gate / publish (push) Failing after 1h0m1s

Carry explicit local date intervals instead of inferring the day from clock
order. Cluster width, delivery, adoption, and reports keep the actual civil
date; adopted date is stored separately from the reported birth_date.

Algorithm identity is scoring-9 / spec-v5. Scoring weights, confirmation
thresholds, and Skill version are unchanged. Isolated Linux final-3 gates
passed; four pre-existing Python failures remain. This is not a production
release.
This commit is contained in:
jesse-ux
2026-09-21 02:55:00 +08:00
parent 3be740f84d
commit b85c4a686a
115 changed files with 106484 additions and 315 deletions
+61 -13
View File
@@ -98,8 +98,12 @@ def _features(built: dict[str, Any]) -> list[dict[str, Any]]:
continue
time = _feature_time(feature)
if time:
rows.append(feature)
rows.sort(key=lambda item: _clock(str(_feature_time(item))))
if "candidate_intervals" in built:
position = {key: context[key] for key in ("candidate_date", "window_index", "window_offset_minutes", "segment_index")}
rows.append({**feature, **position})
else:
rows.append(feature)
rows.sort(key=lambda item: item.get("window_index", _clock(str(_feature_time(item)))))
return rows
@@ -224,27 +228,43 @@ def window_scan(
counts: dict[str, set[int]] = {layer: set() for layer in _LAYER_LABEL}
transitions: list[dict[str, Any]] = []
previous: dict[str, int | None] | None = None
for feature in _features(built):
previous_segment: int | None = None
features = _features(built)
if start_minute is not None and end_minute is not None:
features = [feature for feature in features
if start_minute <= _clock(str(_feature_time(feature))) <= end_minute]
segment_bounds: dict[int, tuple[str, str]] = {}
for feature in features:
if "window_index" in feature:
segment = feature["segment_index"]
stamp = f'{feature["candidate_date"]}T{_feature_time(feature)}'
segment_bounds[segment] = (segment_bounds.get(segment, (stamp, stamp))[0], stamp)
for feature in features:
time = _feature_time(feature)
if time and start_minute is not None and end_minute is not None:
clock = _clock(time)
if clock < start_minute or clock > end_minute:
continue
current = {layer: _scan_layer_value(feature, layer) for layer in _LAYER_LABEL}
for layer, bucket in counts.items():
value = current[layer]
if isinstance(value, int):
bucket.add(value)
if previous and time:
dated = "window_index" in feature
segment_start = dated and feature["segment_index"] != previous_segment
if (previous or segment_start) and time:
for layer, label in _LAYER_LABEL.items():
before = previous[layer]
after = current[layer]
if isinstance(before, int) and isinstance(after, int) and before != after:
before = after if segment_start else previous[layer]
if isinstance(before, int) and isinstance(after, int) and (before != after or segment_start):
row = {
"layer": layer,
"at": time,
"user_meaning": f"{label} 在 {time} 发生变化",
}
if dated:
row.update({key: feature[key] for key in ("candidate_date", "window_index", "window_offset_minutes", "segment_index")})
start_at, end_at = segment_bounds[feature["segment_index"]]
row.update(segment_start_at=start_at, segment_end_at=end_at)
if segment_start:
row["segment_start"] = True
row["user_meaning"] = f"{label} 在这一段起点的状态"
from_sign = _sign_name(before)
to_sign = _sign_name(after)
if from_sign and to_sign:
@@ -252,6 +272,7 @@ def window_scan(
row["to_sign"] = to_sign
transitions.append(row)
previous = current
previous_segment = feature.get("segment_index")
payload: dict[str, Any] = {
"scanned": True,
"confirmation_allowed": False,
@@ -472,8 +493,10 @@ def lagna_contrast(built: dict[str, Any]) -> dict[str, Any] | None:
index = feature.get("ascendant_sign_index")
if not time or not isinstance(index, int) or index < 0 or index > 11:
continue
if current and current["d1_lagna_index"] == index:
if current and current["d1_lagna_index"] == index and current.get("segment_index") == feature.get("segment_index"):
current["end"] = time
if "candidate_date" in feature:
current["end_at"] = f'{feature["candidate_date"]}T{time}'
continue
if current:
intervals.append(current)
@@ -482,6 +505,8 @@ def lagna_contrast(built: dict[str, Any]) -> dict[str, Any] | None:
"start": time,
"end": time,
"d1_lagna_index": index,
**({"segment_index": feature["segment_index"], "start_at": f'{feature["candidate_date"]}T{time}',
"end_at": f'{feature["candidate_date"]}T{time}'} if "candidate_date" in feature else {}),
"lagna": sign,
"lords": {
"l1": _house_lord_zh(index, 1),
@@ -495,11 +520,16 @@ def lagna_contrast(built: dict[str, Any]) -> dict[str, Any] | None:
if len(intervals) < 2:
return None
left, right = intervals[0], intervals[1]
def interval_label(interval: dict[str, Any]) -> str:
if "start_at" in interval:
return f"{interval['start_at'].replace('T', ' ')}–{interval['end_at'].replace('T', ' ')}"
return f"{interval['start']}-{interval['end']}"
return {
"intervals": intervals[:3],
"user_meaning": (
f"窗口里出现两段本命上升:{left['start']}-{left['end']} 为{left['lagna']},"
f"{right['start']}-{right['end']} 为{right['lagna']}。"
f"窗口里出现两段本命上升:{interval_label(left)} 为{left['lagna']},"
f"{interval_label(right)} 为{right['lagna']}。"
"可并列 D9/D10 类型表作校时方法,不是命运承诺,也不能确认唯一分钟。"
),
"unique_minute_claim": False,
@@ -618,6 +648,24 @@ def cluster_scan(
width_minutes: int | None = None,
) -> dict[str, Any]:
"""Scan only the indistinguishable candidate cluster, not the full declared range."""
if "candidate_intervals" in built:
wanted = {str(value)[:5] for value in [*candidate_times, representative_time] if value}
features = _features(built)
selected: dict[int, list[int]] = {}
for feature in features:
if _feature_time(feature) in wanted:
selected.setdefault(feature["segment_index"], []).append(feature["window_offset_minutes"])
if not selected:
return window_scan(built)
bounds = {segment: (min(points), max(points)) for segment, points in selected.items()}
if len(bounds) == 1:
segment, (lo, hi) = next(iter(bounds.items()))
extra = max(int(width_minutes or 0) - (hi - lo + 1), 0)
bounds[segment] = (lo - extra // 2, hi + extra - extra // 2)
contexts = [row for row in built.get("static_contexts") or []
if row.get("segment_index") in bounds
and bounds[row["segment_index"]][0] <= row["window_offset_minutes"] <= bounds[row["segment_index"]][1]]
return window_scan({**built, "static_contexts": contexts})
clocks: list[int] = []
for raw in [*candidate_times, representative_time]:
if isinstance(raw, str) and len(raw) >= 5: