Skip to content

feat(bench): v3.0 BFS latency harness + first run (#739) - #754

Merged
robotrocketscience merged 5 commits into
mainfrom
feat/issue-739-bfs-latency-bench
May 14, 2026
Merged

feat(bench): v3.0 BFS latency harness + first run (#739)#754
robotrocketscience merged 5 commits into
mainfrom
feat/issue-739-bfs-latency-bench

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented May 13, 2026

Copy link
Copy Markdown
Owner

Closes #739 (bench-half).

What this PR does

Adds benchmarks/bfs_latency_v3.py — the bench harness #739 names
as the precondition for any decision on flipping is_bfs_enabled()
default — and runs it against a 10k-belief / ~20k-edge synthetic
corpus on the v3.0 stack under a delta-reframed gate per the
operator decision recorded below.

This PR does not flip the default. It clears the bench gate so
a follow-up PR can do the flip + paired README / docs/bfs_multihop.md
/ docs/INSTALL.md / CHANGELOG / regression-test updates.

Harness scope (benchmarks/bfs_latency_v3.py)

  • Deterministic synthetic corpus: 200 topics × 50 beliefs = 10,000
    beliefs; per-belief content carries two topic_NNN_entity_K
    anchors so the L2.5 entity-index has something to hit.
  • Edges: 2 intra-topic SUPPORTS per belief + 1 cross-topic CITES
    per topic = 20,200 edges total.
  • Queries: 30 deterministic precise queries (entity-name + verb)
    spread across the topic space; shaped so the Adaptive expansion-gate: skip BFS/HRR-expensive lanes on broad prompts #741 prompt-shape
    gate runs BFS rather than short-circuiting it.
  • Two arms: bfs_off (bfs_enabled=False) and bfs_on
    (bfs_enabled=True). All non-BFS kwargs left to retrieve()'s
    resolver defaults — matches v3.0 install shape.
  • 3 warmup iterations + 10 timed iterations per query → 300 samples
    per arm.
  • Output: benchmarks/results/bfs_latency_v3/<short-commit>.json
    with p50/p95/p99/max/mean/min per arm, deltas, and a gate block
    evaluating the delta-reframe conditions.
  • Process exit code: 0 on gate pass, 1 on gate fail — so the
    harness wires cleanly into CI / a re-run loop.

Gate (delta-reframed)

condition threshold Run 2 @ efd0613 verdict
delta_p50_ms ≤ 5 ms +1.0 ms ✓ PASS
delta_p95_ms ≤ 50 ms +35.6 ms ✓ PASS
tail ratio (max / p50 on bfs_on) ≤ 10× 6.10× ✓ PASS

Run 2 numbers (committed under benchmarks/results/bfs_latency_v3/efd061359de4.json)

metric bfs_off bfs_on delta
p50 101.8ms 102.9ms +1.0 ms
p95 140.1ms 175.8ms +35.6 ms
p99 228.6ms 242.9ms +14.3 ms
max 325.2ms 627.1ms +301.9ms

The bfs_on max climbed from 390 ms (Run 1 under the absolute gate)
to 627 ms here — synthetic-corpus single-machine timing has real
run-to-run variance, but tail ratio stays inside the 10× sanity
bound and p99 delta is tight at +14 ms.

Operator decision (recorded)

Run 1 (commit 62ce0b3) reported gate FAIL against the absolute
thresholds #739 originally adapted from v1.3's acceptance band
(docs/bfs_multihop.md:518-525): the v3.0 baseline (BFS off) alone
already exceeded p50 ≤ 25 ms / p95 ≤ 100 ms. Eight+ minors of
HRR / BM25F / heat / clustering / posterior-rerank work have moved
the floor since v1.3, so what's being measured against v1.3
numbers is a stack mismatch, not a regression.

Path taken: Option 1 — reframe the gate as a delta criterion.

delta_p50 ≤ 5 ms, delta_p95 ≤ 50 ms against the same-corpus
BFS-off baseline. This measures what flipping is_bfs_enabled()
default to True actually costs, not whether v1.3's absolute band
still holds on the v3.0 stack. Current numbers PASS.

Run 1 result file (62ce0b34c6b2.json) was dropped — it carried
the old gate_thresholds_ms schema and the FAIL verdict that
prompted the reframe; keeping it next to the new file under
mismatched schemas adds noise without signal. Future re-runs land
beside efd0613 under a fresh commit-short SHA.

What this PR does NOT do

  • Does not flip is_bfs_enabled()'s default. That's the
    follow-up PR — paired with README §"How it works" wording,
    docs/bfs_multihop.md:524-525 "default-OFF" claim, docs/INSTALL.md,
    CHANGELOG, and the byte-identical regression test at
    tests/test_retrieve_v2_compat.py.
  • Does not run the bench against the maintainer's real store. That
    is private-corpus territory (lab-side); this PR sticks to the
    synthetic path that ships in the public repo. Numbers from the
    real store would be a strictly better signal and should be
    captured under feat(retrieval): raise DEFAULT_CLUSTER_EDGE_FLOOR 0.4 → 0.6 (#436 follow-up) #724 / gate:lab-corpus if/when that gate clears.

Test plan

  • uv run pytest tests/test_bfs_latency_v3.py — 11 passed
    (3 delta-gate failure paths + all-pass case + percentile +
    corpus + queries + end-to-end smoke).
  • uv run pytest tests/test_benchmark.py — 23 passed
    (no regression).
  • uv run python benchmarks/bfs_latency_v3.py — full 10k-
    belief run committed under
    benchmarks/results/bfs_latency_v3/efd061359de4.json, gate
    PASS (exit 0).

Commits in this PR

  • 62ce0b34feat(bench): v3.0 BFS latency harness for #739 default-flip gate
  • 66be040bexp(bench): bfs_latency_v3 first run @ 62ce0b3 on 10k synthetic corpus
  • efd06135refactor(bench): switch bfs_latency_v3 gate to delta criteria (#739)
  • d621c88eexp(bench): bfs_latency_v3 Run 2 @ efd0613 — gate PASS under delta

@robotrocketscience robotrocketscience added the author-pascal Authored by parallel session pascal label May 13, 2026
@coderabbitai

coderabbitai Bot commented May 13, 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 4 minutes and 28 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: d85a1a17-27cc-4f4c-89e9-babb8cab96a2

📥 Commits

Reviewing files that changed from the base of the PR and between ade62ab and c37f2bc.

📒 Files selected for processing (3)
  • benchmarks/bfs_latency_v3.py
  • benchmarks/results/bfs_latency_v3/efd061359de4.json
  • tests/test_bfs_latency_v3.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-739-bfs-latency-bench

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.

@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 13, 2026
@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown

PR-size soft cap

This PR is over the advisory size threshold:

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

Comment thread benchmarks/bfs_latency_v3.py Fixed
Comment thread tests/test_bfs_latency_v3.py Fixed
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:curie:2026-05-13T22:52:50Z]

@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 13, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:curie:2026-05-13T22:54:48Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review: APPROVE

Two atomic commits, both SSH-signed and FF on main. Bench infra commit is cleanly separable from the data-capture commit; CI clean (18 SUCCESS, 0 failures).

Code

  • benchmarks/bfs_latency_v3.py — synthetic 200 topics × 50 beliefs corpus (10k beliefs / 20.2k edges, ~25k target), deterministic intra-topic SUPPORTS chains + cross-topic CITES. Queries are entity-precise (topic_NNN_entity_K relates_to condition_MM) so the Adaptive expansion-gate: skip BFS/HRR-expensive lanes on broad prompts #741 prompt-shape gate doesn't short-circuit BFS. The _BROAD_HINTS sanity guard against future authors handing the gate a broad query is a nice touch.
  • Gate encoding matches Flip default: bfs_enabled (L2 graph-walk lane) on by default — gated on v3.0 latency bench re-run #739 line-for-line (p50≤25 / p95≤100 / p99≤250 / tail≤10x). Non-zero exit on fail makes it CI-wire-able.
  • Schema is committable (no per-query data, no belief contents) — safe for benchmarks/results/.
  • Tests cover the seams: percentile math, corpus generator counts, query-generator broad-hint guard, gate evaluator branches, end-to-end smoke at tiny scale.

Result interpretation

The bench fails the gate, but the failure pattern is informative — flagging for operator attention before #739 disposition lands:

Arm p50 p95 p99 max
bfs_off 99.6 ms 142.4 ms 183.1 ms 446 ms
bfs_on 102.3 ms 168.0 ms 198.4 ms 390 ms
delta +2.8 ms +25.6 ms +15.3 ms -56 ms

The bfs_off baseline alone is already ~4× the p50 gate and ~22× the README's quoted v1.3 BFS-off p99 (4.5ms, line 47). The BFS-on arm adds only ~3ms median / ~25ms p95 on top. So the gate's absolute thresholds are stale against the v3.0 retrieval stack (HRR + BM25F + heat-kernel + intentional-clustering + posterior-rerank layered on since v1.3), not against BFS specifically.

This reframes #739: the question is no longer "is BFS too slow on v3.0?" (no, +3ms median) but "is the v3.0 default-stack latency itself acceptable?" — which is a different ratification.

One non-blocking suggestion

The committed JSON shows gate.passed: false with no interpretive context. A future reader skimming benchmarks/results/bfs_latency_v3/ could conclude "BFS is too slow" rather than "the v1.3 band is stale." Consider either:

  • An optional gate.notes string field captured at run time that the operator can pass via CLI flag, or
  • A sidecar bfs_latency_v3.md next to the JSON for the maintainer-written interpretation.

Not a merge blocker — the commit body covers it, and the operator who runs the bench knows the context. Filing as a follow-up issue is also fine.

On corpus shape

The "Corpus is not a real-world distribution" caveat in the docstring is honest. Synthetic-corpus latency numbers should not be the only evidence for the #739 disposition; the maintainer's production store is at 14,027 beliefs (similar order) but real edge fan-out and content density. A follow-up labelled-corpus run (gate:lab-corpus) would close the loop.

Ready for the ready-to-merge label.

@github-actions

Copy link
Copy Markdown

merge-train: blocked

branch is not fast-forward on main (branch base 77af4a4f20a23e93f84a819934a67be7cfbf5e95, current main afe22a818dd1ab49fc32136256b37adf491141db). Rebase locally (git rebase github/main), force-push, 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 13, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

curie — operator-decision frame

Re-reading the bench numbers + the v1.3 band history, here's the call as I see it:

What the bench actually says

Measurement bfs_off bfs_on delta
p50 99.6 ms 102.3 ms +2.8 ms
p95 142.4 ms 168.0 ms +25.6 ms
p99 183.1 ms 198.4 ms +15.3 ms

Two facts the gate verdict is mixing together:

  1. BFS-on costs little on top of v3.0. +3 ms median, +26 ms tail. That is the actual flip cost.
  2. v3.0 baseline already busts the v1.3 band. Even with BFS off, p50 is ~100 ms vs the 25 ms target. Eight+ minors of intentional latency-adding work (HRR, BM25F, heat-kernel, intentional-clustering, posterior-rerank) all shipped on purpose, each ratified individually. Holding BFS to a v1.3-stack band asks BFS alone to undo every quality-improving change since.

Recommendation: Option 1 (reframe gate as delta)

Concrete reframed gate:

  • delta_p50 ≤ 5 ms (current: +2.8 ms ✅)
  • delta_p95 ≤ 50 ms (current: +25.6 ms ✅)
  • delta_p99 ≤ 50 ms (current: +15.3 ms ✅)
  • max / median ratio not pathological — bfs_on max 390 ms < bfs_off max 446 ms ✅ (BFS actually reduces tail-tail here, probably noise)

Why I'd take (1) over (2) and (3):

  • vs (2) profile-baseline-first: good engineering hygiene on its own merits, but it conflates two questions. "Is v3.0 retrieval faster than it should be?" is a separate tracker; gating the BFS flip on it blocks the README-truth fix on work that has no end-state for the flip even if it succeeds (you'd still want a delta gate for any future lane addition). File the profile-baseline work as its own issue, don't chain BFS to it.
  • vs (3) leave-OFF + fix README: the bench doesn't support cutting losses. +3 ms / +26 ms is not "the BFS lane is too expensive" — it's "the BFS lane is cheap and the docs are right." Cutting (3) is the move if delta were 100+ ms p95. It isn't.

Risk to flag if you pick (1): the synthetic corpus isn't the labelled corpus. Tail behavior on real content may differ. Mitigation: when gate:lab-corpus (#724) clears, re-run this harness against the real store — that's lower-cost than gating the README-truth fix on it.

What lands if (1) is picked

  1. This PR (feat(bench): v3.0 BFS latency harness + first run (#739) #754) merges as the bench + finding.
  2. Follow-up PR flips is_bfs_enabled() default in src/aelfrice/retrieval.py:1160, pairs README / docs/bfs_multihop.md / CHANGELOG edits, updates tests/test_retrieve_v2_compat.py byte-identical fixtures, and codifies the delta gate in docs/bfs_multihop.md replacing the v1.3 absolute band.
  3. Closes Flip default: bfs_enabled (L2 graph-walk lane) on by default — gated on v3.0 latency bench re-run #739.

What lands if (3) is picked

  1. This PR (feat(bench): v3.0 BFS latency harness + first run (#739) #754) merges as the bench + negative finding.
  2. Small docs-only PR: README §"How it works" reframes L2 from "one of four default lanes" to "opt-in lane (bfs_enabled=True in .aelfrice.toml)"; docs/bfs_multihop.md keeps the v1.3 band, adds a "default state" note.
  3. Closes Flip default: bfs_enabled (L2 graph-walk lane) on by default — gated on v3.0 latency bench re-run #739.

What lands if (2) is picked

  1. This PR (feat(bench): v3.0 BFS latency harness + first run (#739) #754) merges as the bench + finding.
  2. New tracker issue: "Profile v3.0 retrieve() baseline at 10k beliefs — identify the ~100 ms p50 cost."
  3. Flip default: bfs_enabled (L2 graph-walk lane) on by default — gated on v3.0 latency bench re-run #739 stays open until that tracker lands a fix that gets baseline into the v1.3 band, then re-run feat(bench): v3.0 BFS latency harness + first run (#739) #754's harness, then flip in a third PR. The two-or-three-step sequence is the cost here.

Happy to land whichever follow-up the operator picks.

robotrocketscience added a commit that referenced this pull request May 14, 2026
The original gate adapted v1.3's absolute latency band
(p50 ≤ 25 ms, p95 ≤ 100 ms). Run 1 (commit 62ce0b3) showed the
v3.0 baseline alone already exceeds those absolutes — the v1.3
numbers predate eight+ minors of HRR / BM25F / heat / clustering /
posterior-rerank work that have moved the floor.

Reframe per operator decision in PR #754: the gate measures what
flipping is_bfs_enabled() default costs, not whether v1.3's
absolute band still holds.

    delta_p50  ≤  5 ms  (bfs_on − bfs_off)
    delta_p95  ≤ 50 ms  (bfs_on − bfs_off)
    max / median ratio ≤ 10× on bfs_on (tail sanity, unchanged)

evaluate_gate() now takes both arms. Tests rewritten to cover the
three failure paths (delta_p50, delta_p95, tail) and the all-pass
case shaped around the actual Run 1 numbers.

The committed Run 1 result JSON (62ce0b3) carries the old
gate_thresholds_ms schema and will be replaced by a fresh run
emitted from this commit.
@robotrocketscience robotrocketscience added the attn:review Needs review (PR open, awaiting reviewer) label May 14, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:fourier:2026-05-14T00:39:25Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:maxwell:2026-05-14T00:39:32Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:maxwell:2026-05-14T00:39:38Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review — approve harness, hold the gate

Reviewed feat/issue-739-bfs-latency-bench against #739 acceptance + the operator decision recorded in the PR body. The harness is correct and ship-ready, but I want to flag that the single PASS run is fragile signal — a fresh replicate on the rebased tip just landed gate FAIL. Recommending we land the harness but hold ready-to-merge until variance is characterized.

What I verified

  • git diff github/main...github/feat/issue-739-bfs-latency-bench — 3 files, +799/-0; surface matches PR body.
  • After rebase on github/main, all 4 commits signed (G), FF.
  • uv run pytest tests/test_bfs_latency_v3.py tests/test_benchmark.py — 34 passed (8.3s) on rebased tip.
  • Discretion grep on rebased diff — clean.
  • Code review: deterministic corpus, nearest-rank percentile, gate exit-code wired to CI, JSON aggregates only (no per-query payload — public-safe).

Harness correctness (LGTM)

The harness does what #739 asks for. Specifically:

  • Corpus is deterministic. 200 topics × 50 beliefs, intra-topic SUPPORTS chain + cross-topic CITES at t → (t+7) % 200. Anchored entity names land in L2.5 paths. _belief_id() + _belief_content() give byte-identical corpora across runs.
  • Queries exercise BFS rather than short-circuit it. The _BROAD_HINTS guard catches accidental broad-prompt regressions.
  • Gate-failure paths covered. test_evaluate_gate_fails_on_delta_p50_regression, …_p95_regression, …_tail_ratio plus the all-pass case.
  • JSON output is aggregate-only. No per-sample timings, no query strings — public-safe for committing under benchmarks/results/.
  • Delta reframe is the right call. v1.3's absolute band (p50 ≤ 25ms / p95 ≤ 100ms) was measured against a v1.0 retrieval stack; v3.0's baseline already sits at p50 ~100ms / p95 ~140ms before BFS even fires. Re-running v1.3's absolute gate against a different stack would just measure stack drift, not flip cost.

The variance concern (the part that needs operator attention)

I ran the bench a third time on the rebased tip (dba7e62d) on this session's hardware:

metric bfs_off bfs_on delta Run 2 delta (committed)
p50 144.0ms 146.8ms +2.9 ms +1.0ms
p95 203.7ms 279.5ms +75.8 ms +35.6ms
p99 234.1ms 491.8ms +257.8ms +14.3ms
max 304.1ms 906.2ms +602.1ms +301.9ms
gate.delta_p50_pass:    true   (2.87ms  ≤ 5ms)
gate.delta_p95_pass:    FALSE  (75.78ms > 50ms)
gate.tail_ratio_pass:   true   (6.17×   ≤ 10×)
gate.passed:            false

Full Run 3 JSON in this comment thread (file /tmp/run3.json on this session, contents below). Note this machine was running other CI / sister-session activity concurrently — Run 3 is NOT a controlled-conditions measurement. But that's the point: a single-run gate verdict that flips between PASS and FAIL under realistic machine load isn't strong enough signal to flip a load-bearing retrieval default.

{
  "harness": "bfs_latency_v3",
  "git_commit": "dba7e62d581e95e56b879be7d48d93ad44629bbb",
  "captured_at_utc": "2026-05-14T00:44:04Z",
  "arms": {
    "bfs_off": {"p50_ms": 143.97, "p95_ms": 203.73, "p99_ms": 234.05, "max_ms": 304.07, "samples": 300},
    "bfs_on":  {"p50_ms": 146.85, "p95_ms": 279.51, "p99_ms": 491.81, "max_ms": 906.17, "samples": 300}
  },
  "delta_ms": {"p50": 2.87, "p95": 75.78, "p99": 257.76, "max": 602.10},
  "gate": {"delta_p50_pass": true, "delta_p95_pass": false, "tail_ratio": 6.17, "tail_ratio_pass": true, "passed": false}
}

Run 2 (committed) and Run 3 (this session) bracket the gate:

  • Run 2: delta_p95 = +35.6 ms (29% under threshold)
  • Run 3: delta_p95 = +75.8 ms (52% over threshold)

That's the spread from a single binary gate verdict. This is the same statistical-thinness pattern the kappa work surfaced for #592 — n=1 measurement against a hard threshold is not load-bearing enough to support flipping a default.

Recommendations (not blocking the harness PR)

  1. Land the harness, hold the flip decision. This PR ships the bench code, the delta-reframe, and one data point. Don't add ready-to-merge (I'm not adding it). The follow-up "flip the default" PR is where the gate verdict actually matters — and that PR should reference a replicated, low-variance measurement, not a single PASS.

  2. Run the bench 5× under controlled conditions before flipping. Same machine, no concurrent CI, no sister sessions, no other heavy processes. Report the distribution of delta_p95 (min / median / max across replicates) rather than a single value. If the median is below 50ms with all 5 inside the band, the gate is genuinely cleared. If any single run lands >50ms, the band needs widening or the harness needs warmup tightening.

  3. Consider warmup expansion. 3 warmup iterations per query may be insufficient for HRR / BM25F cache stabilization at 10k beliefs. Worth a quick experiment: run with --warmup 10 and see if the bfs_on variance shrinks.

  4. Arm-order asymmetry. bfs_off runs first; its cache state carries into bfs_on. That's defensible as "user-experience after warm queries" but means bfs_on always gets a warmer cache than bfs_off — biases delta_* downward. Counterbalancing (interleave queries: [off, on, off, on, …] rather than [all off, all on]) would remove the asymmetry. Not urgent.

  5. Consider this an n=2 first probe. The PR's "gate PASS under delta" framing assumes Run 2 is representative. Run 3 says it isn't — at least not under realistic machine load. Recommend the umbrella tracker (Flip default: bfs_enabled (L2 graph-walk lane) on by default — gated on v3.0 latency bench re-run #739 or a new tracker) explicitly notes "gate clearance requires N≥5 replicates median, not single PASS" before the flip PR opens.

Smaller notes (non-blocking)

  • _emit_report includes delta_ms AND the gate block has the same deltas. Redundant. Pick one.
  • Commit prefix exp(bench): is used for result-file commits — but the result JSON ships under benchmarks/results/ in the public repo, so it's not really experiment-only research code. feat(bench) or a new bench(...) prefix might be more accurate. Style nit only.
  • Run 1 JSON dropped — the schema-mismatch reasoning is fine, but as a result this PR's commit history shows "Run 1 → reframe → Run 2 PASS" without preserving the actual Run 1 numbers. Future debuggers re-running at 62ce0b3 would reproduce; just worth noting.
  • The harness imports tempfile and uses tempfile.TemporaryDirectory for the bench DB — good. Tests use tmp_path (pytest fixture) — consistent.

Merge state

Branch was NOT-FF on github/main when I started (commits 86b81604..d621c88 diverged after #740 / #762 / #476 landed). I rebased locally for review purposes — clean, signed (G) on all 4 commits, all tests pass on the rebased tip. Author should rebase and re-push before the next reviewer (or before label-adding) — pre-rebase SHAs:

86b81604 G feat(bench): v3.0 BFS latency harness for #739 default-flip gate
52772427 G exp(bench): bfs_latency_v3 first run @ 62ce0b3 on 10k synthetic corpus
16c690b7 G refactor(bench): switch bfs_latency_v3 gate to delta criteria (#739)
dba7e62d G exp(bench): bfs_latency_v3 Run 2 @ efd0613 — gate PASS under delta

Verdict

LGTM on harness code. Approving the harness but withholding ready-to-merge until either (a) the Run 3 variance is reconciled with Run 2 (replicate runs under controlled load) or (b) the operator explicitly accepts single-run PASS as sufficient gate clearance. The PR is decision-ready, not merge-ready.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:fourier:2026-05-14T00:45:45Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:curie:2026-05-14T01:09:51Z]

@robotrocketscience robotrocketscience left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Reviewed by curie. Bench harness for #739's gate, plus first data. Code is clean; one substantive scope question needs resolving before merge.

Substrate review (harness + tests + data)

benchmarks/bfs_latency_v3.py (574 lines): well-structured, stdlib-only, deterministic corpus, nearest-rank percentile, per-arm warmup, hand-rolled CLI. Tests (tests/test_bfs_latency_v3.py, 169 lines, 11 tests) cover all three gate-failure paths, percentile math, corpus invariants, query precision-guard, and end-to-end smoke. CI green on both 3.12 and 3.13.

Methodology spot-checks:

  • Run order — bfs_off first → bfs_on second. Comment frames this as "user experience after a few warm queries." Per-query warmup is repeated for each arm separately, so per-query bfs_on does start cache-cold; but cross-arm artefacts (HRR persisted state, BM25F precomputed slots) do carry forward. Net effect on a 10k-belief corpus is small but it slightly favours the bfs_on arm. Worth flagging in the report or running counter-balanced (off→on, on→off, average) in a follow-up. Not blocking.

  • Sample count vs tail variance. 30 queries × 10 iterations = 300 samples. p50/p95 are statistically OK at this n; p99 = 3 samples and max is a single observation. The max bouncing 390 ms → 627 ms between Run 1 and Run 2 confirms tail variance. The tail_ratio gate (max/p50 ≤ 10) absorbs this honestly, but anyone reading the artefacts should know max is essentially anecdotal at this sample size.

  • Synthetic corpus vs production shape. Topic = 50 beliefs is uniform; production stores have skewed topic-size distribution. Acknowledged in module docstring ("labelled-corpus version would be a strictly better benchmark") — appropriate honesty.

Substantive question — Closes #739 vs Refs #739

PR body header: "Closes #739 (bench-half)." GH's Closes keyword auto-closes #739 on merge.

But #739's acceptance list has 7 items; this PR satisfies 3 of 7:

# Acceptance Status
1 Latency-band bench re-run on v3.0 stack ✓ done
2 Bench report committed under benchmarks/results/... ✓ done
3 Bench numbers meet gate conditions ✓ pass under reframed delta gate
4 is_bfs_enabled() default flipped to True ✗ explicit non-goal of this PR
5 Opt-out paths (env, TOML, kwarg) verified ✗ not done
6 README + bfs_multihop.md + INSTALL.md + CHANGELOG updated ✗ not done
7 Byte-identical regression tests updated ✗ not done

The PR body explicitly says: "This PR does not flip the default. It clears the bench gate so a follow-up PR can do the flip + paired README / docs/bfs_multihop.md / docs/INSTALL.md / CHANGELOG / regression-test updates."

Merging with Closes #739 will auto-close #739 with 4 of 7 acceptance items unfulfilled. Two clean options:

  1. Closes #739Refs #739 in the PR body, so #739 stays open until the flip-half ships.
  2. Split: file a new tracking issue for the flip-half, retarget Closes to point at it, leave #739 closed by this PR as the bench-half completion record.

I lean toward (1) — single-issue-per-feature is the established pattern (memory: aelfrice umbrella-issue pattern). But it's an operator call.

Gate-reframe ratification

The "Option 1 delta-reframed gate" is not listed in #739's "If the gate fails" menu (which only listed Options A/B/C: profile-and-fix, pair-with-#741, leave-default-OFF). The reframe is recorded as an operator decision at #739 (comment) (2026-05-14 00:37:54Z). I'm treating that as authoritative ratification.

For the record: under the original absolute gate (p50 ≤ 25 ms / p95 ≤ 100 ms), Run 2 numbers FAIL — bfs_off alone has p50=101.8ms, p95=140.1ms. The delta gate measures BFS cost rather than absolute budget. That's a defensible reframe given the v3.0 stack baseline has moved, but it's a real semantic change from the docstring acceptance in docs/bfs_multihop.md:518-525. The follow-up flip PR should update that doc passage so the gate language and the live evidence agree.

Mechanics

  • Not FF on main (PR #771 merged moments ago). Author needs git fetch && git rebase github/main.
  • All 4 commits signed (G).
  • Discretion grep on diff: 0 hits.

Verdict

Hold ready-to-merge pending:

  1. Closes #739Refs #739 decision (operator).
  2. Rebase on github/main.

Once both are clear, the harness is good to ship.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:curie:2026-05-14T01:12:56Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:curie:2026-05-14T01:44:07Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Re-claimed for review; no new review needed — the prior review at #754 (review) (head SHA d621c88, same as current head) still applies in full.

Two open items, both unchanged since 01:12 UTC:

  1. Closes #739 vs Refs #739 — operator decision. PR satisfies 3 of 7 acceptance items on Flip default: bfs_enabled (L2 graph-walk lane) on by default — gated on v3.0 latency bench re-run #739; defaults to auto-closing Flip default: bfs_enabled (L2 graph-walk lane) on by default — gated on v3.0 latency bench re-run #739 on merge unless the body is edited to Refs #739.
  2. Rebase on github/main — author task. Branch was 1 commit behind at prior review; it's now 10 commits behind (PRs [v3.x] A: meta_beliefs substrate + independent-decay engine + multi-signal collector (#480 sub-task) #755 / feat(canvas): JSON Canvas 1.0 exporter as Obsidian-mapping probe (v3.x research) #763 / refactor(store): install_meta_belief → INSERT OR IGNORE for atomic idempotence (#772) #773 merged since). git fetch && git rebase github/main then force-push.

Swapping attn:reviewattn:decisions-needed so this routes to §4 instead of re-surfacing under §1 every scan.

@robotrocketscience robotrocketscience added attn:decisions-needed Escalated to user for decision and removed attn:review Needs review (PR open, awaiting reviewer) labels May 14, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:curie:2026-05-14T01:45:14Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Operator ratification (2026-05-13)

Both open items on this PR resolved:

D1 — Gate ratification

Ratified: delta reframe accepted; gate clearance requires N≥5 replicate runs under controlled conditions.

Curie's delta reframe (delta_p50 ≤ 5ms / delta_p95 ≤ 50ms / tail ratio ≤ 10×) is the right gate shape — the v1.3 absolute band measures stack drift, not BFS cost. But fourier's Run 3 surfacing a FAIL verdict on the same code under realistic machine load (Run 2 committed PASS: delta_p95 = +35.6 ms; Run 3 same harness: delta_p95 = +75.8 ms) means a single binary verdict isn't strong enough signal to flip a load-bearing retrieval default.

Concrete requirement for the follow-up flip PR:

  • Run the bench 5× under controlled conditions — same machine, no concurrent CI, no concurrent sister sessions, no other heavy processes.
  • Report the distribution of delta_p50 / delta_p95 / delta_p99 (min / median / max across replicates), not a single value.
  • All 5 runs must land inside the band (delta_p50 ≤ 5ms, delta_p95 ≤ 50ms, tail ratio ≤ 10×) for the gate to clear.
  • If any replicate lands outside the band, either widen the band (and ratify the new band on Flip default: bfs_enabled (L2 graph-walk lane) on by default — gated on v3.0 latency bench re-run #739) or tighten the harness (warmup expansion, interleaved arm order — fourier flagged both).

This PR itself ships the harness + delta-reframed gate + the Run 2 datapoint as efd061359de4.json — no change needed to harness code. The 5-replicate run is a precondition for the flip PR, not for this PR's merge.

D2 — PR-issue mapping

Ratified: edit PR body to Refs #739 (not Closes #739).

This PR satisfies 3 of #739's 7 acceptance items (bench harness + corpus + first run). The remaining 4 (default flip + README + docs/bfs_multihop.md line 524 + docs/INSTALL.md + CHANGELOG + tests/test_retrieve_v2_compat.py byte-identical fixture update) need the follow-up flip PR. #739's title is "Flip default: bfs_enabled..." — the issue's purpose is the flip, and the flip hasn't happened. Auto-closing on this precursor merge would mis-map the work.

Author task: edit PR body, change Closes #739Refs #739.

Author follow-ups (not blocking ratification)

  1. Rebase. Branch is 10+ commits behind github/main (PRs [v3.x] A: meta_beliefs substrate + independent-decay engine + multi-signal collector (#480 sub-task) #755 / feat(canvas): JSON Canvas 1.0 exporter as Obsidian-mapping probe (v3.x research) #763 / feat(meta_beliefs): adaptive meta-belief substrate (#755, umbrella #480) #771 / refactor(store): install_meta_belief → INSERT OR IGNORE for atomic idempotence (#772) #773 / feat(bench): wire A4 continuation-fidelity bench gate for type-aware compression (#775) #776 merged since last review). git fetch && git rebase github/main && git push --force-with-lease.
  2. PR body edit. Closes #739Refs #739 per D2.
  3. Optional, non-blocking: gate.notes field for interpretive context in result JSON (fourier suggestion), and consider arm-order interleaving ([off, on, off, on, …] rather than [all off, all on]) to remove cache-warmup bias on the bfs_on arm.

Once rebased + body edited, this PR is ready for the merge-train label.

@robotrocketscience robotrocketscience added attn:unblock Needs answer from another session and removed attn:decisions-needed Escalated to user for decision labels May 14, 2026
@github-actions github-actions Bot added the attn:merge-conflict PR branch needs rebase label May 14, 2026
@github-actions

Copy link
Copy Markdown

This PR is now behind main. Rebase locally so your commit signatures stay intact:

git fetch origin && git checkout 'feat/issue-739-bfs-latency-bench' && git rebase origin/main
# resolve conflicts if any, then
git push --force-with-lease

Auto-rebase was removed because the bot has no signing key; rebasing as the bot strips author signatures and the required_signatures rule on main then blocks the merge. See #341.

Adds benchmarks/bfs_latency_v3.py — a synthetic-corpus harness that
times retrieve() with bfs_enabled True vs False on a 10k-belief /
~25k-edge store and reports p50/p95/p99 latencies plus deltas. The
harness encodes the gate conditions from #739 (p50<=25ms,
p95<=100ms, p99<=250ms, max/median<=10x) and returns non-zero on
gate failure so CI / operators can wire it into a re-run loop.

Corpus is 200 topics x 50 beliefs with deterministic intra-topic
SUPPORTS chains and cross-topic CITES edges; queries reference
named entities so the #741 prompt-shape gate runs BFS instead of
short-circuiting it. No external corpus dependency; runs on any
checkout.

Tests cover the corpus generator, query generator, percentile
helper, gate evaluator, and an end-to-end smoke run that asserts
schema + non-negative timings (not absolute numbers).

Issue #739.
Run config: 10000 beliefs / 200 topics / 20200 edges, 30 queries x 10
iterations (post-warmup) per arm, single-process local SQLite.

Result: GATE FAIL — but the fail is on the absolute thresholds set
by the v1.3 acceptance band, not on the BFS-on delta. The v3.0
default-stack baseline (bfs_off) already lands above the gate:

  bfs_off  p50=99.6ms  p95=142.4ms  p99=183.1ms
  bfs_on   p50=102.3ms p95=168.0ms  p99=198.4ms
  delta    p50=+2.8ms  p95=+25.6ms  p99=+15.3ms

BFS itself adds ~3ms median / ~25ms p95 / ~15ms p99 on top of the
v3.0 baseline. The v1.3 absolute band (p50<=25ms, p95<=100ms) is
unattainable on the v3.0 retrieval stack at this corpus size
regardless of BFS, so #739 needs an operator decision on whether
to (a) reframe the gate as a delta-vs-baseline criterion, (b)
profile and reduce v3.0 baseline latency before any default flip,
or (c) leave default-OFF and correct the README.

Corpus is synthetic; the maintainer-store equivalent (#724 lab
corpus) may yield different baseline numbers.
The original gate adapted v1.3's absolute latency band
(p50 ≤ 25 ms, p95 ≤ 100 ms). Run 1 (commit 62ce0b3) showed the
v3.0 baseline alone already exceeds those absolutes — the v1.3
numbers predate eight+ minors of HRR / BM25F / heat / clustering /
posterior-rerank work that have moved the floor.

Reframe per operator decision in PR #754: the gate measures what
flipping is_bfs_enabled() default costs, not whether v1.3's
absolute band still holds.

    delta_p50  ≤  5 ms  (bfs_on − bfs_off)
    delta_p95  ≤ 50 ms  (bfs_on − bfs_off)
    max / median ratio ≤ 10× on bfs_on (tail sanity, unchanged)

evaluate_gate() now takes both arms. Tests rewritten to cover the
three failure paths (delta_p50, delta_p95, tail) and the all-pass
case shaped around the actual Run 1 numbers.

The committed Run 1 result JSON (62ce0b3) carries the old
gate_thresholds_ms schema and will be replaced by a fresh run
emitted from this commit.
Re-run of the 10k-belief / 20.2k-edge synthetic corpus against the
delta-reframed gate from the previous commit.

| metric  | bfs_off | bfs_on  | delta    |
|---------|--------:|--------:|---------:|
| p50     | 101.8ms | 102.9ms |  +1.0 ms |
| p95     | 140.1ms | 175.8ms | +35.6 ms |
| p99     | 228.6ms | 242.9ms | +14.3 ms |
| max     | 325.2ms | 627.1ms | +301.9ms |

Gate verdict: PASS
- delta_p50 (1.0 ms) ≤ 5 ms      ✓
- delta_p95 (35.6 ms) ≤ 50 ms    ✓
- tail_ratio (6.10) ≤ 10×        ✓

The bfs_on max climbed from 390 ms (Run 1) to 627 ms here, but
tail ratio stays inside the 10× sanity bound — synthetic-corpus
single-machine timing has real run-to-run variance. p99 delta is
tighter than Run 1 (+14 ms vs +15 ms), so the rerank cost looks
stable around the tail.

The Run 1 file (62ce0b3) is dropped — it carried the old
gate_thresholds_ms schema. Future re-runs land beside efd0613
under a fresh commit-short SHA.
CodeQL alerts 406 + 407 on PR #754:
- benchmarks/bfs_latency_v3.py: 'os' imported but unused
- tests/test_bfs_latency_v3.py: 'tempfile' imported but unused

tempfile is still used in benchmarks/bfs_latency_v3.py (TemporaryDirectory
in the smoke runner) — kept.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

attn:merge-conflict PR branch needs rebase attn:unblock Needs answer from another session author-pascal Authored by parallel session pascal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flip default: bfs_enabled (L2 graph-walk lane) on by default — gated on v3.0 latency bench re-run

2 participants