Skip to content

feat(hook): coverage line surfaces L1 retrieval/index asymmetry (#857) - #863

Merged
github-actions[bot] merged 5 commits into
mainfrom
feat/issue-857-hook-coverage-line
May 18, 2026
Merged

feat(hook): coverage line surfaces L1 retrieval/index asymmetry (#857)#863
github-actions[bot] merged 5 commits into
mainfrom
feat/issue-857-hook-coverage-line

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented May 18, 2026

Copy link
Copy Markdown
Owner

Closes #857. Sub-issue of #855 — defect 2 (retrieval vs index recall asymmetry).

What this ships

When the per-turn memory hook truncates L1 keyword-match candidates because of the token budget, it now appends one coverage line after the </aelfrice-memory> block:

retrieved N of M matching beliefs for "<topic>"; run `aelf search <topic>` to see the rest.

N is what was injected this turn; M = N + (l1_candidates − l1_packed) — total surfaced plus the count cut by the budget cap. The line is suppressed when nothing was cut (delta == 0), so existing turns where every L1 candidate already fit emit byte-identical output.

The topic in the line is the user's own prompt (truncated at 60 chars for the display portion, kept verbatim for the search hint). The line never echoes belief content, IDs, or any data from the store — only counts plus the prompt the user already typed.

Why this formula

First pass computed M = locked + l2.5 + l1_candidates and compared against len(hits). That breaks the moment another lane (BFS hops, future expansion modes) pads n_injected above m_total: the comparison short-circuits and the line silently disappears even when L1 was genuinely trimmed. The delta formulation is invariant under non-L1 lanes because it only measures what L1 dropped.

The regression test test_coverage_line_fires_when_bfs_pads_n_above_l1_candidates exercises exactly that case (locked=2, l25=3, l1=5, l1_candidates=10, n=15retrieved 15 of 20).

Surface touched

  • src/aelfrice/retrieval.pyLaneTelemetry gains l1_candidates: int. _reset_last_telemetry() helper added so the hook can zero the per-process snapshot before each retrieval (defensive for the mocked-_retrieve path; production retrieval overwrites it immediately).
  • src/aelfrice/hook.py_coverage_line() (new), called in user_prompt_submit after _format_hits builds the block. The existing last_lane_telemetry() read was moved earlier in the function so the coverage suffix lands inside the same write as the audit body.
  • tests/test_hook_coverage_line.py — 9 tests: unit (M==N, M>N, M==0, locked/l25 in counts, BFS-padding regression, no-belief-content, long-prompt truncation, short-prompt verbatim), integration (budget-truncates, all-fit, no-hits).
  • CHANGELOG/v3.md — Unreleased entry.

Verification

  • uv run pytest -x — 4191 passed, 62 skipped, 75 xfailed (was 4190; delta is the new regression test).
  • Discretion grep on git diff github/main...HEAD — empty.
  • All five commits signed.

Acceptance items (from #857)

  • Hook computes M = total candidate matches alongside N = surfaced.
  • Hook output includes the coverage line when M > N (and only then).
  • Tests: M == N (line omitted), M > N (line shown), M == 0 (no-op edge case).
  • Coverage line text is privacy-safe — only counts and the topic the user already typed.

Summary by Sourcery

Add a per-turn coverage line in the user prompt hook to surface when keyword-matching beliefs were dropped due to L1 token-budget limits.

New Features:

  • Append a coverage line after memory output when L1 candidates are truncated, including retrieved vs total match counts and a search hint based on the user prompt.

Enhancements:

  • Extend LaneTelemetry with L1 candidate counts and expose a reset helper so hook-side telemetry reads reflect the current retrieval turn.
  • Adjust the user prompt hook to reset telemetry before retrieval and to include the coverage line within the same write as the memory block.

Documentation:

  • Document the new hook coverage line behavior in the v3 changelog.

Tests:

  • Add unit and integration tests covering coverage-line emission/omission scenarios, prompt truncation behavior, privacy guarantees, and BFS-padding regression.

Summary by CodeRabbit

Release Notes

New Features

  • Added a coverage summary that appears after belief retrieval when results are truncated due to token limits. The message displays how many matching beliefs were found versus how many were included, providing the search topic and suggesting aelf search to view all results.

Review Change Stack

@robotrocketscience robotrocketscience added the author-Kulili PR coordination mutex label May 18, 2026
@sourcery-ai

sourcery-ai Bot commented May 18, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements a coverage-line feature in the UserPromptSubmit hook that surfaces when L1 keyword-match candidates were trimmed by the token budget, backed by new LaneTelemetry plumbing, a reset helper for telemetry, and focused unit/integration tests plus changelog entry.

Sequence diagram for coverage line in user_prompt_submit

sequenceDiagram
    actor User
    participant Hook as user_prompt_submit
    participant Retrieval as _retrieve
    participant Tel as LaneTelemetry_store

    User->>Hook: user_prompt_submit(prompt, budget)
    alt not gate_skip
        Hook->>Tel: _reset_last_telemetry(LaneTelemetry())
        Hook->>Retrieval: _retrieve(prompt, budget)
        Retrieval-->>Hook: hits
        Hook->>Tel: last_lane_telemetry()
        Tel-->>Hook: LaneTelemetry
        Hook->>Hook: _coverage_line(len(hits), tel, prompt)
        Hook-->>User: body (+ coverage when delta>0)
    else gate_skip
        Hook-->>User: no hits, no coverage line
    end
Loading

File-Level Changes

Change Details Files
Add coverage-line suffix generation in the UserPromptSubmit hook when L1 candidates are trimmed by the token budget, ensuring behavior is invariant to non-L1 lanes and privacy-safe.
  • Introduce _coverage_line(n_injected, tel, prompt) helper that computes delta = l1_candidates - l1, derives M = n_injected + delta, and returns a single-line suffix or empty string when no trim occurred.
  • Wire _coverage_line into user_prompt_submit so that it is called after _format_hits* builds the memory block, appending the coverage line to the same write when delta > 0.
  • Move the last_lane_telemetry() read earlier in user_prompt_submit so the coverage line uses the same telemetry snapshot as the audit record, and ensure the coverage line only references counts plus the user prompt (with 60-char truncation for display and no belief content).
src/aelfrice/hook.py
Extend retrieval telemetry with L1 candidate counts and provide a way to safely reset the global telemetry snapshot before each retrieval.
  • Add l1_candidates: int field to LaneTelemetry with documentation clarifying it counts all L1 hits post-scoring/dedup and pre-budget trim.
  • Populate l1_candidates in both retrieval code paths where LaneTelemetry is constructed, using len(l1) to record total L1 candidate count.
  • Introduce _reset_last_telemetry(tel: LaneTelemetry) to overwrite the process-level _LAST_TELEMETRY snapshot, and call it from the hook before _retrieve to avoid stale telemetry influencing coverage-line logic (especially under mocked _retrieve in tests).
src/aelfrice/retrieval.py
src/aelfrice/hook.py
Add targeted unit and integration tests for the coverage line behavior and its interaction with token budgets and BFS padding.
  • Create unit tests for _coverage_line that cover M == N (line omitted), M > N (line present), M == 0, inclusion of locked/L2.5 counts, BFS-padding regression where n_injected exceeds L1 candidates, prompt truncation at 60 characters, and privacy (no belief content leakage).
  • Add integration tests for user_prompt_submit that seed a temp DB, exercise paths where token budget truncates candidates (line present), all candidates fit (line omitted), and no hits are returned (no output at all).
  • Introduce small helpers in the test module to create beliefs, seed the DB, craft hook payloads, and manipulate AELFRICE_DB for isolation.
tests/test_hook_coverage_line.py
Document the new coverage-line behavior in the v3 changelog. CHANGELOG/v3.md

Assessment against linked issues

Issue Objective Addressed Explanation
#857 Have the per-turn memory hook compute M = total candidate matches alongside N = surfaced, and append a coverage line retrieved N of M ... only when M > N (no line when M == N or M == 0).
#857 Add tests covering coverage-line behavior for M == N (line omitted), M > N (line shown), and M == 0 (no-op / no line), including relevant integration paths through the hook.
#857 Ensure the coverage line text is privacy-safe, containing only counts and the user’s own topic/prompt (no belief content or other store data).

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR implements a retrieval coverage reporting feature for the memory hook. It adds telemetry tracking of L1 candidate counts before token-budget trimming, integrates hook logic to reset and read that state per turn, formats a coverage suffix when candidates are dropped, and provides comprehensive unit and integration tests to validate the behavior.

Changes

Coverage Line Feature

Layer / File(s) Summary
Telemetry instrumentation for L1 candidate counts
src/aelfrice/retrieval.py
LaneTelemetry gains l1_candidates field to track the count of L1 hits before token-budget trimming. _reset_last_telemetry() helper resets the process-level snapshot. Both retrieve() and retrieve_with_tiers() populate the field from the L1 list length.
Hook coverage line integration and formatting
src/aelfrice/hook.py
Hook resets telemetry before retrieval to ensure fresh state per turn. _coverage_line() helper computes "retrieved N of M matching beliefs" suffix from telemetry snapshot and prompt, returning empty string when no trimming occurred. The suffix is appended to rendered body only if non-empty, and audit record is written after integration.
Unit tests for coverage line formatting
tests/test_hook_coverage_line.py
Tests verify _coverage_line logic: omission when M == N or M == 0, inclusion when M > N with correct counts and topic label, no belief content leakage, topic truncation for long prompts, verbatim preservation for short prompts, and correct behavior under BFS padding.
Integration tests for hook coverage behavior
tests/test_hook_coverage_line.py
Integration tests seed memory store, run user_prompt_submit, and verify stdout: coverage line present when budget truncates, omitted when all fit, absent when no hits match.
Changelog documentation
CHANGELOG/v3.md
Unreleased changelog entry describes the new coverage line feature, implementation approach, and test coverage.

🎯 2 (Simple) | ⏱️ ~12 minutes

author-Setr

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a coverage line to the hook that surfaces L1 retrieval/index asymmetry, which aligns with the changeset across all four files.
Description check ✅ Passed The PR description is comprehensive and well-structured, including summary, linked issues, type of change checkbox, verification steps, test plan, and detailed notes. It exceeds the template requirements.
Linked Issues check ✅ Passed The PR fully satisfies all acceptance items from #857: computes M (total candidates) alongside N (surfaced), emits coverage line only when M > N, includes tests for M==N/M>N/M==0, and ensures privacy by excluding belief content.
Out of Scope Changes check ✅ Passed All changes are directly scoped to #857: adding l1_candidates telemetry, _coverage_line helper, resetting telemetry before retrieval, comprehensive test coverage, and changelog documentation. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-857-hook-coverage-line

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

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown

PR-size soft cap

This PR is over the advisory size threshold:

  • 309 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.

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

Hey - I've found 2 issues, and left some high level feedback:

  • The implementation of _coverage_line truncates search_topic as well as the display text, which conflicts with the spec/CHANGELOG promise that the search hint uses the verbatim topic; if that contract matters, consider keeping search_topic untruncated while only truncating display_topic.
  • Hook code now depends on the private _reset_last_telemetry symbol from retrieval.py; if this reset behavior is intended to be stable, consider either dropping the leading underscore or exposing a small public helper to avoid cross-module reliance on a private API.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The implementation of `_coverage_line` truncates `search_topic` as well as the display text, which conflicts with the spec/CHANGELOG promise that the search hint uses the verbatim topic; if that contract matters, consider keeping `search_topic` untruncated while only truncating `display_topic`.
- Hook code now depends on the private `_reset_last_telemetry` symbol from `retrieval.py`; if this reset behavior is intended to be stable, consider either dropping the leading underscore or exposing a small public helper to avoid cross-module reliance on a private API.

## Individual Comments

### Comment 1
<location path="src/aelfrice/hook.py" line_range="1489-1490" />
<code_context>
+    search_topic = raw_topic[:_COVERAGE_TOPIC_MAX_CHARS] if truncated else raw_topic
+    display_topic = search_topic + "…" if truncated else raw_topic
+    return (
+        f"retrieved {n_injected} of {m_total} matching beliefs for "
+        f'"{display_topic}"; run `aelf search {search_topic}` to see the rest.\n'
+    )
+
</code_context>
<issue_to_address>
**🚨 suggestion (security):** The suggested `aelf search` command may be unsafe or confusing for prompts with shell-sensitive characters.

Because `{search_topic}` is interpolated directly into a shell command, any quotes, backticks, `$`, or other metacharacters in the prompt could change the meaning of the command if users copy-paste it. Consider either quoting and escaping `{search_topic}` or generating a shell-escaped version (e.g., via `shlex.quote`) so the suggested command is safe for arbitrary input.

Suggested implementation:

```python
    raw_topic = prompt.strip()
    truncated = len(raw_topic) > _COVERAGE_TOPIC_MAX_CHARS
    search_topic = raw_topic[:_COVERAGE_TOPIC_MAX_CHARS] if truncated else raw_topic
    display_topic = search_topic + "" if truncated else raw_topic
    shell_search_topic = shlex.quote(search_topic)
    return (
        f"retrieved {n_injected} of {m_total} matching beliefs for "
        f'"{display_topic}"; run `aelf search {shell_search_topic}` to see the rest.\n'
    )

```

1. Add `import shlex` near the top of `src/aelfrice/hook.py` alongside the other imports, so `shlex.quote` is available.
2. If this module is used in non-POSIX environments and you need platform-specific handling, you may want to document that the suggested command assumes a POSIX-like shell.
</issue_to_address>

### Comment 2
<location path="src/aelfrice/hook.py" line_range="1468" />
<code_context>
+
+def _coverage_line(
+    n_injected: int,
+    tel: Any,
+    prompt: str,
+) -> str:
</code_context>
<issue_to_address>
**suggestion:** Using `Any` for the telemetry parameter obscures the contract of `_coverage_line`.

Because this helper accesses `tel.l1_candidates` and `tel.l1`, please replace `Any` with a concrete type (e.g., `LaneTelemetry`) or a protocol defining those attributes so misuse is caught by the type checker instead of at runtime.

Suggested implementation:

```python
class SupportsL1Telemetry(Protocol):
    """Telemetry required by _coverage_line.

    Any telemetry object providing these attributes can be passed to
    `_coverage_line`, allowing static type checkers to validate usage.
    """

    l1_candidates: int
    l1: int


_COVERAGE_TOPIC_MAX_CHARS: Final[int] = 60

```

```python
def _coverage_line(
    n_injected: int,
    tel: SupportsL1Telemetry,
    prompt: str,
) -> str:

```

1. Ensure `Protocol` is imported in this module, e.g., adjust the existing typing imports to include it:

   `from typing import Any, Final, Protocol`

   (Keep `Any` if used elsewhere; remove it only if no longer needed.)
2. If your codebase prefers a different naming convention, you may want to rename `SupportsL1Telemetry` to align with existing protocol/type names.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/aelfrice/hook.py
Comment thread src/aelfrice/hook.py
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:idnn:2026-05-18T17:19:36Z]

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

Copy link
Copy Markdown
Owner Author

[release:review:idnn:2026-05-18T17:21:52Z]

@github-actions

Copy link
Copy Markdown

merge-train: blocked

1 review thread(s) are unresolved on these files: src/aelfrice/hook.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 18, 2026
@robotrocketscience robotrocketscience added the attn:review Needs review (PR open, awaiting reviewer) label May 18, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:idnn:2026-05-18T19:48:35Z]

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

Review against #857 acceptance items

All four acceptance items satisfied:

  • Hook computes M alongside N — via new LaneTelemetry.l1_candidates, populated at both retrieve() and retrieve_with_tiers() callsites in retrieval.py:2650, 2896.
  • Coverage line appended when M > N_coverage_line returns the line only when delta = l1_candidates - l1 > 0; empty otherwise. Appended to body after the closing </aelfrice-memory> tag, before sout.write.
  • Teststests/test_hook_coverage_line.py covers M==N omitted, M>N shown, M==0 no-op; plus locked+L25 counted, long-prompt truncated, short-prompt verbatim, no belief content, and a BFS-pad regression. Integration tests via user_prompt_submit exercise the live hook path.
  • Privacy-safe text — line contains only counts and the user's own prompt keywords. test_coverage_line_no_belief_content pins this.

Design notes

_reset_last_telemetry pre-retrieval reset — solving stale-snapshot leakage from mocked _retrieve test paths by zeroing before the call is the right shape. Real _retrieve overwrites _LAST_TELEMETRY at the end of _cost so the reset is invisible in production; only test paths that mock _retrieve see the zero state, which is precisely the behavior wanted (no spurious coverage line on mocked retrievals). Sourcery's "cross-module dependence on a private symbol" point is mechanical to address (drop the underscore) but the use is constrained to one in-package call site, so the underscore is fine as a "do not depend on from outside aelfrice" signal.

L1-delta formulationM = n_injected + (l1_candidates − l1) is invariant under BFS / future-lane padding of n_injected, which is correct for trim detection. The BFS regression test pinning "15 of 20" demonstrates this.

Findings

1. CHANGELOG wording vs implementation — minor mismatch

CHANGELOG/v3.md line for this feature says "(truncated to 60 chars for display)" but the implementation truncates both display_topic and search_topic:

search_topic = raw_topic[:_COVERAGE_TOPIC_MAX_CHARS] if truncated else raw_topic
display_topic = search_topic + "…" if truncated else raw_topic

So the suggested aelf search <topic> command also uses the truncated form, not just the displayed quote. Two options:

  • (a) Tighten CHANGELOG: drop "for display" → "(truncated to 60 chars)".
  • (b) Pass raw_topic (full) to aelf search and truncate only display_topic.

Functionally equivalent for BM25 recall, but (a) or (b) closes the spec/impl gap. Sourcery flagged the same point.

2. test_coverage_line_counts_include_locked_and_l25 docstring is misleading

def test_coverage_line_counts_include_locked_and_l25() -> None:
    """M sums locked + l25 + l1_candidates; N is total injected."""

The implementation computes M = n_injected + (l1_candidates − l1), not "locked + l25 + l1_candidates." The test happens to verify M=5 because in this setup 3 + (3 − 1) = 5, which equals 1 + 1 + 3. A future reader reading the docstring would extract a wrong formula. Suggest re-wording to e.g. "L1 trim delta is reported even when locked / L25 contributed to N."

3. BFS-padded N vs spec definition of M (informational)

#857's spec defines M = total beliefs matching the same keyword scoring at any non-zero relevance. With BFS hops added to n_injected, the reported "M" includes those BFS-pad-derived items: e.g. n_injected=15 (5 BFS) + delta=5 → "15 of 20". A user running aelf search <topic> after seeing "15 of 20" will get matches without BFS expansion and may see counts that don't reconcile.

The behavior is consistent and the trim signal surfaces correctly — this is the feature's primary value. But the reported numbers aren't literally "matches at any non-zero relevance" from the spec; they're "injected (incl. BFS) + L1 trim." Worth a small CHANGELOG / docstring note that the count reflects the L1-budget asymmetry, not a literal aelf search row count. Non-blocking.

4. Sourcery's shlex.quote(search_topic) suggestion — operator call

The line is a display hint the user reads; it is not exec'd. A copy-paste attack would require the user to type adversarial input into their own prompt and then paste their own bytes into their own shell. Real but narrow. shlex.quote is mechanical defense-in-depth; the trade-off is uglier output for the common case (no special chars). Defer to operator.

CI / merge readiness

  • pytest 3.12 / 3.13: SUCCESS
  • bench-smoke: SUCCESS
  • CodeQL (python / actions): SUCCESS
  • All Staging Gate checks (secrets / pattern / history / release-docs / commit-msg / pr-title / pr-body-issue-link): SUCCESS
  • migration-policy-check: SUCCESS
  • deptry / vulture / typos: SUCCESS
  • size-check (final commit): SUCCESS
  • Mergeable: true

Discretion grep on the diff is clean.

Recommendation

Findings #1 and #2 are doc-only and would be nicer to fix before merge (one CHANGELOG word + one test docstring). #3 is a doc/spec reconciliation that can land later. #4 is operator judgment. The implementation itself is correct against the acceptance items and CI is green.

Not adding ready-to-merge — leaving the disposition on #1/#2/#4 to the operator.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:idnn:2026-05-18T19:51:40Z]

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

Copy link
Copy Markdown
Owner Author

[claim:review:Idnn:2026-05-18T22:24:28Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Disposition (Idnn)

Operator delegated the call. Disposition: approve as-is, merge-train.

Walking the four findings from the prior review:

  • Add CI workflows, scan config, and README claim alignment #1 CHANGELOG "(truncated to 60 chars for display)" vs impl — cosmetic doc imprecision. The impl truncates both display_topic and search_topic at 60 chars; the CHANGELOG phrasing reads as if only the display side is truncated. Functionally indifferent — BM25 keyword scoring is largely insensitive to trailing tokens past 60 chars for the prompts most likely to trigger this line. Non-blocking; trivial to tighten in any future docs sweep on CHANGELOG/v3.md line 22.
  • Add CI workflows, scan config, and align README #2 Test docstring at test_coverage_line_counts_include_locked_and_l25 — same shape: text says "M sums locked + l25 + l1_candidates" but the implemented formula is M = n_injected + (l1_candidates − l1). Both arithmetics happen to yield M=5 in this test's setup, so the test pins the right behavior; only the docstring misleads. Non-blocking.
  • ci: history-scan should see all branches #3 BFS-pad numerics caveat — informational only, prior reviewer already marked non-blocking. The line surfaces the L1-trim signal correctly; the literal aelf search reconciliation between displayed count and shell output is a separate UX consideration.
  • feat: add Belief/Edge dataclasses and config module #4 shlex.quote(search_topic) (Sourcery) — declining. The line is display, not exec. The "attack" path requires the user to type adversarial content into their own prompt and then paste it back into their own shell, which is self-targeted by construction. Quoting every prompt would make the common case (plain keywords) uglier than it needs to be. Not warranted.

Acceptance items from #857 all satisfied (M alongside N, line gated on M>N, edge cases tested, no belief content surfaced). CI green across pytest 3.12/3.13, bench-smoke, CodeQL, Staging Gate, deptry/vulture/typos, size-check. Mergeable: true.

Findings #1 and #2 can be picked up by anyone in a 2-line doc-tidy follow-up if the cosmetics matter — flagging here so they don't get lost.

Adding ready-to-merge.

@robotrocketscience robotrocketscience added ready-to-merge Trigger merge-train: FF main to this PR's head and removed attn:decisions-needed Escalated to user for decision labels May 18, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Idnn:2026-05-18T22:27:04Z]

@github-actions

Copy link
Copy Markdown

merge-train: blocked

branch is not fast-forward on main (branch base 89b65d5530e1f9d7de0522d5ff0b853d58378deb, current main 81e7c618f386157eb543dc9adf63b32f999c0842). 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 18, 2026
After each UserPromptSubmit retrieval, emit a one-line suffix outside
the </aelfrice-memory> block when M > N (M = total L1 candidates before
token-budget trim, N = injected). Format:

  retrieved N of M matching beliefs for "topic"; run `aelf search topic` to see the rest.

Mechanism:
- New `l1_candidates` field on `LaneTelemetry` (populated in both
  `retrieve()` and `retrieve_with_tiers()`) tracks the pre-budget L1
  candidate count alongside the existing packed `l1` count.
- New `_reset_last_telemetry()` in `retrieval.py` lets the hook reset
  the process-level snapshot before each UPS retrieval so mocked
  `_retrieve` callers in tests see a clean zero-state, not stale data.
- New `_coverage_line(n_injected, tel, prompt)` in `hook.py` computes
  M = tel.locked + tel.l25 + tel.l1_candidates and returns the line
  when M > N. Prompt is truncated to 60 chars for display and the
  search command argument. No belief content is included.
- `user_prompt_submit` calls `_reset_last_telemetry` before `_retrieve`,
  reads `last_lane_telemetry()` after, and appends the coverage line to
  `body` when non-empty, before `sout.write(body)`.
10 tests in tests/test_hook_coverage_line.py covering all three
acceptance items from the spec:

Unit tests on _coverage_line():
- M == N: line omitted
- M > N: line emitted with counts and prompt topic
- M == 0: no-op edge case
- locked + L2.5 counted in M
- no belief content in coverage line
- long prompt truncated at 60 chars
- short prompt preserved verbatim

Integration tests via user_prompt_submit():
- token-budget truncation triggers line (M > N)
- all candidates fit → line omitted (M == N)
- no hits → empty output (M == 0)
…adding

The old formula compared m_total (locked+l25+l1_candidates) against
n_injected, but n_injected includes BFS hops that are not counted in
m_total. When BFS pads n_injected to match or exceed m_total the guard
fires and the coverage line is suppressed even though L1 candidates were
genuinely trimmed by the token budget.

New formula: delta = l1_candidates - l1. When delta <= 0 nothing was
cut; line is omitted. When delta > 0, m_total = n_injected + delta.
This is independent of any non-L1 surfaced lane.
…rage line

Proves that when BFS hops push n_injected above l1_candidates but L1
was still trimmed by budget, the coverage line fires correctly and
reports the right counts.
@robotrocketscience
robotrocketscience force-pushed the feat/issue-857-hook-coverage-line branch from 0bd104d to 934559d Compare May 18, 2026 22:29
@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label May 18, 2026
@github-actions

Copy link
Copy Markdown

merge-train: blocked

1 review thread(s) are unresolved on these files: src/aelfrice/hook.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 18, 2026
@robotrocketscience robotrocketscience added the ready-to-merge Trigger merge-train: FF main to this PR's head label May 18, 2026
@github-actions
github-actions Bot merged commit 934559d into main May 18, 2026
37 of 39 checks passed
@github-actions github-actions Bot removed the ready-to-merge Trigger merge-train: FF main to this PR's head label May 18, 2026
@github-actions

Copy link
Copy Markdown

merge-train: merged 934559dmain via FF push.

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

Labels

author-Kulili PR coordination mutex

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(hook): report retrieval coverage to surface hook/index recall asymmetry (#855 defect 2)

1 participant