Skip to content

feat(eval): kb.effectiveness -- does surfaced knowledge change outcomes? - #537

Closed
tryeverything24 wants to merge 1 commit into
vouchdev:testfrom
tryeverything24:feat/kb-effectiveness-426
Closed

feat(eval): kb.effectiveness -- does surfaced knowledge change outcomes?#537
tryeverything24 wants to merge 1 commit into
vouchdev:testfrom
tryeverything24:feat/kb-effectiveness-426

Conversation

@tryeverything24

@tryeverything24 tryeverything24 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

what

closes #426. adds a read-only, measurement-only effectiveness signal:
vouch health effectiveness / kb.effectiveness.

vouch can already measure retrieval quality (vouch eval recall,
embeddings evals) but nothing correlates "artifact X was surfaced into a
session's context pack" with what happened in that session. per approved
artifact, this reports the association between being surfaced and a
coarse session outcome derived from the audit log (a session whose events
lean claim.confirm / proposal.*.approve is "good", lean
claim.contradict / proposal.*.reject is "bad"; a tie, including no
signal at all, carries no evidence and is dropped from both sides rather
than folded into either bucket).

each artifact's good-outcome rate carries a 95% wilson interval computed
from (surfaced-and-good, surfaced-total). verdict gating is the point of
the feature: useful / harmful render only once the interval clears
the population baseline (the good-outcome rate across every session with
outcome signal in the window) and the sample meets --min-samples;
otherwise unverified (interval straddles baseline) or insufficient
(not enough sessions yet). ranking is by the conservative (worst-plausible)
lift -- how far the interval's lower bound clears baseline -- so a wide,
lucky-looking interval doesn't outrank a tighter, more defensible one.

vouch health effectiveness [--window 90d] [--min-samples 5] [--format text|json]

surfacing log (storage)

context-pack composition needed somewhere to live, so this adds a new
derived table to index_db.SCHEMA:

CREATE TABLE IF NOT EXISTS context_surfacing (
    session_id TEXT NOT NULL, kind TEXT NOT NULL, id TEXT NOT NULL, surfaced_at TEXT NOT NULL
);

recorded by kb.context (server.py + jsonl_server.py), only when a
session_id is passed -- same gating the existing salience reflex
already uses, so there's no new consent surface. it's an internal derived
cache write via index_db, never a knowledge-artifact write: no
proposal, no audit event, no yaml. cleared by index_db.reset like every
other cache table (reset's docstring calls out that, unlike the FTS/
embedding tables, this one isn't re-derivable from durable files -- a
reset genuinely loses that surfacing history rather than rebuilding it,
which is an acceptable trade for a best-effort telemetry cache).

storage-migration risk: none. an existing .vouch/ directory picks
up the new table the next time index_db.open_db runs its normal
CREATE TABLE IF NOT EXISTS schema application -- no migration step, no
version bump, nothing to backfill (there's no history to backfill from;
the table starts recording from the first kb.context call with a
session_id after upgrade).

kb.effectiveness itself only reads: store.list_sessions(),
audit.read_events(), index_db.read_context_surfacing(). it never
writes an artifact, logs an audit event, or files a proposal -- covered
by test_effectiveness_is_read_only.

surfaces touched

