Skip to content

feat(rating): evidence-anchored ratings — write the receipt before the class#85

Merged
emp3thy merged 13 commits into
mainfrom
feat/evidence-ratings
Jul 24, 2026
Merged

feat(rating): evidence-anchored ratings — write the receipt before the class#85
emp3thy merged 13 commits into
mainfrom
feat/evidence-ratings

Conversation

@emp3thy

@emp3thy emp3thy commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Summary

Implements §4 of the retrieval-quality design (evidence-anchored ratings) with the post-PR-D deltas.

Problem: rating variance is the noise floor under every ranking signal — byte-identical memory sets were rated shaped in one session and ignored in another. Nothing anchored a non-ignored rating to anything observable.

Mechanism — evidence-first: the rater writes ONE line of evidence (what the memory changed, or a quote) before choosing a class; nothing to point at ⇒ ignored. Enforced server-side: cited/shaped/misled/overlooked without a non-empty (≤500 chars, trimmed) evidence string reject the whole batch pre-savepoint, loudly.

  • Migration 0016: nullable evidence TEXT on session_memory_exposure (additive; historical rows untouched).
  • Service: _validate_evidence in the existing validate-before-savepoint pass; evidence stamped in the same UPDATE as classification.
  • Tools: memory.credit requires evidence in-schema; apply_session_ratings items carry it optional-at-wire with service validation.
  • Skill + Stop directive + contextual footer rewritten evidence-first (incl. scoping the class-uncertainty hedge so it can't reverse into ignored post-evidence).
  • UI: "Rating evidence" history (last 10, class chips) on reflection and semantic drawers; evidence column on /diagnostics. Project-native CSS (incl. the previously-undefined .chip.outcome-no_outcome).
  • Audit-only: no scoring/retrieval path reads evidence — verified by review grep.
  • Hardening en route: conftest now strips live-deployment env vars from test shells (3rd occurrence of that leak class); backend credit_one evidence threading; test Optionals narrowed.

Validation

  • Full suite 1627 passed / 22 skipped / 0 failed; pyright 0 errors.
  • 6 task reviews (2 NOT-approved rounds fixed + re-approved: skill hedge contradiction, stale website signature); final whole-branch review: 0 Critical / 0 Important, 5 Minors carried with rationale.
  • End-to-end proof: dispatch-level test drives the production CallToolRequest handler; evidence lands in the DB and renders in the drawer.

Deploy

Merge → checkout main + pull. Migration 0016 applies on next MCP server start (may already be applied — editable working tree). The user-level skill junction picks up the SKILL.md rewrite automatically. First live RATE_MEMORIES sweep after restart exercises the contract.

🤖 Generated with Claude Code

emp3thy and others added 12 commits July 24, 2026 10:35
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Non-ignored classes (cited/shaped/misled/overlooked) now require a
trimmed, non-empty, <=500-char evidence line, enforced by
_validate_evidence() in both apply_session_ratings (whole-batch
validation before the savepoint) and credit_one. The exposure-row
stamp UPDATE now also writes evidence.

Deviation: credit_one's evidence param defaults to None (not a
required kwarg) as a compat shim, since the MCP memory.credit handler
doesn't forward evidence yet (Task 3). This makes memory.credit
cleanly ValueError on non-ignored classes until Task 3 wires it
through, instead of TypeErroring. Adjusted the brief's
test_credit_requires_evidence and three pre-existing
tests/mcp/test_rating_tools.py credit-dispatch tests to match. Also
added an optional "evidence" property to the apply_session_ratings
wire schema in mcp/tools.py (additionalProperties:false was rejecting
the key outright). Full details in .superpowers/sdd/task-2-report.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live settings.json env (INJECT_MODE=deferred, embeddings backend) leaks
into test runs; third occurrence of this leak class.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
memory.credit's inputSchema now requires evidence (maxLength 500) and
forwards it to MemoryRatingService.credit_one, closing the gap where
credit degraded with a clean ValueError because the handler dropped
the arg. apply_session_ratings' per-item evidence property gains the
maxLength cap and both descriptions state the evidence-first rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… Optionals

SqliteBackend.credit_one and the StorageBackend protocol never gained the
evidence kwarg Task 2/3 added to MemoryRatingService, so callers going
through the storage interface directly (not the MCP handler) hit the
evidence requirement with no way to satisfy it. Threads evidence: str |
None = None through protocol.py, sqlite.py (forwarded to the service),
and agentcore.py (accepted for signature parity only - agentcore has no
evidence storage and never validated it). Fixes
test_credit_one_for_missing_memory_returns_skip, whose actual subject is
the memory_missing skip path, by giving it a valid evidence string.

Also narrows tool.description: str | None before "in" checks in
test_rating_tools.py's schema-description tests (pyright reportOperatorIssue).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Update website/architecture.md (Self-rating loop section) and
website/mcp-tools.md (Rating tools section) to describe the
evidence-anchored rating requirement introduced earlier in this branch:

- memory.credit and memory.apply_session_ratings require a one-line
  evidence statement for every non-ignored class (cited/shaped/misled/
  overlooked); ignored is exempt and evidence is optional there.
- MemoryRatingService validates the whole batch before writing any row,
  so apply_session_ratings rejects the entire batch loudly if any
  non-ignored entry is missing evidence.
- Evidence is stored on session_memory_exposure.evidence (migration
  0016, nullable; historical rows are untouched) and is audit-only --
  no scoring reads it.
- Documented the "Rating evidence" history (last 10) surfaced in the
  reflection/semantic drawers and the evidence column on /diagnostics.
- Called out the naming distinction from reflections.evidence_count /
  evidence_count on semantic memories, which count synthesis source
  observations and are unrelated to rating evidence.

No code changes: pyright (0 errors) and the full pytest suite
(1627 passed, 22 skipped, 26 deselected) both ran clean with no
stragglers needing fixes.
…ion prose

Code review caught a stale memory_credit(kind, id, 'cited'|'shaped'|'misled')
signature left over from before the evidence requirement, in the Contextual
injection section describing the retrieve_relevant footer text. Actual
footer (better_memory/services/relevant.py ~298-303):
memory_credit(kind, id, class, evidence) - include a one-line evidence
statement. Updated the sentence to match and mention the evidence line.
Grepped architecture.md again for memory_credit -- no other stale instance.

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

🔴 Claude BugBot Analysis

Found 1 potential bug in this PR.

medium: 1

One genuine bug found: the memory.credit handler forwards the new evidence argument to the local sqlite rating service but forgets to forward it on the remote/AgentCore backend branch, silently dropping caller-supplied evidence for that backend.

Comment thread better_memory/mcp/handlers/sessions.py
Addresses PR #85 review: the agentcore branch of the credit handler
silently discarded caller-supplied evidence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

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

🟢 Claude BugBot Analysis

Reviewed all production code changes in the diff (migration 0016, MemoryRatingService evidence validation, MCP tool schemas/handlers, storage backends, UI queries/routes/templates/CSS). The evidence-requirement feature is implemented consistently end-to-end with no logic errors, null dereferences, injection risks, or off-by-one issues found.

No bugs were detected in this PR.

@emp3thy
emp3thy merged commit 0d18089 into main Jul 24, 2026
3 checks passed
@emp3thy
emp3thy deleted the feat/evidence-ratings branch July 24, 2026 11:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant