Skip to content

fix(recall): gate query-independent scoring on topical evidence within tag scope (#130) - #186

Merged
jack-arturo merged 4 commits into
developfrom
fix/130-relevance-gate
Jun 11, 2026
Merged

jack-arturo merged 4 commits into
developfrom
fix/130-relevance-gate

Conversation

@jack-arturo

Copy link
Copy Markdown
Member

Summary

Stacked on #185.

Corrected root cause for #130. The issue hypothesized tags act as score boosters that outrank scoped results. Code inspection shows user-passed tags are already a hard gate on every base path (Qdrant must-filter, graph WHERE, metadata sidecar, tag-only fallback, final post-filter). What actually happened in the repro: the gate constrained the pool to flint-tagged memories, the Forge memories were excluded, and within the surviving pool query-independent components (importance 0.9 × 0.1 weight + recency + tag crumbs) dominated near-zero topical evidence — confident-looking garbage with no signal the pool was gated.

Fixes, with tag semantics untouched:

  • Relevance gate: evidence = max(vector, keyword, metadata, exact); when query tokens exist and evidence < RECALL_RELEVANCE_GATE, importance/confidence/recency/relevance/tag components are scaled by evidence/gate (linear ramp). Ships at 0.0 = exactly current behavior; the enabled value comes from the eval funnel (lab sweep grid 0.10–0.25 + 22-probe + negative-control gates in automem-evals). components now carry evidence + relevance_gated.
  • tag_scope response diagnostics when tags are passed: {filtered, pool_size_hint, gated_low_evidence} — recall is no longer silent about gating.
  • Opt-in scope_fallback=true: fills remaining slots with unscoped vector results flagged outside_tag_scope: true, appended after scoped results, with full filter parity (min_score, time, exclude_tags, edge-based current-state suppression) — only the tag scope is lifted. In-scope memories can never reappear as "outside scope" fills (guards are mutation-tested).
  • Doc corrections: MCP tag_match description claimed default "exact"; the API default is prefix. tags documented as a hard scope filter (use context_tags to boost). Per-path semantics table in docs/API.md.

Testing

20 new tests including a gate-0 bit-identity matrix, linear-ramp midpoint, route-level rerank + diagnostics, fill-resurrection mutation kills, and MCP rendering of the fill flag. Full suite 523 passed, 12 skipped; Node 14/14; black + flake8 clean.

Refs #130 (will update the issue with this corrected analysis).

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses issue #130 by introducing an optional “within-pool relevance gate” that prevents query-independent scoring components (importance/recency/tag overlap, etc.) from dominating when topical evidence is weak inside a tag-scoped candidate pool. It also adds tag-scope diagnostics to the /recall response and an opt-in scope_fallback=true mode to backfill results from an unscoped vector search when the scoped pool is too small.

Changes:

  • Add RECALL_RELEVANCE_GATE (env) and propagate evidence/relevance_gated in score components; gate scales query-independent components when topical evidence is below threshold.
  • Add /recall response diagnostics for tag scoping (tag_scope) and implement opt-in scope_fallback backfill behavior with filter/state parity and outside_tag_scope marking.
  • Update MCP server/client passthrough + formatting and expand documentation + tests for the new semantics and flags.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
automem/utils/scoring.py Implements topical-evidence computation and the optional relevance gate; extends score component payload.
automem/config.py Adds _clamped_unit_interval and RECALL_RELEVANCE_GATE env configuration.
automem/api/recall.py Adds scope-fallback implementation, request parsing, tag-scope diagnostics, and keeps score-filter accounting stable when fills are appended.
tests/test_api_endpoints.py Adds extensive regression/behavior tests for the relevance gate, tag_scope diagnostics, and scope_fallback behavior.
mcp-sse-server/server.js Passes through scope_fallback, surfaces outside_tag_scope in formatted output, and updates MCP tool schema descriptions.
mcp-sse-server/test/server.test.js Adds coverage for scope_fallback query param passthrough and outside_tag_scope rendering in both output formats.
docs/ENVIRONMENT_VARIABLES.md Documents RECALL_RELEVANCE_GATE (and RECALL_MIN_SCORE behavior) for operators.
docs/API.md Clarifies tag semantics, adds scope_fallback contract, and documents tag_scope diagnostics.

@jack-arturo
jack-arturo force-pushed the feat/tag-score-token-cap branch from b291ab6 to dcea2c6 Compare June 11, 2026 02:40
@jack-arturo
jack-arturo force-pushed the fix/130-relevance-gate branch from 7dac6d1 to fe62186 Compare June 11, 2026 02:40
jack-arturo added a commit that referenced this pull request Jun 11, 2026
…nge (#191)

Fixes #190.

## Problem

`_graph_keyword_search` returned the **raw Cypher additive score** — +2
per keyword contained in content, +1 per keyword in any tag, summed over
all extracted keywords, plus a +2/+1 whole-phrase bonus — so a K-keyword
query can score up to **3K+3** while every other channel (vector cosine,
metadata, trending importance) lives in 0–1.

Observed during the 2026-06-11 production forensics: a tag-scoped
exact-content match returned `keyword=11.0`, `final_score=4.03`.
Consequences:

- `SEARCH_WEIGHT_KEYWORD (0.35) × 11 = 3.85` — a keyword hit trumps any
vector/metadata/importance combination, scaling with *query length*
rather than match quality.
- Defeats `RECALL_RELEVANCE_GATE` semantics (PR #186): `evidence =
max(vector, keyword, metadata, exact)` assumes 0–1 components; `evidence
= 11` sails past any gate.

## Fix

1. **Producer**: normalize the raw score by its per-query maximum
(`3·len(keywords) + 3` when a phrase is present; `3` in the phrase-only
branch) before it leaves `_graph_keyword_search`. This is a monotone
per-query transform — within-channel ordering (and the Cypher `ORDER
BY`) is unchanged; only cross-channel blending changes, which is the
point.
2. **Consumer**: defensively clamp the keyword component to `min(1.0,
…)` in `_compute_metadata_score`, so no future producer can break the
0–1 contract or the gate again.

## Verification

- 4 new tests in `tests/test_keyword_score_normalization.py` (TDD'd
against the bug, including the literal `keyword=11.0` repro). Full
suite: **503 passed**.
- **Production-corpus lab A/B** (10,142-memory snapshot, 200 queries, vs
the pooled 3-run parity baseline from the 2026-06-11 release sweep):
- Recall@5 −0.2pp, Recall@10 −0.7pp, MRR −0.008, NDCG@10 −0.007 (all
within baseline run-to-run variance; paired t-test p=0.32)
  - Per-query: **196/197 unchanged**, 0 improved, 1 regressed
- The single flip is the intended behavior change made visible: the
expected memory held rank 1 *only* via the inflated keyword score (ranks
2–5 identical before/after). It's in the fallback-typed `Memory` cohort
(MRR 0.15 baseline — the known data-quality cohort from #188's
classification incident).

## Notes

- This lands on main independently of the #182#187 chain; #186's gate
evidence check is the main beneficiary once the chain rebases over it.
- Trending (`importance`), metadata (capped), and vector (cosine)
channels were verified already bounded; the graph keyword channel was
the only unbounded producer.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@jack-arturo
jack-arturo force-pushed the feat/tag-score-token-cap branch from 495f2a0 to f6e3099 Compare June 11, 2026 18:55
Base automatically changed from feat/tag-score-token-cap to develop June 11, 2026 19:02
jack-arturo and others added 4 commits June 11, 2026 21:02
…n tag scope (#130)

Corrected root cause: issue #130's own diagnosis (tags acting as a score
booster instead of a scope filter) is wrong — user-passed tags are already
a hard gate on every base retrieval path (Qdrant must-filter, graph WHERE,
and the recall post-filters). The actual defect is *within* the tag-gated
pool: when topical evidence is ~zero, query-independent score components
(importance x 0.1 weight + recency + tag crumbs) still produce
confident-looking final scores, so scoped queries return high-scoring
off-topic results with no signal that the pool was constrained. Tag gate
semantics are unchanged; context_tags stays the soft-boost channel.

- _compute_metadata_score: evidence = max(vector, keyword, metadata, exact).
  When query tokens exist and evidence < RECALL_RELEVANCE_GATE, the
  query-independent components (importance, confidence, recency, tag) are
  scaled by evidence/gate — a linear ramp, no cliff. Components are scaled
  before weighting so the breakdown stays truthful; the context bonus is
  untouched. Components now carry "evidence" and "relevance_gated".
- New env RECALL_RELEVANCE_GATE (default 0.0 = gate disabled; the scaling
  branch is guarded behind gate > 0 so legacy scores stay bit-identical).
  Negative values clamp to 0.0, values above 1.0 clamp to 1.0 (evidence is
  bounded at ~1.0, so a larger gate is unreachable and would only dampen
  uniformly). The shipped gate value comes from the eval funnel; 0.0 ships
  until validated there.
- Scope diagnostics: when tags were passed, /recall echoes
  tag_scope {filtered, pool_size_hint, gated_low_evidence}. pool_size_hint
  is the post-tag-filter, pre-limit vector candidate count (null when no
  semantic query ran against the vector store).
- Opt-in scope_fallback param (default false): tag scope + semantic query +
  results under limit -> top up from an unscoped vector search. Fills are
  appended after scoped results regardless of score — implemented in
  handle_recall after final filtering rather than in _run_single_query,
  because the aggregated score re-sort there would interleave fills with
  scoped results. Fills never displace scoped results, respect time and
  exclude_tags filters plus current_only payload-level state checks, are
  deduped against scoped results, and carry outside_tag_scope: true.
- score_filter.filtered_count now snapshots before fills are appended so it
  keeps describing the score filters only.
- MCP SSE server: tag_match description corrected to the actual API default
  (prefix, per recall.py param parsing — it claimed exact), tags documented
  as the hard scope filter vs context_tags soft boost, scope_fallback wired
  through (schema, recallArgs, URL params) with pass-through test coverage.
- Docs: per-path tag semantics note, scope_fallback/tag_scope docs, and
  RECALL_RELEVANCE_GATE + RECALL_MIN_SCORE rows in ENVIRONMENT_VARIABLES.md
  (RECALL_MIN_SCORE had no existing row, so both were added to the Recall
  Settings table).

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

Review follow-up to 29a54f2 (within-pool relevance gate + scope_fallback).

Important fixes:
- Fills can no longer resurrect scoped results dropped by min_score or the
  adaptive floor: any fallback candidate whose tags match the request's
  tag_filters is rejected outright (it is in-scope by definition and must
  never be labeled outside_tag_scope), and the request's min_score now
  applies to fills for filter parity with the scoped path.
- current_only parity: fills are routed through the same edge-based state
  filtering as the main path (_apply_current_state_filter), so memories
  superseded via INVALIDATED_BY/EVOLVED_INTO edges cannot re-enter as
  fills; their active replacements fill instead, deduped against scoped
  results and rejected when in-scope.
- MCP SSE server: formatRecallAsItems now renders the promised
  outside_tag_scope flag — "Outside tag scope: true" line in the detailed
  format, "[outside tag scope]" suffix in the compact format.

Minors:
- Extracted the scope-fallback block into _apply_scope_fallback(), matching
  the file's helper-extraction convention and keeping handle_recall's call
  site small.
- scoring.py: corrected the evidence comment (tag overlap IS query-token
  derived; it is excluded because the scope tag often matches a query token,
  making it scope-confounded crumb evidence) and gated relevance_score with
  the other query-independent components (no behavior change at the default
  SEARCH_WEIGHT_RELEVANCE=0.0; covered by a test at a non-zero weight).
- Replaced the dead max(recall_max_limit, limit) expression (limit is
  clamped at parse) with recall_max_limit.
- _compute_metadata_score return annotation now Dict[str, Any] (components
  carry the relevance_gated bool).
- Moved fallback_query/has_semantic_query declarations above the scope-
  fallback block with a comment noting their dual use by the tag_scope
  diagnostics.
- docs/API.md: scope_fallback bullet rewritten for the new filter-parity
  semantics; pool_size_hint caveats documented (capped by the vector fetch
  limit, may double-count across decomposed queries).
- Tests: min_score resurrection guard, fill-below-min_score exclusion,
  edge-superseded fill replacement, null pool_size_hint on tag-only recall,
  gated relevance_score scaling, and outside_tag_scope rendering in both
  MCP text formats.

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

Mutation testing showed both _in_tag_scope guards in _apply_scope_fallback
were unkilled: deleting either left all 521 tests passing.

- test_recall_scope_fallback_in_scope_state_replacement_not_resurrected
  pins the replacement-row guard (recall.py:743): an out-of-scope fill
  superseded via INVALIDATED_BY by an in-scope replacement must not
  resurrect that replacement mislabeled outside_tag_scope.
- test_recall_scope_fallback_rejects_in_scope_fill_above_min_score pins
  the direct-fill guard (recall.py:703): an in-scope candidate surfaced
  only by the unscoped fallback search is rejected even when its
  recomputed fill score clears min_score. Runs under state_mode=history
  because the state-filter pass re-checks tag scope on its own rows and
  would otherwise mask this guard.

Both tests were RED-verified against their respective guard deletions
and pass on the unmutated code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jack-arturo
jack-arturo force-pushed the fix/130-relevance-gate branch from fe62186 to 3693734 Compare June 11, 2026 19:03
@jack-arturo
jack-arturo merged commit c11b594 into develop Jun 11, 2026
4 checks passed
@jack-arturo
jack-arturo deleted the fix/130-relevance-gate branch June 11, 2026 19:03
jack-arturo added a commit that referenced this pull request Jun 11, 2026
…#187)

## Summary
Stacked on #186. Driven by the failure-mode diagnosis in #183 (58
failed-but-retrieved LongMemEval questions: answer-construction 42,
missing-date-use 7, ranking 4).

Server (production + benchmark):
- **Timestamp tiebreak** (always-on): exact score ties now order
newest-first deterministically (`_score_sort_key`).
- **`recency_bias=auto|on|off`** (ships `off` via
`RECALL_RECENCY_BIAS`): after dedup/state-filter and before the adaptive
floor, candidate timestamps are min-max normalized and
`SEARCH_WEIGHT_TEMPORAL` (default 0.1) × relative recency is added — so
the newest version of a conflicting fact can outrank an older, heavier
one. `auto` triggers on temporal intent ("latest", "current", "what
changed", …; word-boundaried, "currency"/"nowhere" safe).
- **Supersession chain-walk**: `current` state mode now resolves
INVALIDATED_BY/EVOLVED_INTO chains to their head (A→B→C surfaces C,
provenance still points at A; depth-bounded at 5, cycle-safe, batched).
*Honesty note: benchmark corpora carry no supersession edges — this is a
production-correctness fix, not a score mover.*

Harness (benchmark-only, flag-gated for methodology reproducibility):
- **`temporal_answer_hint`** config flag (default off; new
`temporal-answer` preset for A/B): chronological memory rendering with
scores + conflict-recency guidance + anti-overabstention guidance (27 of
the 58 failures were literal "I don't know" with the answer retrieved;
abstention remains possible). Flag-off prompt is byte-identical to the
canonical methodology (equality-tested).

## Testing
40 new tests (tiebreak, bias flip with the shipped default weight,
auto-detection, chain-walk depth/cycle/query-count guards, #158
preference latest-wins acceptance, prompt byte-identity). Full suite 563
passed, 12 skipped; black + flake8 clean.

## Validation plan before enabling anything
Lab IR A/B → automem-evals 22-probe zero-delta gate (recency_bias=off
default keeps it zero-delta) → LongMemEval mini judge-off (recall@5 ≥
97.2% floor) → judged mini → full run; `temporal-answer` preset A/B for
the harness flag, with server-vs-harness deltas reported separately in
EXPERIMENT_LOG.

Refs #158, #159

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
jack-arturo added a commit that referenced this pull request Jun 12, 2026
…nce gate, date-aware ranking (#182, #193, #186, #187, #183, #184, #188) (#194)

## Release: ranking & recall series (develop → main)

⚠️ **Merge with a MERGE COMMIT — do not squash.** release-please needs
the individual conventional commits below to compute the version and
changelog for PR #154.

### What's in this release

| PR | Change | Default behavior |
|---|---|---|
| #182 | `feat(recall)`: configurable recency decay window/curve |
unchanged (env-gated) |
| #193 (replaces #185) | `feat(recall)`: tag-score denominator cap fixes
query-length bias | unchanged (`SEARCH_TAG_SCORE_TOKEN_CAP=0`) |
| #186 | `fix(recall)`: relevance gate — query-independent scoring gated
on topical evidence (#130) | unchanged (gate off) |
| #187 | `feat(recall)`: date-aware ranking,
`recency_bias=off\|on\|auto`, latest-fact selection (#158, #159) |
`RECALL_RECENCY_BIAS=off`; adds deterministic timestamp tiebreak for
near-ties |
| #183 | `feat(benchmarks)`: failure-mode diagnosis harness + judge
quota preflight | tooling only |
| #184 | `fix(mcp)`: surface stored metadata + `updated_at` in detailed
recall format (#111) | additive |
| #188 | `feat(enrichment)`: classification fallback-rate metrics in
`/enrichment/status` | additive |

Plus: CI now runs on `develop` pushes/PRs; benchmark experiment log +
README contribution-policy note.

### Verification evidence

- **Unit/lint/npm**: 625 pytest + 16 mcp-sse-server tests green on
develop head; CI green.
- **Default-preserve**: recall-lab baseline on the 10k-memory production
snapshot — develop defaults vs main pooled baseline identical aggregates
(R@5 0.655 / R@10 0.710 / MRR 0.434 / NDCG@10 0.501). Two-stack probe
run (main vs develop, defaults): 11/12 preserve-exact, remaining diffs
are near-tie reorders (top-1 score deltas ≤ 5.4e-5, the #187 timestamp
tiebreak).
- **Full judged 500q LongMemEval** (ship config:
`RECALL_RECENCY_BIAS=auto` + `temporal-answer` harness): recall@5 96.6%
(483/500), accuracy 86.0% (430/500), `judge_errors=0`,
`memory_ingest_failures=0`.
- **Churn attribution** (targeted re-runs of all 17 churned questions on
current-main-at-defaults and develop-at-defaults): 15/17 moved with #191
(already on main) — the April canonical 97.2% floor is stale; current
main measures ~97.0%. Develop-at-defaults differs from current main by
**1 question in 500** (a near-tie rank-5/6 flip from #187's
deterministic tiebreak). Accuracy is within answerer replicate noise
(identical-config reference runs flip 28/500 answers).
- Full detail: `benchmarks/EXPERIMENT_LOG.md` (2026-06-11 entry) and
`benchmarks/results/lme_churn17_*` + `analyze_churn17.py`.

### Opt-in features shipped OFF

`RECALL_RELEVANCE_GATE` (validated at 0.40 on lab corpus; improves
negative-probe precision) and `RECALL_RECENCY_BIAS=auto` (current-state
query re-ranking). Neither affects default behavior; see
`docs/ENVIRONMENT_VARIABLES.md`.

### After merging

release-please will update PR #154 (v0.16.0); merging *that* cuts the
tag and publishes the `:stable` image — the actual user-facing deploy
event for Railway template users.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
jack-arturo added a commit that referenced this pull request Jun 26, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.16.0](v0.15.2...v0.16.0)
(2026-06-26)


### Features

* **api:** add admin backup endpoint
([#162](#162))
([8b1f264](8b1f264))
* **api:** support bulk memory associations
([1221e36](1221e36))
* **api:** support bulk memory associations
([#198](#198))
([28eb916](28eb916))
* **benchmarks:** LongMemEval failure-mode diagnosis harness + judge
quota preflight
([#183](#183))
([f99bece](f99bece))
* **consolidation:** expose cluster threshold and min size as env vars
([#163](#163))
([7e731f3](7e731f3))
* **enrichment:** expose classification fallback-rate metrics in
/enrichment/status
([#188](#188))
([0b522a9](0b522a9))
* **entity:** harden identity cleanup and repair tooling
([#176](#176))
([827dfbc](827dfbc))
* **eval:** recall-quality optimization harness — lab foundation +
design ([#197](#197))
([431433e](431433e))
* **graph:** support unbounded visualizer snapshots
([#141](#141))
([c730128](c730128))
* **lab:** add aged labelled distractor injection
([cc5d546](cc5d546))
* **lab:** add config_complexity simplicity metric
([dfb10d9](dfb10d9))
* **lab:** add distractor_rate_at_k precision guardrail metric
([872eab2](872eab2))
* **lab:** add lab_corpus with parameterized recall
([5e1e071](5e1e071))
* **lab:** add pick_winner scorecard decision rule
([3187eac](3187eac))
* **lab:** add real consolidation pass helper
([48a7d4a](48a7d4a))
* **lab:** isolate production clone restores
([#171](#171))
([aef90c0](aef90c0))
* **lab:** wire scorecard, distractors, recall params, consolidation
into runner
([589ec30](589ec30))
* **recall:** add metadata sidecar search
([#177](#177))
([4e7956e](4e7956e))
* **recall:** add state_mode=current|history recall alias
([#173](#173))
([b1df86c](b1df86c))
* **recall:** cap tag-score denominator to fix query-length bias
([#193](#193))
([cefa516](cefa516))
* **recall:** date-aware ranking + latest-fact selection
([#158](#158),
[#159](#159))
([#187](#187))
([a6ed945](a6ed945))
* **recall:** make recency decay window and curve configurable
([#182](#182))
([dbb933f](dbb933f))
* **recall:** ranking release — recency config, tag-score cap, relevance
gate, date-aware ranking
([#182](#182),
[#193](#193),
[#186](#186),
[#187](#187),
[#183](#183),
[#184](#184),
[#188](#188))
([#194](#194))
([337fe98](337fe98))
* **scripts:** safer reclassify_with_llm.py with provider flags +
tighter prompt
([#164](#164))
([a742602](a742602))


### Bug Fixes

* **api:** address copilot review on PR
[#198](#198)
([0466a1e](0466a1e))
* **api:** handle grouped association write failures
([cd93df9](cd93df9))
* **backup:** make backup_automem.py runnable as `python
scripts/backup_automem.py`
([#175](#175))
([edd9742](edd9742))
* **benchmarks:** add publication verification bundle
([#166](#166))
([420d721](420d721))
* **consolidation:** skip eager first tick at startup to avoid FalkorDB
load race
([#165](#165))
([1b812cf](1b812cf))
* **docs:** keep dispatch payload arrays stable
([df6e9e8](df6e9e8))
* **embedding:** fall back to per-item real embeddings before
placeholders in batch path
([#189](#189))
([6e9c62c](6e9c62c))
* **entity:** restore person-shape exemption on the slug validation path
([#179](#179))
([5e29960](5e29960))
* **entity:** stop validator over-rejecting real people, code tools, and
event categories
([#178](#178))
([193b730](193b730))
* **lab:** address copilot review on PR
[#197](#197)
([45f80d6](45f80d6))
* **lab:** align scorecard key contract (build_scorecard -&gt;
pick_winner)
([7d91530](7d91530))
* **mcp-sse:** decouple /health liveness from upstream readiness
([#151](#151))
([5bcfb8b](5bcfb8b))
* **mcp:** cap association failure summary
([ea4e08f](ea4e08f))
* **mcp:** surface stored metadata and updated_at in detailed recall
format ([#184](#184))
([230416e](230416e))
* **recall:** address copilot review on PR
[#194](#194)
([50b1647](50b1647))
* **recall:** canonicalize / and : separators in context_tag matching
([3afd9d3](3afd9d3))
* **recall:** canonicalize / and : separators in context_tag matching
([#203](#203))
([ba5e9ff](ba5e9ff))
* **recall:** gate query-independent scoring on topical evidence within
tag scope
([#130](#130))
([#186](#186))
([c11b594](c11b594))
* **recall:** hydrate semantic recall summaries
([#192](#192))
([76e845d](76e845d))
* **recall:** normalize graph keyword scores into the 0-1 component
range ([#191](#191))
([3653ddf](3653ddf))
* **recall:** respect current memory state
([#170](#170))
([ed36b98](ed36b98)),
closes [#169](#169)
[#158](#158)
[#159](#159)
* **scripts:** add sys.path guard to reembed_embeddings.py
([d333cf0](d333cf0))


### Documentation

* add scripts catalog, recall-quality-lab guide, and 0.16.0 migrations
([f20c664](f20c664))
* **bench:** log full judged 500q LongMemEval ship-config run with churn
attribution
([41bf8d0](41bf8d0))
* **eval:** Plan A — lab metric foundation (TDD, 9 tasks)
([0087dda](0087dda))
* **eval:** Plan B — parallel matrix harness (TDD, 9 tasks)
([c8ddfb2](c8ddfb2))
* **evals:** mark Memora/FAMA/WRIT lifecycle diagnostics as
diagnostic-only
([#174](#174))
([e8a3285](e8a3285))
* **eval:** spec for recall-quality optimization harness
([b1a1995](b1a1995))
* fix stale claims and document gated flags for 0.16.0
([b152d64](b152d64))
* note develop-branch contribution policy in README
([ccf02dd](ccf02dd))
* **positioning:** add scout reference
([#168](#168))
([922d23b](922d23b))
* refresh benchmark currency for the neutral AMB run and prune stale
archive docs
([3ff95bd](3ff95bd))
* refresh benchmark currency for the neutral AMB run and prune stale
archive docs
([#204](#204))
([89c30e0](89c30e0))
* refresh README and benchmark guidance
([#157](#157))
([bba31cc](bba31cc))
* **runtime:** align Docker viewer paths and setup guidance
([#155](#155))
([bbda79b](bbda79b))
* scripts catalog, recall quality lab guide, and 0.16.0 migration
runbook ([#199](#199))
([f190ae5](f190ae5))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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.

2 participants