diff --git a/benchmarks/expansion_gate_bench.py b/benchmarks/expansion_gate_bench.py new file mode 100644 index 00000000..3187f755 --- /dev/null +++ b/benchmarks/expansion_gate_bench.py @@ -0,0 +1,294 @@ +"""Adaptive expansion-gate latency micro-bench (#741 acceptance). + +Replays a labelled fixture of broad / narrow prompts against the +public ``retrieve()`` surface in four cells: + + (gate-on, bfs-on) (gate-on, bfs-off) + (gate-off, bfs-on) (gate-off, bfs-off) + +The gate axis is driven by ``AELFRICE_NO_EXPANSION_GATE``; the BFS +axis is driven by the ``bfs_enabled`` kwarg on ``retrieve()``. For +each cell × label, the harness reports wall-clock p50 / p95 in +milliseconds and the count of calls where ``LaneTelemetry.expansion_ +gate_skipped_bfs`` fired (a sanity-check on the gate actually doing +something on broad prompts in the ``(gate-on, bfs-on)`` cell). + +Acceptance bullet from #741: broad-prompt p95 in ``(gate-on, bfs-on)`` +must beat broad-prompt p95 in ``(gate-off, bfs-on)`` by >= 30%. The +narrow-prompt p50 in the same two cells must not regress. + +Fixture format: JSONL, one row per line: + + {"prompt": "", "label": "broad"} + {"prompt": "", "label": "narrow"} + +Output: a single JSON file at ``/expansion_gate_bench.json``. +The harness seeds the multi-hop corpus from +``aelfrice.benchmark.seed_multihop_corpus`` so BFS and HRR have real +edges to walk; small corpus on purpose, latency-not-recall is the +load-bearing metric. + +Usage: + + uv run python -m benchmarks.expansion_gate_bench \\ + --fixture benchmarks/fixtures/expansion_gate_stub.jsonl \\ + --out benchmarks/results// +""" +from __future__ import annotations + +import argparse +import json +import os +import sys +import time +from contextlib import contextmanager +from dataclasses import asdict, dataclass, field +from datetime import datetime, timezone +from pathlib import Path +from typing import Iterator + +from aelfrice import __version__ as AELFRICE_VERSION +from aelfrice.benchmark import seed_multihop_corpus +from aelfrice.expansion_gate import ( + ENV_FORCE_EXPANSION, + ENV_NO_EXPANSION_GATE, +) +from aelfrice.retrieval import last_lane_telemetry, retrieve +from aelfrice.store import MemoryStore + + +FIXTURE_LABELS = ("broad", "narrow") +CELL_KEYS = ( + ("gate-on", "bfs-on"), + ("gate-on", "bfs-off"), + ("gate-off", "bfs-on"), + ("gate-off", "bfs-off"), +) + + +@dataclass(frozen=True) +class FixtureRow: + prompt: str + label: str # "broad" | "narrow" + + +@dataclass +class CellLabelStats: + n: int = 0 + p50_ms: float = 0.0 + p95_ms: float = 0.0 + gate_skipped_bfs_count: int = 0 + latencies_ms: list[float] = field(default_factory=list) + + def to_dict(self) -> dict[str, float | int]: + d = asdict(self) + d.pop("latencies_ms") + return d # type: ignore[return-value] + + +@dataclass +class BenchReport: + run_id: str + aelfrice_version: str + fixture_path: str + fixture_size: int + fixture_broad: int + fixture_narrow: int + started_at: str + finished_at: str + cells: dict[str, dict[str, dict[str, float | int]]] + + def to_dict(self) -> dict[str, object]: + return { + "run_id": self.run_id, + "aelfrice_version": self.aelfrice_version, + "fixture_path": self.fixture_path, + "fixture_size": self.fixture_size, + "fixture_broad": self.fixture_broad, + "fixture_narrow": self.fixture_narrow, + "started_at": self.started_at, + "finished_at": self.finished_at, + "cells": self.cells, + } + + +def _percentile(values: list[float], q: float) -> float: + if not values: + return 0.0 + if not 0.0 <= q <= 1.0: + raise ValueError(f"q must be in [0, 1], got {q}") + s = sorted(values) + if len(s) == 1: + return s[0] + pos = q * (len(s) - 1) + lo = int(pos) + hi = min(lo + 1, len(s) - 1) + frac = pos - lo + return s[lo] + (s[hi] - s[lo]) * frac + + +def load_fixture(path: Path) -> list[FixtureRow]: + rows: list[FixtureRow] = [] + with path.open("r", encoding="utf-8") as fh: + for lineno, raw in enumerate(fh, start=1): + line = raw.strip() + if not line or line.startswith("#"): + continue + try: + obj = json.loads(line) + except json.JSONDecodeError as exc: + raise ValueError( + f"{path}:{lineno} not valid JSON: {exc}" + ) from exc + prompt = obj.get("prompt") + label = obj.get("label") + if not isinstance(prompt, str) or not prompt.strip(): + raise ValueError(f"{path}:{lineno} missing/empty 'prompt'") + if label not in FIXTURE_LABELS: + raise ValueError( + f"{path}:{lineno} label must be one of " + f"{FIXTURE_LABELS!r}, got {label!r}" + ) + rows.append(FixtureRow(prompt=prompt, label=label)) + return rows + + +@contextmanager +def _gate_env(gate_on: bool) -> Iterator[None]: + """Toggle the expansion-gate via env. gate_on=True leaves the + gate resolver at default; gate_on=False sets + ``AELFRICE_NO_EXPANSION_GATE=1`` to disable it. Restores the + prior env on exit.""" + prior_no_gate = os.environ.get(ENV_NO_EXPANSION_GATE) + prior_force = os.environ.get(ENV_FORCE_EXPANSION) + # Always clear FORCE_EXPANSION so it never contaminates a cell. + if prior_force is not None: + del os.environ[ENV_FORCE_EXPANSION] + if gate_on: + if prior_no_gate is not None: + del os.environ[ENV_NO_EXPANSION_GATE] + else: + os.environ[ENV_NO_EXPANSION_GATE] = "1" + try: + yield + finally: + if prior_no_gate is None: + os.environ.pop(ENV_NO_EXPANSION_GATE, None) + else: + os.environ[ENV_NO_EXPANSION_GATE] = prior_no_gate + if prior_force is not None: + os.environ[ENV_FORCE_EXPANSION] = prior_force + + +def run_cell( + store: MemoryStore, + rows: list[FixtureRow], + *, + gate_on: bool, + bfs_on: bool, +) -> dict[str, CellLabelStats]: + stats: dict[str, CellLabelStats] = { + label: CellLabelStats() for label in FIXTURE_LABELS + } + with _gate_env(gate_on): + for row in rows: + t0 = time.perf_counter() + retrieve(store, row.prompt, bfs_enabled=bfs_on) + dt_ms = (time.perf_counter() - t0) * 1000.0 + tel = last_lane_telemetry() + s = stats[row.label] + s.n += 1 + s.latencies_ms.append(dt_ms) + if tel.expansion_gate_skipped_bfs: + s.gate_skipped_bfs_count += 1 + for s in stats.values(): + s.p50_ms = _percentile(s.latencies_ms, 0.50) + s.p95_ms = _percentile(s.latencies_ms, 0.95) + return stats + + +def _cell_name(gate_axis: str, bfs_axis: str) -> str: + return f"{gate_axis}__{bfs_axis}" + + +def run(fixture_path: Path, out_dir: Path, run_id: str) -> Path: + rows = load_fixture(fixture_path) + if not rows: + raise SystemExit(f"fixture {fixture_path} is empty") + + broad_n = sum(1 for r in rows if r.label == "broad") + narrow_n = sum(1 for r in rows if r.label == "narrow") + + store = MemoryStore(":memory:") + seed_multihop_corpus(store) + + started_at = datetime.now(timezone.utc).isoformat() + cells: dict[str, dict[str, dict[str, float | int]]] = {} + for gate_axis, bfs_axis in CELL_KEYS: + gate_on = gate_axis == "gate-on" + bfs_on = bfs_axis == "bfs-on" + cell_stats = run_cell(store, rows, gate_on=gate_on, bfs_on=bfs_on) + cells[_cell_name(gate_axis, bfs_axis)] = { + label: s.to_dict() for label, s in cell_stats.items() + } + finished_at = datetime.now(timezone.utc).isoformat() + + report = BenchReport( + run_id=run_id, + aelfrice_version=AELFRICE_VERSION, + fixture_path=str(fixture_path), + fixture_size=len(rows), + fixture_broad=broad_n, + fixture_narrow=narrow_n, + started_at=started_at, + finished_at=finished_at, + cells=cells, + ) + + out_dir.mkdir(parents=True, exist_ok=True) + out_path = out_dir / "expansion_gate_bench.json" + out_path.write_text( + json.dumps(report.to_dict(), indent=2, sort_keys=False) + "\n", + encoding="utf-8", + ) + return out_path + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser( + prog="expansion_gate_bench", + description=__doc__.splitlines()[0] if __doc__ else None, + ) + parser.add_argument( + "--fixture", + type=Path, + required=True, + help="Path to JSONL fixture of {prompt, label} rows.", + ) + parser.add_argument( + "--out", + type=Path, + required=True, + help="Output directory; writes /expansion_gate_bench.json.", + ) + parser.add_argument( + "--run-id", + default=None, + help="Run identifier embedded in the output JSON. " + "Default: UTC timestamp.", + ) + args = parser.parse_args(argv) + + if not args.fixture.is_file(): + parser.error(f"fixture not found: {args.fixture}") + run_id = args.run_id or datetime.now(timezone.utc).strftime( + "%Y%m%dT%H%M%SZ" + ) + + out_path = run(args.fixture, args.out, run_id) + print(f"wrote {out_path}", file=sys.stdout) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/fixtures/expansion_gate_acceptance.jsonl b/benchmarks/fixtures/expansion_gate_acceptance.jsonl new file mode 100644 index 00000000..ddfe1608 --- /dev/null +++ b/benchmarks/fixtures/expansion_gate_acceptance.jsonl @@ -0,0 +1,109 @@ +# 100-row labelled fixture for the #741 expansion-gate acceptance bench. +# Hand-authored from in-repo public material (README, CHANGELOG, docs/, +# src/, tests/). Schema: one JSON row per line, {prompt, label} where +# label is broad or narrow. +# +# Lines starting with '#' are comments; the loader skips them. +# +# --- Broad prompts (50) --------------------------------------------------- +{"prompt": "what is the right way to think about the four layer retrieval stack when a query touches both locked beliefs and the fuzzy long tail at the same time", "label": "broad"} +{"prompt": "why does the project insist on deterministic stdlib gates instead of just letting an embedding model decide which lane to fire on a given query", "label": "broad"} +{"prompt": "how should we decide when a multi hop walk is worth the latency budget versus returning a smaller but tighter set of locked anchors and recent corroborations", "label": "broad"} +{"prompt": "tell me about the overall philosophy behind the deterministic narrow surface and how that constrains the kinds of new gates we are willing to add", "label": "broad"} +{"prompt": "explain how the Bayesian posterior over a belief updates when fresh evidence arrives and what the half life decay buys us beyond a simple count of corroborations", "label": "broad"} +{"prompt": "which trade offs become load bearing when we start asking the rebuilder to fire on every turn versus only on threshold crossings or session boundaries", "label": "broad"} +{"prompt": "who should be the audience for the periodic checkpoint summaries and how does that audience shape the level of detail the rebuilder needs to preserve", "label": "broad"} +{"prompt": "what does it mean for the system to honor the principle that a belief is never silently overwritten and where in the pipeline does that promise actually live", "label": "broad"} +{"prompt": "why might a long natural language question be a poor fit for graph walks even when the underlying belief store has rich edges and recent provenance", "label": "broad"} +{"prompt": "how does the project draw the line between an honest disagreement among beliefs and a contradiction that the resolver should actively try to surface to the user", "label": "broad"} +{"prompt": "tell me about how locked anchors propagate confidence into neighbors and what stops a single locked claim from dragging the rest of the graph along with it", "label": "broad"} +{"prompt": "explain why the gate is willing to be conservative and skip expansion when in doubt rather than running the expensive lanes and ranking later", "label": "broad"} +{"prompt": "what does a healthy mix of broad and narrow prompts look like in real daily usage and how would we even measure that without leaking user content off device", "label": "broad"} +{"prompt": "why is it preferable to keep the expansion gate purely textual and shape based instead of learning a classifier on past query telemetry over time", "label": "broad"} +{"prompt": "how do we want the bench harness to treat warmups and outliers when the goal is comparing percentile latency rather than mean throughput across cells", "label": "broad"} +{"prompt": "which kinds of failure modes should we worry about most when the gate misclassifies a query and we silently lose the multi hop lane on that call", "label": "broad"} +{"prompt": "tell me about the relationship between recall and latency for the broad question shape and where the diminishing returns actually live on the curve", "label": "broad"} +{"prompt": "why does the rebuilder bother emitting a session scoped block in addition to the locked block when the locked rows are already pinned and load bearing", "label": "broad"} +{"prompt": "how would a user notice if the gate started firing too aggressively on prompts that look broad but actually wanted the graph walk to be honest", "label": "broad"} +{"prompt": "explain the reasoning behind keeping the structural query lane on even when the BFS lane is gated off for a given broad shaped query", "label": "broad"} +{"prompt": "what assumptions about prompt distribution underlie the acceptance bullet that broad p95 must improve by at least thirty percent when the gate is on", "label": "broad"} +{"prompt": "why should narrow prompt p50 latency be the regression guard rather than narrow prompt p95 or some richer summary of the tail", "label": "broad"} +{"prompt": "how does the deterministic resolver precedence interact with someone who wants to opt out of gating only for a particular project rather than globally", "label": "broad"} +{"prompt": "tell me about the kinds of structural markers that historically have correlated with a question actually wanting a multi hop walk in this codebase", "label": "broad"} +{"prompt": "explain why the question form prefix heuristic exists at all when the length and marker checks would already catch most genuinely broad prompts on their own", "label": "broad"} +{"prompt": "what should we do when a user pastes a long block of natural language that happens to contain one stray identifier near the end of the message", "label": "broad"} +{"prompt": "why might it be a mistake to expose the gate decision back to the model as a tool result instead of keeping it an internal telemetry signal", "label": "broad"} +{"prompt": "how does the project balance the desire for a small surface area against the reality that real users keep asking for one more knob on every release", "label": "broad"} +{"prompt": "which release notes are most relevant to someone trying to understand the current expansion gate behavior without reading every commit since the initial cut", "label": "broad"} +{"prompt": "tell me about the historical reasons the retrieval pipeline grew lanes incrementally rather than landing as one monolithic surface from the start", "label": "broad"} +{"prompt": "what would change about the gate design if the underlying belief store grew by an order of magnitude and the BFS lane became dramatically more expensive", "label": "broad"} +{"prompt": "why is wall clock latency the right yardstick for the acceptance bench instead of something like nodes visited or candidate set size at each stage", "label": "broad"} +{"prompt": "how should we describe the gate behavior in user facing documentation without overpromising determinism or hiding the heuristic nature of the decision", "label": "broad"} +{"prompt": "explain how the project thinks about test coverage when a feature is partly a latency optimization and partly a behavioral change in retrieval output", "label": "broad"} +{"prompt": "what makes a good labelled fixture for a latency benchmark and where does hand authoring stop being scalable once the corpus needs to grow", "label": "broad"} +{"prompt": "why does the README emphasize that aelfrice is a memory layer for long lived agents rather than a general purpose vector database for arbitrary text", "label": "broad"} +{"prompt": "how would a future contributor reason about whether to extend the gate with a new heuristic versus adding a whole new resolver step earlier in the pipeline", "label": "broad"} +{"prompt": "tell me about the way the project handles speculative beliefs and how that affects which candidates can ever reach the top of a retrieval result list", "label": "broad"} +{"prompt": "which design decisions in the retrieval surface are most likely to look obviously wrong in hindsight once the system has a year of real usage data", "label": "broad"} +{"prompt": "what would it take to convince the project to ship an embedding lane in spite of the stated preference for deterministic stdlib only retrieval mechanics", "label": "broad"} +{"prompt": "why is keeping retrieval offline and on device a hard constraint rather than something we relax when a hosted call would clearly improve recall", "label": "broad"} +{"prompt": "how does the project want maintainers to think about backwards compatibility for the gate decision dataclass as new fields get added over time", "label": "broad"} +{"prompt": "explain why the bench harness reports percentiles per label per cell instead of collapsing everything into a single global summary statistic", "label": "broad"} +{"prompt": "what are the load bearing claims in the acceptance criteria for the gate and which of them depend on the labelled corpus actually being representative", "label": "broad"} +{"prompt": "tell me about the way locked beliefs survive demotion pressure and how that interacts with retrieval ordering when neighbors decay around them", "label": "broad"} +{"prompt": "which guarantees does the retrieval surface make about ordering stability when two candidates have identical scores under the current ranking rule", "label": "broad"} +{"prompt": "why might a contributor reach for the gate as a latency lever before exploring caching or memoization on the expensive lanes themselves", "label": "broad"} +{"prompt": "how should documentation describe the difference between a broad prompt that gets short circuited and a genuinely empty query that gets only the locked rows", "label": "broad"} +{"prompt": "tell me about the kinds of questions where the user would actually be annoyed if the gate fired and silently dropped the multi hop lane behind their back", "label": "broad"} +{"prompt": "what should the project do about prompts that arrive in a language other than English where the question form prefix list will simply not match", "label": "broad"} +# --- Narrow prompts (50) -------------------------------------------------- +{"prompt": "should_run_expansion in src/aelfrice/expansion_gate.py", "label": "narrow"} +{"prompt": "AELFRICE_NO_EXPANSION_GATE env override semantics in src/aelfrice/expansion_gate.py", "label": "narrow"} +{"prompt": "AELFRICE_FORCE_EXPANSION escape hatch precedence at src/aelfrice/expansion_gate.py", "label": "narrow"} +{"prompt": "ExpansionDecision run_bfs field default", "label": "narrow"} +{"prompt": "ExpansionDecision run_hrr_structural reserved field", "label": "narrow"} +{"prompt": "BROAD_PROMPT_TOKEN_THRESHOLD constant value in expansion_gate.py", "label": "narrow"} +{"prompt": "_QUESTION_FORM_PREFIXES tuple contents in expansion_gate.py", "label": "narrow"} +{"prompt": "_has_structural_markers helper in expansion_gate.py", "label": "narrow"} +{"prompt": "_read_toml_flag walk in expansion_gate.py", "label": "narrow"} +{"prompt": "retrieve() bfs_enabled kwarg path", "label": "narrow"} +{"prompt": "last_lane_telemetry in src/aelfrice/retrieval.py", "label": "narrow"} +{"prompt": "LaneTelemetry expansion_gate_skipped_bfs flag", "label": "narrow"} +{"prompt": "#741 acceptance broad p95 bullet", "label": "narrow"} +{"prompt": "#724 lab_corpus convention reference", "label": "narrow"} +{"prompt": "#605 v3.0 philosophy_decision callsite", "label": "narrow"} +{"prompt": "#387 aelf_doctor producer for POTENTIALLY_STALE marker", "label": "narrow"} +{"prompt": "#421 edge_rerank consumer entry point", "label": "narrow"} +{"prompt": "#548 wonder_lifecycle RESOLVES edge", "label": "narrow"} +{"prompt": "EDGE_SUPPORTS insert path through MemoryStore.insert_edge", "label": "narrow"} +{"prompt": "EDGE_CONTRADICTS valence in edge_valence dict", "label": "narrow"} +{"prompt": "EDGE_SUPERSEDES propagation behavior in edge_valence", "label": "narrow"} +{"prompt": "EDGE_RELATES_TO catch_all weight value", "label": "narrow"} +{"prompt": "EDGE_DERIVED_FROM versus EDGE_CITES coupling in src/aelfrice/models.py", "label": "narrow"} +{"prompt": "EDGE_IMPLEMENTS propagation multiplier in src/aelfrice/models.py", "label": "narrow"} +{"prompt": "EDGE_TEMPORAL_NEXT chronological weight in src/aelfrice/models.py", "label": "narrow"} +{"prompt": "EDGE_TESTS evidential_edge multiplier value", "label": "narrow"} +{"prompt": "EDGE_RESOLVES wonder_marker zero valence", "label": "narrow"} +{"prompt": "POTENTIALLY_STALE marker_edge in models.py", "label": "narrow"} +{"prompt": "BFS_EDGE_WEIGHTS table entries in src/aelfrice/retrieval.py", "label": "narrow"} +{"prompt": "seed_multihop_corpus in aelfrice.benchmark", "label": "narrow"} +{"prompt": "benchmarks/expansion_gate_bench.py CELL_KEYS tuple", "label": "narrow"} +{"prompt": "benchmarks/fixtures/expansion_gate_stub.jsonl row count", "label": "narrow"} +{"prompt": "tests/unit/test_expansion_gate.py question form cases", "label": "narrow"} +{"prompt": "tests/integration/test_retrieve_expansion_gate.py wiring", "label": "narrow"} +{"prompt": "docs/PHILOSOPHY.md narrow_surface section", "label": "narrow"} +{"prompt": "src/aelfrice/models.py EDGE_VALENCE dict", "label": "narrow"} +{"prompt": "src/aelfrice/store.py MemoryStore insert_edge signature", "label": "narrow"} +{"prompt": "src/aelfrice/retrieval.py retrieve() entry", "label": "narrow"} +{"prompt": "src/aelfrice/context_rebuilder/__init__.py trigger_mode default", "label": "narrow"} +{"prompt": "[retrieval] expansion_gate_enabled in .aelfrice.toml", "label": "narrow"} +{"prompt": "CONFIG_FILENAME constant for .aelfrice.toml walk_root", "label": "narrow"} +{"prompt": "RETRIEVAL_SECTION key_name in expansion_gate.py", "label": "narrow"} +{"prompt": "EXPANSION_GATE_FLAG constant_lookup in expansion_gate.py", "label": "narrow"} +{"prompt": "_env_force_expansion truthy parse_table", "label": "narrow"} +{"prompt": "_env_no_expansion_gate falsy parse_table", "label": "narrow"} +{"prompt": "_starts_with_question_form word_boundary check", "label": "narrow"} +{"prompt": "_FILE_PATH_RE regex_prefixes src tests docs benchmarks scripts", "label": "narrow"} +{"prompt": "_SNAKE_CASE_RE pattern match on insert_edge", "label": "narrow"} +{"prompt": "_CAMEL_CASE_RE pattern_match on MemoryStore class", "label": "narrow"} +{"prompt": "_ISSUE_REF_RE matches #741 and #605 references", "label": "narrow"} diff --git a/benchmarks/fixtures/expansion_gate_stub.jsonl b/benchmarks/fixtures/expansion_gate_stub.jsonl new file mode 100644 index 00000000..a4e3fe35 --- /dev/null +++ b/benchmarks/fixtures/expansion_gate_stub.jsonl @@ -0,0 +1,37 @@ +# Stub fixture for benchmarks/expansion_gate_bench.py. +# +# This is a *wiring smoke test*, NOT the #741 acceptance corpus. The +# real labelled 50+50 broad/narrow corpus is sourced out-of-tree +# (gate:lab-corpus convention; see #724). Small-N numbers from this +# fixture are NOT load-bearing for the #741 acceptance bullet; they +# only confirm the harness produces parseable cells and the gate +# fires on broad-shaped prompts. +# +# All rows are hand-authored from in-repo public sources: the #741 +# issue body, CHANGELOG, README, source-file paths and identifiers +# visible under src/. +# +# Lines starting with '#' are comments; the loader skips them. +# +# --- Broad prompts (long, exploratory, no structural markers) --- +{"prompt": "what could we improve about how aelfrice handles broad natural-language questions that don't reference any specific belief structure or identifiers", "label": "broad"} +{"prompt": "tell me about the overall design philosophy of the retrieval pipeline and what tradeoffs the project makes between latency and recall", "label": "broad"} +{"prompt": "how does the system decide which beliefs to surface when many possibly relevant candidates compete for the same token budget", "label": "broad"} +{"prompt": "why might the rebuilder fire less often after the trigger mode default flipped and what would that mean for session state recovery quality over time", "label": "broad"} +{"prompt": "explain how the four layer retrieval stack interacts with the adaptive expansion gate and what failure modes we should watch for in real world usage", "label": "broad"} +{"prompt": "what would the right cadence be for periodic checkpointing if we wanted to balance recovery quality against per turn overhead on the hook path", "label": "broad"} +{"prompt": "tell me about the federation write model and how peers interact when one project wants to read from another project belief store", "label": "broad"} +{"prompt": "how should the deterministic narrow surface principle apply when designing new gates and resolvers that classify queries by shape", "label": "broad"} +{"prompt": "what are the open questions around adaptive half life as a meta belief and how would that interact with the existing posterior decay", "label": "broad"} +{"prompt": "describe the general approach to ranking when multiple lanes contribute candidates with different score scales and provenance histories", "label": "broad"} +# --- Narrow prompts (short, structural markers: ids, paths, issues) --- +{"prompt": "should_run_expansion in src/aelfrice/expansion_gate.py", "label": "narrow"} +{"prompt": "AELFRICE_NO_EXPANSION_GATE env override behavior", "label": "narrow"} +{"prompt": "ExpansionDecision run_bfs field", "label": "narrow"} +{"prompt": "context_rebuilder.py trigger_mode threshold default", "label": "narrow"} +{"prompt": "retrieve() bfs_enabled kwarg precedence", "label": "narrow"} +{"prompt": "#741 acceptance broad-prompt p95", "label": "narrow"} +{"prompt": "LaneTelemetry expansion_gate_skipped_bfs", "label": "narrow"} +{"prompt": "src/aelfrice/retrieval.py:1473 gate_decision", "label": "narrow"} +{"prompt": "is_bfs_enabled resolver TOML key", "label": "narrow"} +{"prompt": "EDGE_SUPPORTS insert_edge MemoryStore", "label": "narrow"}