Skip to content

feat(retrieval): adaptive expansion_gate token threshold (#760) - #799

Merged
github-actions[bot] merged 4 commits into
mainfrom
feat/issue-760-expansion-gate-token-threshold-meta
May 14, 2026
Merged

feat(retrieval): adaptive expansion_gate token threshold (#760)#799
github-actions[bot] merged 4 commits into
mainfrom
feat/issue-760-expansion-gate-token-threshold-meta

Conversation

@robotrocketscience

Copy link
Copy Markdown
Owner

Closes #760

Sub-task F of umbrella #480 (adaptive meta-belief layer). Wires meta:retrieval.expansion_gate.token_threshold into the expansion-gate long-prompt heuristic. Reframed per the 2026-05-14T16:55Z operator decision (single-key reframe + wait for #789, both satisfied).

What this PR adds

  • Log-linear bounded encoding [20, 320] for the new meta-belief, mirroring the [v3.x] E: meta:retrieval.bfs_depth_budget — adaptive L2 depth cap (#480 sub-task) #759 bfs_depth_budget pattern. Geometric mean of 20 and 320 is exactly 80, so decode(0.5) == BROAD_PROMPT_TOKEN_THRESHOLD == 80 with zero floating-point error — cold-start is byte-identical to today's hardcoded path.
  • META_EXPANSION_GATE_TOKEN_THRESHOLD_KEY = "meta:retrieval.expansion_gate.token_threshold", STATIC_DEFAULT = 0.5, POSTERIOR_DECAY_SECONDS = 30 * 24 * 3600 (30d).
  • decode_expansion_gate_token_threshold(v) -> int, is_meta_belief_expansion_gate_token_threshold_enabled() -> bool, install_expansion_gate_token_threshold_meta_belief(store, *, now_ts) -> None, resolve_expansion_gate_token_threshold_with_meta(store, *, now_ts, explicit=None) -> int.
  • should_run_expansion gains optional store + now_ts kwargs; both retrieve() and retrieve_with_tiers() now pass these through, activating the adaptive layer for production callers when the env flag is on.
  • get_active_meta_belief_consumers() updated to include the new key when the env flag is on — this closes the Live close-the-loop relevance-signal infrastructure — #756 / #480 prereq #779 relevance-signal loop for this consumer (UPS sweeper delivers referenced ∈ {0, 1} evidence on the next turn).
  • Env flag: AELFRICE_META_BELIEF_EXPANSION_GATE_TOKEN_THRESHOLD (also accepts =enabled, mirroring the sibling pattern). Ships default-OFF per the [exploration] Adaptive half-life as a Bayesian-engine-governed meta-belief #480 bench-gate clause.

What this PR does NOT change

Atomic commits

  1. 16778a7c feat(retrieval): log-linear bounded expansion_gate token threshold encoding + meta-belief env flag
  2. bff9cd05 feat(retrieval): install_expansion_gate_token_threshold_meta_belief helper + meta-aware resolver
  3. ae06caa4 feat(expansion_gate): wire meta-belief expansion_gate_token_threshold into expansion gate
  4. e52d93ee docs(changelog): note adaptive expansion_gate token threshold consumer (#760)

All four commits SSH-signed (%G? = G).

Tests

  • tests/test_expansion_gate_token_threshold_meta.py (new, 39 tests): encoding boundaries + int return + bounds + monotonicity + clamping, env-flag tokens, constants pin, cold-start byte-identical property, install idempotency, signal-class pin to relevance, resolver precedence (five paths), 100-strong-positive-evidence responsiveness, get_active_meta_belief_consumers inclusion and sorting, and four integration tests (flag-off byte-identical, flag-on + meta-absent fallback, flag-on + cold-start byte-identical, flag-on + high-threshold lets medium-prompt through).
  • Full suite: 4163 passed, 62 skipped, 75 xfailed in 81.9s.

Acceptance check

Out of scope (per spec)

…coding + meta-belief env flag

First slice of #760 consumer wiring. Pure additions — no caller
changes yet, so expansion gate is byte-identical until the
read-path commits land.

Adds:

- EXPANSION_GATE_TOKEN_THRESHOLD_FLOOR (20),
  EXPANSION_GATE_TOKEN_THRESHOLD_CEIL (320),
  META_EXPANSION_GATE_TOKEN_THRESHOLD_KEY,
  META_EXPANSION_GATE_TOKEN_THRESHOLD_STATIC_DEFAULT (0.5),
  META_EXPANSION_GATE_TOKEN_THRESHOLD_POSTERIOR_DECAY_SECONDS (30d).
- ENV_META_BELIEF_EXPANSION_GATE_TOKEN_THRESHOLD =
  "AELFRICE_META_BELIEF_EXPANSION_GATE_TOKEN_THRESHOLD", ships
  default-OFF behind the #437 A/B bench-gate clause.
- decode_expansion_gate_token_threshold(v) — log-linear interpolation
  [0, 1] → [20, 320] with int(round(...)). v=0.5 decodes to 80
  exactly: the geometric mean of 20 and 320 is sqrt(6400) = 80,
  so cold-start byte-identical to BROAD_PROMPT_TOKEN_THRESHOLD.
- is_meta_belief_expansion_gate_token_threshold_enabled() — truthy-
  token resolver mirroring is_meta_belief_bfs_depth_budget_enabled(),
  including the '=enabled' spelling.

23 new tests cover encoding boundaries (floor, ceil, mid=80),
clamping for out-of-range inputs, int return, bounds on [0,100],
monotonicity, env-flag truthy/falsy tokens, constants pin, and
the cold-start byte-identical property.
…elper + meta-aware resolver

Second slice of #760. Still no caller change in expansion_gate.py —
the new resolve_expansion_gate_token_threshold_with_meta() function
exists in parallel, ready for the third commit to wire in at the
token-threshold resolution site.

Adds:

- install_expansion_gate_token_threshold_meta_belief(store, *, now_ts)
  — idempotent install of the #760 row with the v3.x ratified
  defaults: relevance signal only (expansion quality is measured by
  whether injected beliefs are referenced, not by latency), 30d
  posterior decay, static_default=0.5 which decodes to 80 exactly
  via decode_expansion_gate_token_threshold.

- resolve_expansion_gate_token_threshold_with_meta(store, *, now_ts,
  explicit=None) — two-tier precedence: explicit kwarg (positive int)
  > meta-belief (gated on the env flag) > BROAD_PROMPT_TOKEN_THRESHOLD.
  No TOML or env-var override layer — the token threshold has never
  had a user-facing config knob. store=None collapses to the static
  default.

- Updates get_active_meta_belief_consumers() to include
  META_EXPANSION_GATE_TOKEN_THRESHOLD_KEY when the env flag is on,
  so the #779 UPS sweeper delivers relevance evidence to it at the
  next turn.

12 new tests cover: install idempotency, signal-class pin (relevance
only), cold-start value == BROAD_PROMPT_TOKEN_THRESHOLD, five resolver
precedence paths, 100-strong-positive-evidence responsiveness, and
two get_active_meta_belief_consumers inclusion/sorting checks.
… into expansion gate

Closes the #760 loop in should_run_expansion. The hardcoded
BROAD_PROMPT_TOKEN_THRESHOLD comparison at the long-prompt heuristic
is now resolved through resolve_expansion_gate_token_threshold_with_meta
when a store is supplied; falls back to the static 80 when no store or
flag-off.

Wiring:

- should_run_expansion gains optional store + now_ts kwargs (both
  default None). When store is provided and the env flag is on, the
  threshold is read from the meta-belief and decoded; otherwise the
  hardcoded BROAD_PROMPT_TOKEN_THRESHOLD is used. TYPE_CHECKING import
  for MemoryStore avoids a runtime import cycle; the resolver is
  imported deferred inside the function body to mirror the existing
  deferred-import pattern throughout expansion_gate.py.

- retrieve() and retrieve_with_tiers() in retrieval.py both pass
  store=store, now_ts=int(time.time()) into should_run_expansion,
  activating the adaptive layer for production callers. retrieve_v2()
  reaches the same path through retrieve_with_tiers.

- BROAD_PROMPT_TOKEN_THRESHOLD is NOT deleted — it remains as the
  cold-start fallback, the static_default reference, and a named
  constant for contributors. The old hardcoded value is still the
  default when the feature flag is off.

- Relevance signal update: the #779 UPS sweeper already handles this
  via get_active_meta_belief_consumers() (updated in Commit 2). No
  additional signal-update wiring is needed inside expansion_gate.py.

Default-OFF byte-identical contract: with the env flag unset,
resolve_expansion_gate_token_threshold_with_meta collapses to
BROAD_PROMPT_TOKEN_THRESHOLD (80), identical to the pre-#760 path.

4 new integration tests cover: flag-off byte-identical, flag-on +
meta-absent fallback, flag-on + cold-start value byte-identical, and
flag-on + high-threshold lets medium-prompt through.

@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

@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:

  • 689 changed lines (limit: 200)
  • 4 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.

@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 2 minutes and 34 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: 51f96fbd-cb50-4b18-910d-c27978f2a4a9

📥 Commits

Reviewing files that changed from the base of the PR and between c345f3d and e52d93e.

⛔ Files ignored due to path filters (1)
  • CHANGELOG.md is excluded by !**/CHANGELOG.md
📒 Files selected for processing (3)
  • src/aelfrice/expansion_gate.py
  • src/aelfrice/retrieval.py
  • tests/test_expansion_gate_token_threshold_meta.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-760-expansion-gate-token-threshold-meta

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.

Comment thread src/aelfrice/expansion_gate.py
Comment thread src/aelfrice/retrieval.py
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:bagheera:2026-05-14T17:59:27Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review (bagheera, queue review):

Labeling ready-to-merge for the merge-train.

@robotrocketscience robotrocketscience added ready-to-merge Trigger merge-train: FF main to this PR's head and removed attn:review Needs review (PR open, awaiting reviewer) labels May 14, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:bagheera:2026-05-14T18:01:58Z]

@github-actions

Copy link
Copy Markdown

merge-train: blocked

2 review thread(s) are unresolved on these files: src/aelfrice/expansion_gate.py, src/aelfrice/retrieval.py. Resolve them on the PR (click 'Resolve conversation' on each) and re-add the label.

The ready-to-merge label has been removed. Address the issue above and re-add the label when you're ready for another attempt.

@github-actions github-actions Bot removed 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

Resolved 2 CodeQL cyclic-import threads (retrieval ↔ expansion_gate). The cycle is pre-existing and mitigated by deferred function-body imports + TYPE_CHECKING — the standard pattern in this codebase. CodeQL doesn't model deferred imports; no runtime cycle exists. Re-labeling ready-to-merge.

@robotrocketscience robotrocketscience added 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 e52d93e into main May 14, 2026
32 of 34 checks passed
@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

Copy link
Copy Markdown

merge-train: merged e52d93emain via FF push.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[v3.x] F: meta:retrieval.expansion_gate_thresholds — adaptive #741 knobs (#480 sub-task)

2 participants