new kb.* method needs all four registration sites
(tests/test_capabilities.py enforces this):

  • MCP tool kb_effectiveness in server.py
  • JSONL handler _h_effectiveness in jsonl_server.py
  • capabilities.py METHODS list
  • CLI mirror vouch health effectiveness in cli.py (new health group;
    _CLI_MIRRORS["kb.effectiveness"] = "health effectiveness" since the
    default flat-command mirror rule doesn't apply)

also added to hot_memory.HOT_MEMORY_EXCLUDED (aggregated ranking, not a
claim browse -- test_hot_memory_universal_coverage catches any kb.*
method that skips this classification).

tests

new tests/test_effectiveness.py:

  • wilson interval sanity (bounds, narrows with n, --min-samples < 1
    rejected)
  • index_db.record_context_surfacing / read_context_surfacing round
    trip, and index_db.reset clearing the new table
  • insufficient-sample path: one surfaced session is real signal but
    below --min-samples, verdict stays insufficient
  • clear-signal path with a fixed clock: a 50/50 population baseline
    (5 good + 5 bad control sessions) plus an artifact surfaced only
    alongside 8/8 good outcomes (verdict useful, ci_low clears
    baseline) and one surfaced only alongside 0/8 good outcomes (verdict
    harmful, ci_high under baseline) -- both n=8 margins are wide
    enough vs. baseline=0.5 that the assertions aren't flaky
  • read-only invariant: audit log, context_surfacing, proposed/,
    sessions/ all byte-identical before/after compute()
  • the actual read-path hook: kb.context via JSONL records surfacing
    only when session_id is passed, not otherwise
  • CLI smoke test (--format json / --format text / bad --window /
    bad --min-samples) and the JSONL handler directly

validation

python -m pytest tests/ -q --ignore=tests/embeddings   # 1656 passed, 38 skipped
python -m mypy src                                      # Success: no issues found in 103 source files
python -m ruff check src tests                           # All checks passed!

make check green.

Summary by CodeRabbit

  • New Features

    • Added read-only knowledge-base effectiveness reporting through vouch health effectiveness, JSONL, and MCP interfaces.
    • Reports compare surfaced artifacts with session outcomes, including usefulness verdicts, confidence intervals, baseline rates, and lift.
    • Added configurable reporting windows, minimum sample thresholds, and JSON or text output.
    • Context-pack usage is now tracked to support artifact-level effectiveness insights.
  • Documentation

    • Documented the new effectiveness measurement capability and its reporting behavior.

vouch can measure retrieval quality but not whether surfaced knowledge
actually helps. adds a read-only, measurement-only signal: per approved
artifact, correlate it being surfaced into a session's context pack with
a coarse session outcome derived from the audit log (confirm/approve vs
contradict/reject), reported with a 95% wilson interval and a verdict
gated by statistical power -- useful/harmful render only once the
interval clears the population baseline and --min-samples is met,
otherwise unverified/insufficient.

surfacing is recorded by kb.context (only when a session_id is given,
same gating as the existing salience reflex) into a new context_surfacing
table in index_db.SCHEMA -- a rebuildable derived cache, cleared by
index_db.reset like every other cache table, not a knowledge write.
kb.effectiveness itself never writes: no artifact, no audit event, no
proposal.

exposed as vouch health effectiveness / kb.effectiveness across all four
registration sites (server.py, jsonl_server.py, capabilities.py METHODS,
cli.py), plus the hot_memory exclusion table and the cli-mirror table in
test_capabilities.py.
@github-actions github-actions Bot added docs documentation, specs, examples, and repo guidance cli command line interface mcp mcp, jsonl, and http surfaces retrieval context, search, synthesis, and evaluation tests tests and fixtures size: L 500-999 changed non-doc lines labels Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds the read-only kb.effectiveness signal, which correlates surfaced artifacts with audit-derived session outcomes using Wilson intervals. It stores surfacing metadata in SQLite and exposes reports through CLI, JSONL, and MCP interfaces.

Changes

Effectiveness measurement

Layer / File(s) Summary
Effectiveness evaluation model
src/vouch/eval/effectiveness.py, src/vouch/eval/__init__.py
Defines report dataclasses, Wilson intervals, session outcome classification, verdict gating, artifact ranking, and text/JSON serialization.
Context surfacing cache
src/vouch/index_db.py
Adds SQLite storage and read/reset functions for session-to-artifact surfacing records.
Effectiveness command and service exposure
src/vouch/capabilities.py, src/vouch/cli.py, src/vouch/jsonl_server.py, src/vouch/server.py, src/vouch/hot_memory.py
Registers kb.effectiveness, records surfaced context items, and exposes CLI, JSONL, and MCP report paths.
Effectiveness validation and release surface
tests/test_effectiveness.py, tests/test_capabilities.py, CHANGELOG.md
Tests calculations, storage, read-only behavior, integrations, CLI errors, capability parity, and documents the new signal.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ContextEndpoint
  participant IndexDB
  participant EffectivenessEndpoint
  participant AuditLog
  Client->>ContextEndpoint: request context with session_id
  ContextEndpoint->>IndexDB: record surfaced artifact pairs
  Client->>EffectivenessEndpoint: request effectiveness report
  EffectivenessEndpoint->>IndexDB: read surfaced artifacts
  EffectivenessEndpoint->>AuditLog: read session audit events
  EffectivenessEndpoint-->>Client: return effectiveness report
Loading

Possibly related PRs

  • vouchdev/vouch#241: Updates the evaluation package export surface, overlapping with this PR’s additions to src/vouch/eval/__init__.py.

Suggested labels: storage

Suggested reviewers: plind-junior, dripsmvcp

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.29% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and accurately summarizes the new kb.effectiveness evaluation feature.
Linked Issues check ✅ Passed The changes satisfy #426 by adding read-only effectiveness metrics, Wilson intervals, surfacing storage, stable CLI/JSON/MCP surfaces, and tests.
Out of Scope Changes check ✅ Passed The diff stays focused on the effectiveness signal and its supporting plumbing, with no clear unrelated code added.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/vouch/index_db.py (1)

288-295: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

read_context_surfacing always does a full, unbounded table scan.

No since/window parameter exists, so every kb.effectiveness call reads every row ever recorded, regardless of --window. This table is written on every kb.context call carrying a session_id (a hot path), so it will grow much faster than the audit log and is never pruned outside of a full index_db.reset(). Worth adding an optional since filter (WHERE surfaced_at >= ?) here and threading it through from effectiveness.compute()'s since before this becomes a real cost.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/index_db.py` around lines 288 - 295, Update read_context_surfacing
to accept an optional since timestamp and add a surfaced_at >= ? predicate when
provided, while preserving the current oldest-first ordering and unfiltered
behavior when omitted. Thread effectiveness.compute()'s since value into its
call to read_context_surfacing so kb.effectiveness only reads rows within the
requested window.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/vouch/index_db.py`:
- Around line 262-285: Make record_context_surfacing best-effort by filtering
both kind and aid, and catching sqlite3.Error around database access, logging
the failure and returning without propagating it. In src/vouch/index_db.py lines
262-285 apply the root fix; src/vouch/jsonl_server.py lines 222-233 and
src/vouch/server.py lines 305-315 require no direct change once this function is
hardened, though a shared helper may replace their duplicated call-site
handling.

In `@tests/test_effectiveness.py`:
- Around line 265-271: Extend test_jsonl_effectiveness_handler to exercise the
JSONL handle_request() path rather than only HANDLERS["kb.effectiveness"],
asserting the successful {id, ok, result} envelope and the failure {id, ok:
false, error} envelope using an invalid min_samples value. Keep the existing
effectiveness payload assertions within the success result.

---

Nitpick comments:
In `@src/vouch/index_db.py`:
- Around line 288-295: Update read_context_surfacing to accept an optional since
timestamp and add a surfaced_at >= ? predicate when provided, while preserving
the current oldest-first ordering and unfiltered behavior when omitted. Thread
effectiveness.compute()'s since value into its call to read_context_surfacing so
kb.effectiveness only reads rows within the requested window.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1964f872-39a1-4650-87d8-a883e7e6d7d2

📥 Commits

Reviewing files that changed from the base of the PR and between 1c9ca7c and 43c5646.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • src/vouch/capabilities.py
  • src/vouch/cli.py
  • src/vouch/eval/__init__.py
  • src/vouch/eval/effectiveness.py
  • src/vouch/hot_memory.py
  • src/vouch/index_db.py
  • src/vouch/jsonl_server.py
  • src/vouch/server.py
  • tests/test_capabilities.py
  • tests/test_effectiveness.py

Comment thread src/vouch/index_db.py
Comment on lines +262 to +285
def record_context_surfacing(
kb_dir: Path, *, session_id: str, items: Iterable[tuple[str, str]],
) -> None:
"""Log which artifacts were surfaced into a session's context pack.

Called from the read path (`kb.context`) only when a session_id is
given — same gating as the salience reflex. `items` is (kind, id) pairs.
Never called from `kb.effectiveness` itself, which only reads this
table: recording surfacing is a side effect of retrieval, not of
measurement.
"""
if not session_id:
return
pairs = [(kind, aid) for kind, aid in items if aid]
if not pairs:
return
ts = _dt.datetime.now(_dt.UTC).isoformat(timespec="seconds")
rows = [(session_id, kind, aid, ts) for kind, aid in pairs]
with open_db(kb_dir) as conn:
conn.executemany(
"INSERT INTO context_surfacing (session_id, kind, id, surfaced_at) "
"VALUES (?, ?, ?, ?)",
rows,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Context-surfacing telemetry write isn't actually "best-effort" — it can fail the primary kb.context response. record_context_surfacing is documented as a best-effort telemetry cache, not a knowledge write, but neither its own implementation nor either of its two call sites guards against it failing, so any error there (a kind=None NOT NULL violation, a locked db, etc.) currently turns an already-successfully-built context pack into a failed response.

  • src/vouch/index_db.py#L262-L285: filter on kind and aid (not just aid) and wrap the executemany/open_db call in try/except sqlite3.Error, logging and returning rather than propagating.
  • src/vouch/jsonl_server.py#L222-L233: once the definition is hardened this site needs no change, but until then wrap the index_db.record_context_surfacing(...) call here in try/except so a telemetry failure can't turn a successful context pack into an internal_error response.
  • src/vouch/server.py#L305-L315: same fix as jsonl_server.py — this block is a byte-for-byte duplicate of the jsonl_server.py call site, so consider extracting a single shared helper (e.g. in index_db.py) that both kb_context and _h_context call, rather than fixing the duplicated logic twice.
📍 Affects 3 files
  • src/vouch/index_db.py#L262-L285 (this comment)
  • src/vouch/jsonl_server.py#L222-L233
  • src/vouch/server.py#L305-L315
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/index_db.py` around lines 262 - 285, Make record_context_surfacing
best-effort by filtering both kind and aid, and catching sqlite3.Error around
database access, logging the failure and returning without propagating it. In
src/vouch/index_db.py lines 262-285 apply the root fix;
src/vouch/jsonl_server.py lines 222-233 and src/vouch/server.py lines 305-315
require no direct change once this function is hardened, though a shared helper
may replace their duplicated call-site handling.

Comment on lines +265 to +271
def test_jsonl_effectiveness_handler(store: KBStore, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.chdir(store.root)
from vouch.jsonl_server import HANDLERS

body = HANDLERS["kb.effectiveness"]({"window": "all", "min_samples": 1})
assert body["sessions_considered"] == 0
assert body["artifacts"] == []

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a JSONL envelope-shape assertion for kb.effectiveness.

This test exercises HANDLERS["kb.effectiveness"] directly and only checks the raw payload, not the {id, ok, result} envelope that handle_request() actually produces for JSONL clients (and the {id, ok: false, error} shape on failure, e.g. via a bad min_samples).

As per coding guidelines, "For each new kb.* method, add a test that asserts the JSONL envelope shape: {id, ok, result} on success and {id, ok: false, error} on failure."

✅ Proposed addition
 def test_jsonl_effectiveness_handler(store: KBStore, monkeypatch: pytest.MonkeyPatch) -> None:
     monkeypatch.chdir(store.root)
     from vouch.jsonl_server import HANDLERS

     body = HANDLERS["kb.effectiveness"]({"window": "all", "min_samples": 1})
     assert body["sessions_considered"] == 0
     assert body["artifacts"] == []
+
+
+def test_jsonl_effectiveness_envelope_shape(store: KBStore, monkeypatch: pytest.MonkeyPatch) -> None:
+    monkeypatch.chdir(store.root)
+    from vouch.jsonl_server import handle_request
+
+    ok = handle_request({"id": 1, "method": "kb.effectiveness", "params": {"window": "all"}})
+    assert ok["id"] == 1 and ok["ok"] is True and "result" in ok
+
+    bad = handle_request(
+        {"id": 2, "method": "kb.effectiveness", "params": {"min_samples": 0}}
+    )
+    assert bad["id"] == 2 and bad["ok"] is False and "error" in bad
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_jsonl_effectiveness_handler(store: KBStore, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.chdir(store.root)
from vouch.jsonl_server import HANDLERS
body = HANDLERS["kb.effectiveness"]({"window": "all", "min_samples": 1})
assert body["sessions_considered"] == 0
assert body["artifacts"] == []
def test_jsonl_effectiveness_handler(store: KBStore, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.chdir(store.root)
from vouch.jsonl_server import HANDLERS
body = HANDLERS["kb.effectiveness"]({"window": "all", "min_samples": 1})
assert body["sessions_considered"] == 0
assert body["artifacts"] == []
def test_jsonl_effectiveness_envelope_shape(store: KBStore, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.chdir(store.root)
from vouch.jsonl_server import handle_request
ok = handle_request({"id": 1, "method": "kb.effectiveness", "params": {"window": "all"}})
assert ok["id"] == 1 and ok["ok"] is True and "result" in ok
bad = handle_request(
{"id": 2, "method": "kb.effectiveness", "params": {"min_samples": 0}}
)
assert bad["id"] == 2 and bad["ok"] is False and "error" in bad
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_effectiveness.py` around lines 265 - 271, Extend
test_jsonl_effectiveness_handler to exercise the JSONL handle_request() path
rather than only HANDLERS["kb.effectiveness"], asserting the successful {id, ok,
result} envelope and the failure {id, ok: false, error} envelope using an
invalid min_samples value. Keep the existing effectiveness payload assertions
within the success result.

Source: Coding guidelines

@github-actions

Copy link
Copy Markdown
Contributor

closing automatically: CodeRabbit requested changes and this pr has had no new commits for 2 days. the feedback still stands — push a fix and reopen this pr (or open a fresh one) and it will be reviewed again.

@github-actions github-actions Bot closed this Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli command line interface docs documentation, specs, examples, and repo guidance mcp mcp, jsonl, and http surfaces retrieval context, search, synthesis, and evaluation size: L 500-999 changed non-doc lines tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant