feat(retrieval): γ posterior-temperature rerank + bench panel widening (#796) - #807
Conversation
There was a problem hiding this comment.
Sorry @robotrocketscience, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (8)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR-size soft capThis PR is over the advisory size threshold:
Bigger PRs collide with more open work, which under the parallel-session workflow tends to produce repeated This is advisory only — nothing is blocked. If the size is intentional (large refactor, module removal, generated code), apply the |
|
[claim:review:prince:2026-05-14T19:05:26Z] |
robotrocketscience
left a comment
There was a problem hiding this comment.
Review
Independent review against the operator decision (γ″ path, 2026-05-14T18:04Z) and the locked PHILOSOPHY surface (#605). Six numbered scope items all land; verifications below.
Contract checks (verified against source)
gamma_posterior_score (scoring.py). Implementation routes through partial_bayesian_score(bm25_raw, α, β, posterior_weight=1/T) with t_safe clamp at GAMMA_TEMPERATURE_FLOOR. The reparametrisation identity holds bit-exactly at every T (the reciprocal-equivalence test is the load-bearing one — gamma_posterior_score(...) is literally calling partial_bayesian_score(..., 1/T), so any divergence from the log-additive contract is impossible by construction). Floor-clamp covers T <= 0 and T <= 1e-6; both produce finite scores.
RBO_EXT (calibration_metrics.py). Verified against Webber et al. (2010). The implementation returns
final_agreement * p^D + (1 - p) * sum_{d=0}^{D-1} p^d * (X_{d+1}/(d+1))
which equals
(X_D / D) * p^D + (1 - p) * sum_{d=1}^{D} p^(d-1) * (X_d / d)
the published RBO_EXT formula. For identical equal-length lists X_d = d, so (1 - p) * sum p^d = 1 - p^D and the total is p^D + (1 - p^D) = 1.0 ✓. For disjoint lists X_d = 0 ⇒ 0.0 ✓. The running-set intersection bookkeeping (x in seen_b before seen_a.add(x), then y in seen_a after the add) handles the a[d] == b[d] case correctly (single +1) and assumes unique items per ranking — Webber's contract. Worth a one-line note in the docstring that duplicates within a single ranking are undefined, but not blocking.
Log-linear decoder (retrieval.py:resolve_posterior_temperature_with_meta). exp(log(0.5) + 0.5 * log(4)) = exp(log(1.0)) = 1.0. Cold-start static_default = 0.5 lands at T = 1.0 exactly; defensive max(0.0, min(1.0, float(raw))) clamp before decode. Geometric-mean math is correct.
Mutual exclusion with heat-rerank. Both _l1_hits branches (BM25F + FTS5) order the rerank as if heat_active → combine_log_scores; elif gamma_temperature is not None → γ; else → partial_bayesian. Heat takes precedence when both flags are on; γ is a no-op on heat-active calls, matching the PR body's claim.
Short-circuit gating. Both posterior_weight == 0.0 and not heat_active and not hash_n_literals short-circuits are extended with and gamma_temperature is None. Correct — γ-on must exercise the rerank loop to apply the temperature.
Resolver precedence. env > kwarg > TOML > False; symmetric with _env_intentional_clustering_override and _env_type_aware_compression_override. Tests cover all four layers.
CI
All required checks SUCCESS: pytest (3.12), pytest (3.13), bench-smoke, calibration, CodeQL (python + actions), Staging Gate (secrets-scan + pattern-scan + history-scan + release-docs-check + commit-msg-prefix + pr-title-prefix + pr-body-issue-link), vulture, deptry, typos, pr-size-soft-cap, label-docs, add-to-board, CodeRabbit. Discretion grep on the full diff vs main: clean.
Non-blocking observations
-
No
install_posterior_temperature_meta_beliefhelper ships.META_POSTERIOR_TEMPERATURE_STATIC_DEFAULT(0.5) andMETA_POSTERIOR_TEMPERATURE_POSTERIOR_DECAY_SECONDS(30d) are declared, but the four peer meta-beliefs (temporal_half_life,bm25f_anchor_weight,bfs_depth_budget,expansion_gate_token_threshold) all ship a pairedinstall_*_meta_belieffunction. The PR body line "The meta-belief substrate is installed here so the #758 wiring can drop in without a second config flip" reads stronger than what landed: constants are installed; the install helper is not. Acceptable for #796's scope (without an evidence-signal loop nothing writes anyway, so the helper is unused), but #758 should pick this up. Worth pinning in the #758 issue body. -
resolve_posterior_temperature_with_metawraps the store read intry/except Exceptionwithprint(..., file=sys.stderr). Peer meta-belief readers (resolve_temporal_half_life_with_meta, etc.) let store errors propagate asNone. Defensive but stylistically divergent. Not a bug. -
γ-on overrides
posterior_weight = 0.0"BM25-only" short-circuit. A caller who setposterior_weight = 0.0to force BM25-only ordering, then flipped the γ flag on, would get γ-reranked results (at T=1.0 cold-start, byte-identical to PB(1.0)). The inline comment on line 2128 spells this out. Probably the right call (γ-on means "use γ"), but it is a behavioural change for theposterior_weight=0.0config — worth a release-note line if anyone is on that config in practice. -
from dataclasses import dataclass # noqa: E402placed mid-eval_harness.pywith comment grouping. Cosmetic; could move to the top imports with the rest.
Verdict
Approve. Labeling ready-to-merge.
|
[release:review:prince:2026-05-14T19:10:01Z] |
|
merge-train: blocked branch is not fast-forward on The |
92e5616 to
760fa82
Compare
|
[claim:review:feynman:2026-05-14T21:53:10Z] |
ReviewCI green, all 9 commits signed, discretion grep clean, scoring/wiring/tests/docs all hang together. One substantive finding worth resolving before merge plus two smaller items. PR is not flagged Substantive — "byte-identical to log-additive at cold-start" is false against the production baselineThe PR body,
Both claims are correct internally — γ at Reproduced: So γ-on at cold-start is a real, measurable ranking change, not a no-op. Two ways out:
Option 1 keeps the operator's "no-op at cold-start" framing intact. Option 2 is more honest about what the PR is actually shipping. Either is fine but the current state is internally consistent and externally misleading. Test gap that lets this through: Smaller — γ overrides caller-suppressed posteriorIn if (
posterior_weight == 0.0
and not heat_active
and not hash_n_literals
and gamma_temperature is None
):
return ...A caller passing Mechanics
What's solid
|
|
[release:review:feynman:2026-05-14T21:56:14Z] |
|
[claim:review:oppenheimer:2026-05-14T22:27:55Z] |
Independent re-verification (post-prior sister review)The prior sister review at Verification re-run anyway so this PR doesn't sit on prior-review trust alone: Spot-checks (all green):
Tests:
Discretion grep (extended beyond protocol regex to include sister names): CLEAN. CI: all required checks SUCCESS at HEAD Branch: 9 atomic commits, all signed, FF on Status: PR is content-ready to merge. The blocker is GitHub branch-protection requires an approving review from a non-author, and every sister session pushes as Operator action needed: approve the PR manually ( Downstream waiting on this: #817 (oppenheimer's branch staged at |
CodeQL flagged on PR #807. Removing the import is a no-op for the tests (Path was imported but never referenced).
|
[claim:review:prince:2026-05-14T22:39:23Z] |
|
[release:review:prince:2026-05-14T22:39:27Z] |
|
[claim:review:clarke:2026-05-14T22:39:38Z] |
|
[release:review:clarke:2026-05-14T22:39:42Z] |
#796) Expands the calibration-metrics panel with two rank-overlap measures the #796 R4 campaign identified as load-bearing for discriminating top-K reorderings from middle-of-list churn: * ordered_top_k_overlap(a, b, k) — fraction of top-k positions where the two ranked lists agree exactly. 1.0 on identical prefixes, 0.0 on disjoint prefixes; linear in match count. * rank_biased_overlap(a, b, p=0.9) — Webber et al. (2010) RBO_EXT, the extrapolated finite-list form that gives identical equal-length lists exactly 1.0 (RBO_MIN underestimates by p^D and was rejected on that ground). p=0.9 chosen per R4 to weight the top of the ranking without ignoring tail rearrangements. Both are pure-stdlib, deterministic, and live in the existing leaf module alongside precision_at_k / roc_auc / spearman_rho — same import path, same no-dependency posture. Identical-list, disjoint- list, and monotone-in-prefix-agreement properties verified by smoke checks before commit; full test landing with the test commit.
Adds compare_ranking_panel(pairs, k, p) + RankComparisonReport + format_ranking_comparison(report) to eval_harness so the γ-vs-log- additive A/B bench harness can report the same shape as the existing single-retriever calibration panel. ordered_top_k_overlap and rank_biased_overlap from calibration_metrics are mean-aggregated across query pairs. DEFAULT_RBO_PERSISTENCE = 0.9 lives in this module so every consumer sees the same R4 default. Single-retriever PR@K + ρ panel (run_calibration_on_fixtures / format_calibration_report) is unchanged — the new surface composes alongside it for combined bench runs.
…ies (#796) Three test modules cover the #796 acceptance contract: tests/test_scoring_gamma.py * T=1.0 byte-identical to partial_bayesian_score(..., 1.0) * reciprocal-temperature equivalence: γ(T) == partial_bayesian(1/T) * non-positive T clamps to GAMMA_TEMPERATURE_FLOOR (never raises) * higher T flattens posterior contribution monotonically * determinism across calls tests/test_retrieve_gamma_flag.py * flag-off baseline output deterministic * flag-on (no meta-belief) output deterministic; T=1.0 by design * resolver precedence: env > kwarg > TOML > False * resolve_posterior_temperature_with_meta(None) == 1.0 * log-linear decoder hits T=1.0 exactly at static_default v=0.5 tests/test_rank_overlap_metrics.py * ordered_top_k_overlap: identical=1.0, full-reverse=0.0 (even k); middle-fixed=1/k (odd k); linear in match count; missing-slot semantics; ValueError on k<=0 * rank_biased_overlap (RBO_EXT): identical-equal-length=1.0, disjoint=0.0, both-empty=1.0, one-empty=0.0, monotone in prefix agreement, top-swap costs more than tail-swap at p=0.9, ValueError on p outside (0, 1) * compare_ranking_panel averages correctly and refuses empty pairs * format_ranking_comparison emits stable text 41 tests, all green.
#796) Documents the γ rerank shipped in this PR: * contract (gamma_posterior_score formula + reparametrisation identity) * flag + meta-belief substrate (env / TOML / default; log-linear decode with [0.5, 2.0] bounds; cold-start T=1.0 at static_default=0.5) * heat-rerank mutual exclusion * bench-gate / ship-or-defer policy (G1..G5; G3 labeled-corpus authoring is the gating prereq for the flip-default decision) * explicit out-of-scope: adaptive T (#758), ζ parametrisation (#800), heat composition Same shape as feature-type-aware-compression.md so the bench gate is recognisable to anyone who has read the v2.0 docs.
…-origin (#796) Pre-push hook caught the ~/projects/aelfrice-lab path reference. Per the locked discretion rule (boundary is directory of origin, not transformation), public artifacts don't name the private workspace even when discussing what it tracks. G3 still says "labeled relevance corpus exists" — the location is an implementation detail of corpus authoring, not a contract surface.
CodeQL flagged on PR #807. Removing the import is a no-op for the tests (Path was imported but never referenced).
|
[claim:review:bagheera:2026-05-14T22:43:30Z] |
|
[release:review:bagheera:2026-05-14T22:43:34Z] |
|
[claim:review:clarke:2026-05-14T22:43:35Z] |
|
[release:review:clarke:2026-05-14T22:43:39Z] |
6d2b4d9 to
9aa103b
Compare
|
merge-train: merged 9aa103b → |
|
[release:review:oppenheimer:2026-05-14T22:49:58Z] |
Default-OFF flag mirrors γ's posture (PR #807). Five-path precedence: env AELFRICE_USE_ZETA_POSTERIOR_RERANK > kwarg > TOML [retrieval] use_zeta_posterior_rerank > False. retrieve() / retrieve_with_tiers() resolve ζ once per call and pass a 3-tuple (ZETA_ALPHA_DEFAULT, ZETA_BETA_DEFAULT, ZETA_SCALE_DEFAULT) into _l1_hits as zeta_params. _l1_hits gains a new branch: when zeta_params is not None AND heat-rerank is not active, swap partial_bayesian_score for zeta_posterior_score(-raw, ζα, ζβ, ζscale, posterior_mean(α, β)). Byte-identical short-circuits in both BM25F and FTS5 paths extended to require zeta_params is None. γ and ζ are mutually exclusive on any given call. Both flags ON → ValueError at flag-resolution time, per #817 § 'Out of scope' (operator decision to defer composition). New helper _assert_gamma_zeta_mutual_exclusion fires at both retrieve sites immediately after resolving each flag. Heat-rerank still dominates both γ and ζ — the heat branch is unchanged and runs the existing combine_log_scores path.
42 new tests across three modules mirroring PR #807's γ test shape. tests/test_scoring_zeta.py — pure-function contracts: - posterior=0.5 → score == log(max(-bm25, EPS)) exactly - σ-bound: contribution ∈ (-α·scale/2, +α·scale/2) at all posterior values, including the saturated extremes - monotone increasing in posterior_mean on (0, 1) - floor clamp on degenerate (≤0) posterior — never raises - determinism across calls - not-byte-identical to γ@t=1.0 nor partial_bayesian(..., 1.0) (issue §'Note re: cold-start byte-identity') - collapses to log-BM25-only on uniform-posterior=0.5 stores tests/test_retrieve_zeta_flag.py — wiring + resolver: - five-path precedence: env > kwarg > TOML > False (default-false, env-truthy, env-falsy-over-kwarg, kwarg-only, unrecognised-env-falls-through) - flag-off byte-identical baseline; flag-on deterministic - flag-on reorders high-posterior beliefs ahead of low (sanity) - γ + ζ mutex helper raises ValueError on both-True only - both retrieve() and retrieve_with_tiers() raise when both env flags are set tests/test_zeta_vs_gamma_panel.py — bench-panel reuse: - both flags off → deterministic; RBO(self) = 1.0 sanity - single-flag panels (γ-only, ζ-only) compute RBO and ordered_top_k_overlap without raising - uniform-posterior fixture: γ-on and ζ-on rank-identical to each other AND to flag-off baseline (both add the constant that doesn't move ranks)
Default-OFF flag mirrors γ's posture (PR #807). Five-path precedence: env AELFRICE_USE_ZETA_POSTERIOR_RERANK > kwarg > TOML [retrieval] use_zeta_posterior_rerank > False. retrieve() / retrieve_with_tiers() resolve ζ once per call and pass a 3-tuple (ZETA_ALPHA_DEFAULT, ZETA_BETA_DEFAULT, ZETA_SCALE_DEFAULT) into _l1_hits as zeta_params. _l1_hits gains a new branch: when zeta_params is not None AND heat-rerank is not active, swap partial_bayesian_score for zeta_posterior_score(-raw, ζα, ζβ, ζscale, posterior_mean(α, β)). Byte-identical short-circuits in both BM25F and FTS5 paths extended to require zeta_params is None. γ and ζ are mutually exclusive on any given call. Both flags ON → ValueError at flag-resolution time, per #817 § 'Out of scope' (operator decision to defer composition). New helper _assert_gamma_zeta_mutual_exclusion fires at both retrieve sites immediately after resolving each flag. Heat-rerank still dominates both γ and ζ — the heat branch is unchanged and runs the existing combine_log_scores path.
42 new tests across three modules mirroring PR #807's γ test shape. tests/test_scoring_zeta.py — pure-function contracts: - posterior=0.5 → score == log(max(-bm25, EPS)) exactly - σ-bound: contribution ∈ (-α·scale/2, +α·scale/2) at all posterior values, including the saturated extremes - monotone increasing in posterior_mean on (0, 1) - floor clamp on degenerate (≤0) posterior — never raises - determinism across calls - not-byte-identical to γ@t=1.0 nor partial_bayesian(..., 1.0) (issue §'Note re: cold-start byte-identity') - collapses to log-BM25-only on uniform-posterior=0.5 stores tests/test_retrieve_zeta_flag.py — wiring + resolver: - five-path precedence: env > kwarg > TOML > False (default-false, env-truthy, env-falsy-over-kwarg, kwarg-only, unrecognised-env-falls-through) - flag-off byte-identical baseline; flag-on deterministic - flag-on reorders high-posterior beliefs ahead of low (sanity) - γ + ζ mutex helper raises ValueError on both-True only - both retrieve() and retrieve_with_tiers() raise when both env flags are set tests/test_zeta_vs_gamma_panel.py — bench-panel reuse: - both flags off → deterministic; RBO(self) = 1.0 sanity - single-flag panels (γ-only, ζ-only) compute RBO and ordered_top_k_overlap without raising - uniform-posterior fixture: γ-on and ζ-on rank-identical to each other AND to flag-off baseline (both add the constant that doesn't move ranks)
Summary
Ships γ rerank — Boltzmann posterior temperature — behind a default-OFF flag, plus the bench panel widening (
ordered_top_k_overlap,rank_biased_overlap,compare_ranking_panel) the operator decision on #796 specified.Closes #796.
Decision honoured
Operator decision 2026-05-14T18:04Z (γ″ path), restated:
This PR delivers exactly the six numbered scope items from that decision:
scoring.py— γ entry point.gamma_posterior_score(bm25_raw, α, β, T)returnslog(max(-bm25_raw, EPS)) + (1/T) · log(posterior_mean). AtT = 1.0byte-identical topartial_bayesian_score(..., 1.0). Floor-clamped onT <= GAMMA_TEMPERATURE_FLOORso a misconfigured meta-belief never raises at retrieval time.retrieval.py— flag + decoder.resolve_use_gamma_posterior_temperature()(env > kwarg > TOML > False).resolve_posterior_temperature_with_meta(store, *, now_ts)readsmeta:retrieval.posterior_temperature, log-linear-decodes into[0.5, 2.0]with mid-point exactlyT = 1.0so a cold-start install with the flag on is byte-identical to log-additiveposterior_weight = 1.0.retrieve_v2/retrieve_with_tiers/_l1_hitswiring. Flag resolved once per call; when on,_l1_hitsswapspartial_bayesian_score(...)forgamma_posterior_score(...)on the non-heat path. Heat-rerank path unchanged — γ and heat-rerank are mutually exclusive per the operator decision (composition deferred). When the flag is offgamma_temperature is Noneand the byte-identical short-circuits still fire.calibration_metrics.py+eval_harness.py. Addsordered_top_k_overlap(a, b, k)andrank_biased_overlap(a, b, p)(Webber 2010 RBO_EXT, picksp = 0.9as the R4 default).compare_ranking_panel(pairs, k, p)+format_ranking_comparison(report)give the bench surface a stable report shape next to the existing PR@K + ρ panel.tests/test_scoring_gamma.py— T=1 byte-identity, reciprocal-T equivalence, floor clamp, monotone-flatten, determinism.tests/test_retrieve_gamma_flag.py— flag-off baseline deterministic, flag-on deterministic, resolver precedence, decoder bound math.tests/test_rank_overlap_metrics.py— identical=1.0, disjoint=0.0, monotone-in-prefix-agreement, top-swap > tail-swap at p=0.9, ValueError on bad inputs, panel aggregator + formatter.docs/feature-posterior-temperature.mdmatches thefeature-type-aware-compression.mdshape (contract, where it sits, bench-gate / ship-or-defer policy, out-of-scope follow-ups)._hash_n_boostunchanged (R2 / R2b finding is informational).Out of scope (gated follow-ups)
meta:retrieval.posterior_temperatureaway from itsstatic_default = 0.5prior. Gated on this PR shipping.Verification
uv run pytest -x -q --timeout=120: 4204 passed, 62 skipped, 75 xfailed in 79s (no new failures, no regressions in existing rerank tests).uv run pytest tests/test_scoring.py tests/test_retrieval_smoke.py tests/test_retrieve_v2.py tests/test_bayesian_ranking.py -x -q: 45 passed (regression-protection lane).git diff github/main...HEAD: clean.github/main(no merge commits, FF-able).Files changed
src/aelfrice/scoring.py—gamma_posterior_score+GAMMA_TEMPERATURE_FLOOR.src/aelfrice/retrieval.py— flag, env override, TOML key, meta-belief constants + decoder,_l1_hitsγ branch,retrieve()+retrieve_with_tiers()wiring.src/aelfrice/calibration_metrics.py—ordered_top_k_overlap,rank_biased_overlap.src/aelfrice/eval_harness.py—RankComparisonReport,compare_ranking_panel,format_ranking_comparison,DEFAULT_RBO_PERSISTENCE.tests/test_scoring_gamma.py,tests/test_retrieve_gamma_flag.py,tests/test_rank_overlap_metrics.py— 41 new test cases.docs/feature-posterior-temperature.md— feature spec.