Spec: Rectification Tooling¶
Status |
Draft — exploration ( |
Created |
2026-07-10 |
Owner |
Kate |
Type |
Spec-Driven Development (SDD) design doc |
Theory |
1. Summary¶
Build a computational birth-time rectifier: given a chart with unknown/ uncertain time plus a set of dated life events (and optionally temperament notes), return a calibrated posterior distribution over birth time, with sect as its robust coarse marginal, and a per-event, per-technique explanation of why.
The model is a hierarchical Bayesian grid posterior with a simulation-based forward model (the astrology engine); see the theory doc. The build is incremental and validation-first: the smallest honest milestone is a sect classifier benchmarked on the AA-rated notables, which proves signal exists before any calibration machinery is written.
2. Goals / Non-Goals¶
Goals¶
G1 — A
RectificationResultposterior over birth time, with a sect marginal, credible intervals, and itemised per-event/per-technique contributions (explainable by construction).G2 — Reuse existing engines as the forward model (ZR, firdaria, profections, directions, returns, transits/search, dignities) — no re-derivation.
G3 — A fast candidate re-cast (planets once; angles-only per candidate).
G4 — A validation harness over the notables DB: sect-classification accuracy, time MAE, and calibration curves.
G5 — Empirical calibration so credible intervals are honest.
G6 — Framed and documented as a computed indicator, human-in-the-loop.
Non-Goals¶
N1 — No deep learning for the inference (grid is exact at 1-D).
N2 — No single-timestamp “answer”; always a distribution.
N3 — Automatic personality inference is optional/soft, not the core.
N4 — Not a fully-automatic oracle; the practitioner keeps the final call.
3. Architecture¶
UnknownTimeChart + Evidence
│
▼
RectificationEngine
├─ candidate grid over the window (§4.1)
├─ fast re-cast: planets once, angles/houses per candidate (§4.2)
├─ per-candidate forward pass → activation features (§4.3)
├─ likelihood combiner (weighted) → log-posterior grid (§4.4)
└─ normalise → RectificationResult (posterior, sect marginal, explanations)
│
▼
Calibration (global weights/temperature) ── fit on the notables corpus (§6)
3.1 Data models (core/models.py or a rectification/ package)¶
@dataclass(frozen=True)
class LifeEvent:
date: date # best-known date
precision: str # "day" | "month" | "year"
type: str # taxonomy key (relationship, career, ...)
description: str
significance: str = "major" # major | moderate | minor
significators: tuple[str, ...] = () # optional override; else derived
@dataclass(frozen=True)
class Evidence:
events: tuple[LifeEvent, ...]
traits: tuple[str, ...] = () # soft, optional
@dataclass(frozen=True)
class EventContribution:
event: LifeEvent
technique: str
weight: float
log_likelihood_peak_time: datetime | None # where this evidence pulls
detail: str # human explanation
@dataclass(frozen=True)
class RectificationResult:
window_start: datetime
window_end: datetime
posterior: tuple[tuple[datetime, float], ...] # (candidate time, prob)
p_day: float
p_night: float
map_estimate: datetime # posterior mode
credible_intervals: dict[float, tuple[datetime, datetime]] # e.g. {0.68: (...)}
contributions: tuple[EventContribution, ...]
information_budget: float # expected achievable sharpness
notes: tuple[str, ...]
3.2 API¶
result = chart.rectify(evidence, resolution="4min") # on an UnknownTimeChart
result.p_day, result.p_night
result.map_estimate, result.credible_intervals[0.68]
for c in result.contributions: ... # explain
# Coarse-only shortcut (the first milestone):
sect = chart.classify_sect(evidence) # -> {"day": 0.82, "night": 0.18}
Underneath: RectificationEngine(chart, evidence, *, calibration=...). On a
timed chart, rectify() can still refine around the known time.
4. Mechanics¶
4.1 Candidate grid. Window = full day (unknown) or ±N hr (approximate), stepped at a configurable resolution (default ~4 min ≈ 1° of ASC). Optionally regime-aware: bound cells by sect flips and ASC sign-changes so discrete techniques evaluate once per regime.
4.2 Fast re-cast. Compute planetary positions once for the day; per candidate, recompute only ASC/MC/cusps and anything angle-derived (sect, Fortune/Spirit, house placements). Avoid full
.calculate()per candidate.4.3 Activation features. Per (candidate, event): for each technique, extract “how well and how sharply is this event’s significator activated near its date” — e.g. directed-angle arc vs. age, ZR peak/LB coincidence, active firdaria period ruler, profected-year lord, transit-to-angle proximity. Reuse the search engine’s
find_*inverters and the electionalpredicatesas ready-made “does this hold” primitives.4.4 Likelihood combiner. Map features → per-pair log-likelihood; weight and sum with the prior (theory §3.2). Start with hand-set weights; the calibration layer (§6) fits them later.
5. Phased plan (each independently shippable & informative)¶
Phase 0 — Harness. Candidate grid + fast angle-only re-cast + a way to run any technique across candidates. Pure plumbing; tested for correctness/speed.
Phase 1 — Sect classifier (the proving experiment). Only the coarse evidence (sect-dependent dignity/almuten shift, firdaria period matching, optional soft traits) →
P(day)/P(night). Shipchart.classify_sect()and a contrastive day/night report (walk both paths, show what differs).Phase 2 — Notables validation harness. Blind-test sect classification over the AA corpus → accuracy; wire the plumbing for time MAE + calibration curves. This is the go/no-go gate: does the signal exist?
Phase 3 — Continuous posterior. Add the sharp, time-sensitive techniques (directions/transits to angles, ZR peaks, returns) → full posterior over
twith credible intervals and the explanation object.Phase 4 — Calibration. Fit global weights/temperature on the corpus; calibrate credible-interval coverage. Escalate combiner complexity only if validation demands (expert weights → logistic regression → GBT).
Phase 5 — Soft evidence & extensions. Personality channel (human-in-loop or LLM-elicited, flagged); the information-budget elicitation UX; optionally the prenatal-epoch contributor.
6. Calibration & data¶
Corpus: AA-rated notables with documented, dated life events + temperament notes, gathered via the companion research prompt (
rectification-corpus-research-prompt.md). Provenance is verified at the source, not trusted from our DB — ourdata_quality/has_reliable_timelayer proved unreliable (rectified times mislabeled AA, uncited biography times mislabeled A), so the prompt independently re-establishes each birth time from an AstroDataBank/primary source note and rejects anything rectified, aggregator-sourced, or undocumented before it can enter the corpus. Itsrejected:output back-corrects the notables DB, so the verify-reject loop runs once. Critically, rectified times are excluded — validating a rectifier against a rectified time is circular.Fit: global per-technique weights + temperature; hold out a slice for honest reporting.
Report: sect accuracy; time median-abs-error; coverage (does the 68% interval contain truth ~68% of the time?).
7. Testing strategy¶
Correctness: the fast re-cast matches a full
.calculate()at the same time (angles, sect, house placements) to tolerance.Sanity: on a known-time notable, feeding its true events makes the posterior peak near the true time and
classify_sectreturns the correct sect.Determinism: grid + fixed weights → reproducible posterior.
Benchmark (slow tier): the notables harness runs as an offline script/test producing the accuracy/MAE/coverage report (not a per-commit gate).
8. Open questions¶
Q1 — Package home: ~~a new top-level
stellium/rectification/, or fold into an existing subsystem?~~ Resolved (2026-07-11): standalone-first. Build the proving harness outside the package as a consumer of stellium’s public API (underdocs/development/specs/rectification/), keepsrc/stellium/untouched until the sect classifier proves signal on the 63-person benchmark, then promote the proven core intostellium/rectification/. Building against the public surface doubles as a test of whether that surface suffices; gaps found become the promotion checklist. See the build spec: RECTIFICATION_PHASE0_SPEC.md.Q2 — Resolution/regime cells: uniform grid vs. regime-bounded cells. (Lean: regime-aware for the discrete techniques, fine grid for the sharp ones.)
Q3 — Significator mapping: fixed table vs. weighted-and-marginalised. (Lean: start fixed table from theory §6; marginalise later if it helps.)
Q4 — Soft evidence in v1? Recommend deferring to Phase 5; keep the core purely event-driven and defensible first.
Q5 — Corpus size for meaningful calibration: how many notables/events are needed before the fitted weights are trustworthy (vs. staying with expert priors)? Determined empirically in Phase 2.
9. Acceptance criteria¶
RectificationEngine+RectificationResultwith sect marginal, credible intervals, and itemised contributions.Fast angle-only re-cast verified against full calculation.
chart.classify_sect()+ a contrastive day/night report.Notables validation harness reporting sect accuracy, time MAE, and calibration coverage.
Posterior credible intervals are calibrated (coverage ≈ nominal) after Phase 4.
Docs framing it as a computed, explainable, human-in-the-loop indicator; theory + spec linked from
docs/development/README.md.No regression in the engines it orchestrates.