v2.5.2b1 — beta: the contract breaks (store result / check_health status / source-type enum)
Pre-releasePre-release (first beta of the 2.5.2 stabilization ladder; the latest final release remains v2.5.1, which pip installs by default — pre-releases require explicit opt-in).
The alpha rungs stayed inside the implementation. This one changes what callers see: it is the first cut of the 2.5.x line to break the public MCP tool contract, which is why it goes through the ladder (RELEASE_LIFECYCLE_STANDARD §2.1). The DB schema is unchanged — upgrade and rollback across this cut need no migration.
Contract breaks (3)
storeanswers whether it stored. Every response now carriesresult:stored(a row was written),skipped(nothing written, nothing wrong — dedup, or persistence paused),rejected(the request was refused).oktracks that verdict instead of being unconditionally true, and the oldskipped: trueflag is gone. Previously a store that wrote nothing — empty content, content that sanitizes to empty — answered{ok: true}, so a caller checking the obvious field read success for a write that never happened.check_healthhas one verdict. Thehealthyboolean is gone;status(healthy/degraded/unhealthy) is the verdict. The two fields disagreed by construction:healthywaslen(issues) == 0, so a database whose only findings were informational reportedhealthy: falsewhilestatusin the same response saidhealthy. The counts are still inissuesandseverity_summary.- One definition of the source-type enum. The canonical set was stated in three places and owned by none of them (a SQL literal in the health check, a tuple in the write seam, prose in the published schema).
utils.CANONICAL_SOURCE_TYPESis now the single definition; thestoreJSON Schema publishes it as anenumand generates its alias sentence from the alias table, so a caller no longer learns the contract by reading a health check.
Also in this cut
- Write-path bounds (
bug-156):sourceandmetadatareach the row as JSON with no size limit, and an episode's summary/keywords never had one — content has been capped since 2.1. Oversized sidecars are now refused, episode prose is capped in the shared prepare seam (so the queue drain obeys the same rule), andupdate_memoryruns the same sanitizer asstoreinstead of only.strip(). - A silent filter, disclosed (
bug-157): everyexternal_contextentry filtered recall while onlyuser/assistantentries were merged into the response, so asystementry could make a memory disappear with nothing saying why. The response now carriescontext_filter_only. - Remote-recall hydration batched (
bug-158): remote mode re-hydrated every hit with its own awaitedSELECT ... WHERE id = ?. Alimit=100recall paid 100 sequential round trips on the read hot path — measured 102 for 102 hits, now 2. - Bounded
/indexpush (bug-159): the write-path POST inherited the embed client's 30s default while the health probe (3s) and remote search (5s) each state their own. It states 10s now. - Descriptions rendered from the code they describe (
bug-160): the check-registry size (claimed 20, was 23), which deep checks honourfix=true, the tools a pause silences, the@autosentinel. Structural CI gates fail when any of them falls behind the implementation again. The shippedcpersona-memoryskill had the same drift and is gated too. - 18 findings from the independent pre-ship review (
bug-162..bug-179), and the outward claims the breaks falsified — README,SUPPORT.mdand the standard saying the tool contract is preserved across line transitions — corrected in the same batch.
Verification
- Behaviour-equivalence goldens were extended to the three layers these breaks touch (handler seam, MCP boundary, health/deep-check) before the breaks landed, with a frozen clock so wall-clock never reaches the golden; pin effectiveness was proven by hand-applied mutation. Each golden line that moved in this cut is one of the documented contract changes.
- Full suite: 664 passed, 5 skipped;
verify-issues.sh178 entries, 0 errors; ruff clean; mutation-proof CI job green; waiver registry clean. - PyPI artifacts (wheel + sdist) published via OIDC Trusted Publishing with provenance.
Known gap
The automated mutation-diff lane was skipped on this cut: the diff exceeded the lane's cap, and the cap currently skips everything rather than selecting a subset (tracked; the fix makes large diffs sample instead of opt out). The contract changes carry hand-applied mutation evidence instead, recorded per test.