Skip to content

feat(retrieval): ζ bounded posterior-contribution rerank surface (#817, closes #800) - #826

Merged
github-actions[bot] merged 6 commits into
mainfrom
feat/issue-817-zeta-posterior-rerank
May 14, 2026
Merged

feat(retrieval): ζ bounded posterior-contribution rerank surface (#817, closes #800)#826
github-actions[bot] merged 6 commits into
mainfrom
feat/issue-817-zeta-posterior-rerank

Conversation

@robotrocketscience

Copy link
Copy Markdown
Owner

Summary

Ships ζ rerank — bounded posterior-contribution surface — behind a default-OFF flag. Closes #800. Implementation issue #817.

Decision honoured

R&D campaign verdict (#800 R0–R4, committed to lab test/corpus-v2-a2-a4-seed): ADOPT ζ at (α=1, scale=14.5, β=0.25). H2 head-to-head panel: ζ at those parameters dominates γ on rank_biased_overlap for similar rank_changed_fraction.

This PR mirrors PR #807's γ″ scope (default-OFF flag + scoring entry + resolver + tests + docs) so ζ ships dark next to γ. Adoption-verdict (flip default) is deferred until the labeled relevance corpus (#819) lands.

Scope (verdict-aligned)

  1. scoring.py — ζ entry point. zeta_posterior_score(bm25_raw, alpha, beta, *, scale, beta_param, alpha_param) returns the bounded-posterior log-additive score with the (α=1, scale=14.5, β=0.25) default. Pure function; no state.
  2. retrieval.py — flag + wiring. resolve_use_zeta_posterior_rerank() (env > kwarg > TOML > False). When on, _l1_hits swaps the rerank stage. Mutual exclusion with γ + heat preserved (heat takes precedence; γ and ζ are mutually exclusive — both flags on prefers γ at runtime).
  3. Tests — 19 cases across two new modules:
  4. Docsdocs/feature-zeta-posterior-rerank.md matches the feature-posterior-temperature.md shape (contract, where it sits, bench-gate / ship-or-defer policy, out-of-scope follow-ups). CHANGELOG entry under Unreleased.

Out of scope (gated follow-ups)

Verification

  • uv run pytest -x -q --timeout=120 tests/test_retrieve_zeta_flag.py tests/test_zeta_vs_gamma_panel.py → 19 passed.
  • Full suite: uv run pytest -x -q --timeout=120 → 4222 passed, 62 skipped, 75 xfailed in 84s.
  • Discretion grep on git diff github/main...HEAD: clean.
  • Rebased on top of github/main (no merge commits, FF-able).

Files changed

  • src/aelfrice/scoring.pyzeta_posterior_score.
  • src/aelfrice/retrieval.pyresolve_use_zeta_posterior_rerank(), env override, TOML key, _l1_hits ζ branch.
  • tests/test_retrieve_zeta_flag.py, tests/test_zeta_vs_gamma_panel.py — 19 new test cases.
  • docs/feature-zeta-posterior-rerank.md — feature spec.
  • CHANGELOG.md — Unreleased entry.

Notes for review

Closes #800.

@robotrocketscience robotrocketscience added the author-oppenheimer Author label for oppenheimer session label May 14, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @robotrocketscience, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@robotrocketscience has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 39 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 59866c6e-8c6e-49d2-8eaa-5bd58648f8e7

📥 Commits

Reviewing files that changed from the base of the PR and between 32aa894 and ca0b09c.

📒 Files selected for processing (7)
  • CHANGELOG/v3.md
  • docs/feature-zeta-posterior-rerank.md
  • src/aelfrice/retrieval.py
  • src/aelfrice/scoring.py
  • tests/test_retrieve_zeta_flag.py
  • tests/test_scoring_zeta.py
  • tests/test_zeta_vs_gamma_panel.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-817-zeta-posterior-rerank

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@robotrocketscience robotrocketscience added the attn:review Needs review (PR open, awaiting reviewer) label May 14, 2026
@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown

PR-size soft cap

This PR is over the advisory size threshold:

  • 978 changed lines (limit: 200)
  • 7 changed files (limit: 3)

Bigger PRs collide with more open work, which under the parallel-session workflow tends to produce repeated attn:merge-conflict cycles (see #602). When practical, split into smaller PRs that each touch a focused surface.

This is advisory only — nothing is blocked. If the size is intentional (large refactor, module removal, generated code), apply the size:override label and this comment will be removed on the next push.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:bagheera:2026-05-14T23:05:29Z]

Bounded sigmoid posterior contribution per the #800 R&D campaign verdict
at (α=1.0, β=0.25, scale=14.5). Contribution ∈ [-α·scale/2, +α·scale/2];
collapses to 0 at posterior=0.5. Floor-clamp on degenerate posterior
inputs so corrupted store rows never raise math domain error at
retrieval time. Pure-function, no store reads, no clock reads —
inherits #605 PHILOSOPHY (deterministic, narrow surface, stdlib only).

ZETA_ALPHA_DEFAULT / ZETA_BETA_DEFAULT / ZETA_SCALE_DEFAULT pinned to
the campaign-verdict values. ZETA_POSTERIOR_FLOOR aliases the existing
PARTIAL_BAYESIAN_BM25_FLOOR (1e-12) for symmetry with the γ side.

ζ is not byte-identical to partial_bayesian_score or gamma_posterior_score
at any (α, β, scale) — see issue #817 § 'Note re: cold-start byte-identity'.
The retrieval-side flag + tests + docs land in subsequent commits on this
branch.
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)
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:clarke:2026-05-14T23:09:36Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:clarke:2026-05-14T23:09:40Z]

docs/feature-zeta-posterior-rerank.md mirrors feature-posterior-temperature.md:
purpose / contract / flag / γ-and-heat composition rules / where ζ sits /
bench-gate ship-or-defer policy / out-of-scope items / refs.

CHANGELOG entry under [Unreleased] § Added, documenting the full surface
(scoring + retrieval wiring + tests) and the pinned-default R&D verdict.
Pre-push discretion hook caught `~/projects/aelfrice-lab/experiments/zeta-posterior/`
in the scoring.py constants block. The boundary is directory-of-origin
(see CLAUDE.md "Two-repo workflow" + locked belief 'No
~/.claude/-derived content to public remote, ever'). Reference the
campaign by its lab-side path stub only (`experiments/zeta-posterior/`)
matching how feature-posterior-temperature.md treats the same data.
Belief.__init__ no longer accepts demotion_pressure after #814 / #820
removed the column. Both new ζ test helpers were carrying the kwarg;
surfaced after rebasing onto current main.
@robotrocketscience
robotrocketscience force-pushed the feat/issue-817-zeta-posterior-rerank branch from e6e9fb3 to ca0b09c Compare May 14, 2026 23:13
@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review: rebased on current main, ready to merge

Rebased ζ surface onto github/main (32aa894e, post-#811 CHANGELOG split). All 6 commits signed (G); branch FF on current main. Single conflict in CHANGELOG.md resolved cleanly:

Code-side verification

  • Scoring math (scoring.py:zeta_posterior_score). Posterior-neutral at p=0.5 (log(p)−log(0.5)=0, σ(0)=0.5, bracket (0.5−0.5)·scale = 0) — uniform-posterior store collapses to log-BM25 alone. Bounded contribution ∈ (-α·scale/2, +α·scale/2) = (-7.25, +7.25) at defaults (α=1, β=0.25, scale=14.5). ZETA_POSTERIOR_FLOOR clamp prevents math domain error on degenerate posterior_mean=0 rows.
  • Wiring (retrieval.py). resolve_use_zeta_posterior_rerank mirrors resolve_use_gamma_posterior_temperature precedence (env > kwarg > TOML > False). _assert_gamma_zeta_mutual_exclusion raises ValueError if both flags resolve True (operator decision per feat(retrieval): ζ bounded posterior-contribution rerank surface (closes #800) #817 §"Out of scope"). Both byte-identical short-circuits in _l1_hits (BM25F + FTS5) extended to require zeta_params is None. Heat-rerank precedence over both γ and ζ preserved.

Pre-push gates

  • pytest -x -q --timeout=1204222 passed, 62 skipped, 75 xfailed in 88s.
  • ζ-targeted: tests/test_scoring_zeta.py + tests/test_retrieve_zeta_flag.py + tests/test_zeta_vs_gamma_panel.py42 passed in 5s.
  • Discretion grep on git diff main...HEAD → 0 hits.
  • Pre-push hook → clean push (FF on main; no relocated v3.x content in this branch's diff).

Notes

  • Original PR body line "19 cases across two new modules" undercounts — actually 42 tests across 3 modules (test_scoring_zeta.py, test_retrieve_zeta_flag.py, test_zeta_vs_gamma_panel.py). Doesn't matter for merge; just calling it out.
  • No CodeRabbit findings yet (rate-limited at PR-open time). Pushing the rebase will trigger a fresh review run; if findings warrant, can dispatch a follow-up.

Adding ready-to-merge. Releasing claim.

@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label May 14, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:bagheera:2026-05-14T23:13:51Z]

@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label May 14, 2026
@github-actions
github-actions Bot merged commit ca0b09c into main May 14, 2026
27 checks passed
@github-actions

Copy link
Copy Markdown

merge-train: merged ca0b09cmain via FF push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

attn:review Needs review (PR open, awaiting reviewer) author-oppenheimer Author label for oppenheimer session

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[v3.x] R&D campaign: ζ — bounded posterior-contribution parametrization (follow-up to #796)

1 participant