research(rectification): offline R1/R2/R3 — answer-flip tolerance, V1/V2 rerun, dasha shift arithmetic

- R1: flipping 1 answer keeps truth in range 98-100% but cuts head hit by
  a third or more; 2 flips squeeze truth out in 7-10% of ±30/±60 replays
  (two flips = 8 points = SEPARATION_LEAD).
- R2: weights do apply (research scorer == production at V0); V1/V2 are
  identity at ±30/±60 by construction and leave six-question metrics
  unchanged at ±10 -> no_benefit (measured). Supplementary V1n does not
  pass the gate.
- R3: boundary shift is ~3.8 days/minute (1.3-5.9), not 1.1; the 45-day
  gate is ~8-34 minutes. The _representative_pairs hypothesis is refuted
  (all-pairs adds no dated probes); the bottleneck is monthly evaluation.
  New finding recorded as BUG-1048 (investigating): _boundary_windows
  year-straddle exemption and positional zip misalignment bypass the gate.
- Dated errata appended (no deletions) to the 09-14/09-16 briefs and
  research docs; README board row -> 待验收. No production code, scoring,
  thresholds, gates or Skill changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
This commit is contained in:
Jesse_Chen
2026-09-26 14:47:48 +08:00
co-authored by Claude Opus 5.5
parent 7203d94e9c
commit 0f5442cea2
19 changed files with 19012 additions and 1 deletions
+77
View File
@@ -0,0 +1,77 @@
"""Pure-helper regression tests for the 2026-09-26 offline rectification research.
Fictional inputs only. These cover the research helpers, not production code.
"""
from __future__ import annotations
from datetime import date, timedelta
from scripts.research.offline_research_20260926_lib import (
NAKSHATRA_SPAN,
answerable_indices,
exhaustive_flip_sets,
flipped_answers,
nakshatra_lord,
sample_flip_sets,
shift_days_per_minute,
zip_misaligned,
)
from scripts.research.representative_pairs_probe import strict_boundary_windows
from scripts.research.varga_sensitivity_rerun import V1N_FACTORS, factor_table
MEAN_MOON_DEG_PER_DAY = 13.176
def test_shift_per_minute_is_about_one_year_per_degree_not_122_days() -> None:
venus = shift_days_per_minute(NAKSHATRA_SPAN * 1 + 1.0, MEAN_MOON_DEG_PER_DAY)
sun = shift_days_per_minute(NAKSHATRA_SPAN * 2 + 1.0, MEAN_MOON_DEG_PER_DAY)
assert nakshatra_lord(NAKSHATRA_SPAN * 1 + 1.0) == "Venus"
assert nakshatra_lord(NAKSHATRA_SPAN * 2 + 1.0) == "Sun"
assert 4.9 < venus < 5.1
assert 1.45 < sun < 1.55
# The 2026-09-14 figure (1.1 days) is below the slowest lord at mean speed.
assert sun > 1.1
def test_flip_sets_are_seeded_and_only_touch_answered_questions() -> None:
answers = ["yes", None, "no", "yes", None, "no"]
assert answerable_indices(answers) == [0, 2, 3, 5]
first = sample_flip_sets(answers, 2, 5, case_id="fictional", radius=10)
again = sample_flip_sets(answers, 2, 5, case_id="fictional", radius=10)
assert first == again
assert all(len(item) == 2 and set(item) <= {0, 2, 3, 5} for item in first)
assert sample_flip_sets(answers, 5, 3, case_id="fictional", radius=10) == []
assert len(exhaustive_flip_sets(answers, 2)) == 6
assert flipped_answers(answers, (0, 2)) == ["no", None, "yes", "yes", None, "no"]
# Unequal spacing, like real dasha periods (equal spacing would make an
# off-by-one zip look like a constant shift).
OFFSETS = (0, 300, 1000, 1200, 2500, 2600)
def test_zip_misalignment_detects_off_by_one_lists() -> None:
base = [date(2000, 1, 10) + timedelta(days=days) for days in OFFSETS]
shifted = [item + timedelta(days=20) for item in base]
assert not zip_misaligned(base, shifted)
assert zip_misaligned(base[1:], shifted)
def test_strict_gate_drops_year_straddle_and_realigns() -> None:
left = [date(1999, 12, 28), date(2003, 5, 1)]
right = [date(2000, 1, 4), date(2003, 5, 8)]
assert strict_boundary_windows(left, right, min_days=45) == []
base = [date(2000, 3, 1) + timedelta(days=days) for days in OFFSETS]
later = [item + timedelta(days=10) for item in base]
assert strict_boundary_windows(base, later[1:], min_days=45) == []
far = [item + timedelta(days=60) for item in base]
assert len(strict_boundary_windows(base, far, min_days=45)) == 2 * len(OFFSETS)
def test_v1_saturates_on_wide_windows_and_v1n_keeps_mean_one() -> None:
table = factor_table()
assert set(table["30"].values()) == {1.0}
assert set(table["60"].values()) == {1.0}
assert table["10"]["D2"] < 1.0
assert abs(sum(V1N_FACTORS.values()) / len(V1N_FACTORS) - 1.0) < 1e-4
assert V1N_FACTORS["D30"] > V1N_FACTORS["D2"]