Skip to content

Commit 39c1901

Browse files
Tomas Pflanzerclaude
andcommitted
release: v2.0.5 — honest numbers (impact endpoint, mistake-avoided lie, shape drift)
Three correctness bugs hiding behind quiet metrics: 1. /api/v1/impact crashed on a fresh DB. ImpactEvent lives in engine/impact.py, but init_db() only imports storage/models.py before Base.metadata.create_all(). The impact_events table never existed on a fresh DB and any code path that hit record_impact died on `no such table`. Force-import memee.engine.impact inside init_db() before create_all so the table is present everywhere. 2. MISTAKE_AVOIDED was being credited for warnings the agent IGNORED, as long as outcome=success. The CLI hook line read warnings_avoided=N over numbers that were always warnings_violated. New ImpactType.WARNING_INEFFECTIVE for violated-but-task-passed; MISTAKE_AVOIDED is now reserved for evidence-backed behaviour change. CLI line renamed to warnings_violated, which is what it always was. 3. get_impact_summary returned 8 keys when empty, 18 otherwise — forced every caller into defensive .get() branching. Now: one shape with zeros / {} / [] in the empty case. Plus: doc drift — README, CLAUDE.md, launch-copy and the historical review-fixes doc all claimed 24 MCP tools; the real number is 19 (the five research_* tools were removed in v2.0.0). hooks_config.py comments described /dev/null redirect and stderr writes that the commands don't actually do; comments rewritten to match the commands. Six new tests under test_v2_0_5_fixes.py cover the three correctness fixes. Existing test_learn_auto updated for the renamed CLI field. 368/368 trimmed-suite tests pass; the v2.0.4 ledger guard confirms the suite leaves ~/.memee/packs.json byte-identical. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2fa7ebd commit 39c1901

14 files changed

Lines changed: 351 additions & 48 deletions

CHANGELOG.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
11+
## [2.0.5] — 2026-04-28
12+
13+
The "honest numbers" patch. v2.0.4 had three correctness bugs hiding
14+
behind quiet metrics. v2.0.5 makes the numbers say what actually
15+
happened.
16+
17+
### Fixed
18+
19+
- **`/api/v1/impact` worked on populated DBs and crashed on fresh
20+
ones.** ``ImpactEvent`` is defined in ``engine/impact.py`` (so
21+
``record_impact`` lives next to the metric logic), but ``init_db``
22+
only imports ``storage.models`` before calling ``Base.metadata.
23+
create_all``. On a fresh DB the ``impact_events`` table never got
24+
created and any code path that hit ``record_impact`` died on
25+
``no such table``. v2.0.5 force-imports ``memee.engine.impact``
26+
inside ``init_db`` before ``create_all`` so the table is present
27+
on every fresh DB. Regression test seeds a brand-new DB and
28+
asserts ``inspect(engine).get_table_names()`` contains it.
29+
30+
- **`MISTAKE_AVOIDED` lied when the agent ignored the warning.**
31+
``post_task_review`` recorded ``MISTAKE_AVOIDED`` for any warning
32+
the agent *violated* as long as the task ended in ``outcome="success"``.
33+
The metric read like a win for behaviour the agent had ignored.
34+
v2.0.5 introduces a new ``ImpactType.WARNING_INEFFECTIVE`` for that
35+
exact case and reserves ``MISTAKE_AVOIDED`` for evidence-backed
36+
behaviour change. ``MISTAKE_MADE`` (violation + failure) is
37+
unchanged. The CLI hook line that surfaced this as
38+
``warnings_avoided=N`` now reads ``warnings_violated=N``, which is
39+
what the underlying number always was.
40+
41+
- **`get_impact_summary` returned a different key set when empty.**
42+
Eight keys on empty, eighteen otherwise — every consumer was
43+
forced into defensive ``.get(key, 0)`` branching. v2.0.5 returns
44+
the same key set in both cases, with zeros / ``{}`` / ``[]``
45+
instead of missing keys.
46+
47+
- **Docs claimed 24 MCP tools; the real count is 19.** README,
48+
CLAUDE.md, launch copy and the historical review-fixes doc all
49+
carried the v1.x number through the v2.0.0 deletion. The
50+
``research_*`` MCP tools that lived under "Research:" were
51+
removed in v2.0.0 with the autoresearch engine; v2.0.5 lists
52+
the 19 tools that actually ship.
53+
54+
- **`hooks_config.py` comments described behaviour the commands
55+
did not have** — Stop "redirects to /dev/null" (it does not),
56+
UserPromptSubmit "writes to stderr" (it writes to stdout, which is
57+
what Claude Code surfaces). Comments were rewritten to match the
58+
commands; commands themselves were not changed because the current
59+
behaviour is the one we want.
60+
61+
### Tests
62+
63+
- ``tests/test_v2_0_5_fixes.py`` — six new cases covering the four
64+
fixes above. Plus the autouse ``_isolate_pack_ledger`` fixture
65+
from v2.0.4 means none of these tests can pollute the developer
66+
ledger.
67+
1068
## [2.0.4] — 2026-04-27
1169

1270
The "tests stop polluting the developer's ledger" patch.

CLAUDE.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ Memee ships as two packages, with clear licence separation:
181181

182182
| Package | Licence | What it adds |
183183
|---|---|---|
184-
| **`memee`** (this repo) | MIT | Full single-user product: every engine module, MCP server, CLI, CMAM adapter, dashboard. No users, no teams, no scope enforcement. |
185-
| **`memee-team`** (private repo, licence-gated) | Proprietary (EULA) | `User` + `Team` SQLAlchemy models, `scoping.py` engine (personal → team → org promotion), SSO (SAML/OIDC), audit log export, RBAC, multi-user dashboard auth, licence key verification. |
184+
| **`memee`** (this repo) | MIT | Full single-user product: every engine module, MCP server, CLI, CMAM adapter. No users, no teams, no scope enforcement. |
185+
| **`memee-team`** (private repo, licence-gated) | Proprietary (EULA) | `User` + `Team` SQLAlchemy models, `scoping.py` engine (personal → team → org promotion), SSO (SAML/OIDC), audit log export, RBAC, licence key verification. |
186186

187187
`memee-team` plugs into OSS via `memee.plugins` hooks
188188
(`current_user_id`, `visible_memories`, `promote`, `can_promote`, `on_record`).
@@ -203,21 +203,22 @@ Pricing model reflects "Memee is memory, not model" — flat per-team
203203
(like Supabase, Vercel, Plausible), not per-seat (like Copilot, Cursor).
204204
Value scales sublinearly with headcount: one canon serves the whole team.
205205

206-
## MCP Tools (23)
206+
## MCP Tools (19)
207207

208-
Core: memory_record, memory_search, memory_suggest, memory_validate,
209-
memory_invalidate, decision_record, antipattern_record, antipattern_check
208+
Core: memory_record, memory_search, search_feedback, memory_suggest,
209+
memory_validate, memory_invalidate, decision_record, antipattern_record,
210+
antipattern_check
210211

211212
Intelligence: propagate_patterns, predict_warnings, inherit_knowledge,
212213
run_dream, review_code, get_briefing, post_task_feedback
213214

214-
Research: research_create, research_log, research_status, research_meta,
215-
research_complete
216-
217215
Analytics: learning_status, canon_list
218216

219217
Delivery: sync_to_cmam (push canon to Claude Managed Agents Memory)
220218

219+
(The five ``research_*`` tools that lived here through v1.x were removed
220+
in v2.0.0 along with the autoresearch engine.)
221+
221222
## CMAM Bridge (Claude Managed Agents Memory)
222223

223224
Anthropic's managed memory is a filesystem-style store at `/mnt/memory/` inside
@@ -276,11 +277,11 @@ MCP tool `sync_to_cmam` lets agents trigger the push themselves.
276277
| File | Purpose |
277278
|------|---------|
278279
| `src/memee/cli.py` | 25+ Click commands (incl. `cmam sync`/`cmam status`) |
279-
| `src/memee/mcp_server.py` | 24 MCP tools |
280+
| `src/memee/mcp_server.py` | 19 MCP tools |
280281
| `src/memee/adapters/cmam.py` | Claude Managed Agents Memory bridge |
281282
| `src/memee/storage/models.py` | 15 SQLAlchemy models |
282283
| `src/memee/storage/database.py` | DB init, FTS5, WAL mode |
283-
| `src/memee/api/routes/dashboard.py` | Chart.js dashboard |
284+
284285
| `src/memee/api/routes/api_v1.py` | REST API (12+ endpoints) |
285286
| `src/memee/installer.py` | Interactive setup wizard |
286287
| `src/memee/doctor.py` | Health check + auto-configure AI tools |
@@ -304,5 +305,5 @@ test_real_impact (A/B with/without), test_perf_simulation (9 scenarios)
304305
- 33 commits on feat/initial-setup
305306
- 63 Python files, 18,899 lines of code
306307
- 201 tests passing
307-
- 16 engine modules + CMAM adapter, 24 MCP tools, 12+ API endpoints (GET-only dashboard API)
308+
- 16 engine modules + CMAM adapter, 19 MCP tools, 12+ API endpoints (GET-only dashboard API)
308309
- MIT licence (OSS `memee`), proprietary EULA for `memee-team`

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ Full methodology + per-repo file sizes: [docs/benchmarks.md](docs/benchmarks.md)
125125

126126
## Using it with Claude, GPT, Gemini
127127

128-
An MCP server with 24 tools ships with the install. Drop this into `~/.claude/settings.json` — or the Cursor / Continue / any MCP-capable client equivalent:
128+
An MCP server with 19 tools ships with the install. Drop this into `~/.claude/settings.json` — or the Cursor / Continue / any MCP-capable client equivalent:
129129

130130
```json
131131
{

docs/launch-copy.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Char count: 271. Hook in 4 words. One verb (writes). One outcome (every model re
9999
> memee setup
100100
> memee doctor
101101
>
102-
> MCP server with 24 tools ships in the box. Drop it into Claude Code, Cursor, Continue, anything MCP-shaped. Memory lives in ~/.memee/memee.db.
102+
> MCP server with 19 tools ships in the box. Drop it into Claude Code, Cursor, Continue, anything MCP-shaped. Memory lives in ~/.memee/memee.db.
103103
>
104104
> MIT. github.com/gizmax/memee
105105

docs/review-fixes.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
A thorough internal review (2026-04-24) measured Memee on impact tests, token calculations, OrgMemEval, large simulations, and a handcrafted 12-memory retrieval benchmark. The numbers confirmed the headline claims (71 % less time, 65 % fewer iterations, 96 % token reduction) but surfaced seven concrete problems. All seven were addressed in a single session using parallel fix-agents. This document records what changed, with before/after numbers, so future reviewers can see our work.
44

5+
> **Historical note (v2.0.5):** the dashboard panels referenced below
6+
> (`Retrieval health`, the impact-panel three-card layout) describe the
7+
> v1.x web dashboard. That dashboard was removed in v2.0.0 along with the
8+
> autoresearch engine. The same metrics still live in the underlying
9+
> tables and are surfaced in `memee status` and the JSON API; the panels
10+
> themselves do not exist any more.
11+
512
## Summary
613

714
| # | Problem | Before | After | Where |

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "memee"
7-
version = "2.0.4"
7+
version = "2.0.5"
88
description = "Cross-model shared memory for AI agents. One canon. Every model, every project, every teammate."
99
readme = "README.md"
1010
requires-python = ">=3.11"

src/memee/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Memee — Institutional memory for AI agent companies."""
22

3-
__version__ = "2.0.4"
3+
__version__ = "2.0.5"

src/memee/cli.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1318,8 +1318,12 @@ def learn(auto, project, diff_text, outcome, agent, model):
13181318
# Silent unless something was learned. A noisy hook gets disabled.
13191319
if patterns_followed == 0 and warnings_violated == 0:
13201320
return
1321+
# Report what actually happened. ``warnings_violated`` is exactly
1322+
# that — counts of warnings the agent ignored. v2.0.4 and earlier
1323+
# surfaced this number as ``warnings_avoided``, which read like a
1324+
# win. It wasn't.
13211325
click.echo(
1322-
f"memee learn: ok (warnings_avoided={warnings_violated}, "
1326+
f"memee learn: ok (warnings_violated={warnings_violated}, "
13231327
f"patterns_followed={patterns_followed}, new_patterns={new_patterns})"
13241328
)
13251329
else:

src/memee/engine/feedback.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -99,23 +99,32 @@ def post_task_review(
9999
continue
100100

101101
# A violation means the agent wrote code that matches a known
102-
# anti-pattern. If the task succeeded anyway we still credit
103-
# MISTAKE_AVOIDED (the warning surfaced in review was visible and
104-
# the agent's work passed). If the task failed — the warning was
105-
# ignored AND there's a real negative outcome — it's a MISTAKE_MADE.
106-
impact_kind = (
107-
ImpactType.MISTAKE_AVOIDED.value
108-
if outcome == "success"
109-
else ImpactType.MISTAKE_MADE.value
110-
)
102+
# anti-pattern. We map outcome to one of three honest states:
103+
# - failure → MISTAKE_MADE (warning ignored, real damage)
104+
# - success → WARNING_INEFFECTIVE (warning ignored, got lucky)
105+
# We deliberately do NOT credit MISTAKE_AVOIDED here — that
106+
# would be a metric that lies about what happened, and bad
107+
# numbers ruin the trust users have to put in this dashboard
108+
# for it to be worth running.
109+
if outcome == "success":
110+
impact_kind = ImpactType.WARNING_INEFFECTIVE.value
111+
outcome_text = (
112+
"Warning ignored; task succeeded anyway. The warning "
113+
"did not change the agent's behaviour."
114+
)
115+
else:
116+
impact_kind = ImpactType.MISTAKE_MADE.value
117+
outcome_text = (
118+
f"Warning ignored. Task outcome: {outcome}"
119+
)
111120
record_impact(
112121
session, mem.id,
113122
impact_kind,
114123
agent=agent, model=model, project_id=project_id,
115124
trigger="Post-task review detected anti-pattern violation",
116125
memory_shown=mem.title,
117126
agent_action=f"Violated warning: {warning.get('title', '')}",
118-
outcome=f"Warning ignored. Task outcome: {outcome}",
127+
outcome=outcome_text,
119128
severity_avoided=warning.get("severity", "medium"),
120129
)
121130

src/memee/engine/impact.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
class ImpactType(str, Enum):
2424
MISTAKE_AVOIDED = "mistake_avoided" # Agent got warning, changed approach
2525
MISTAKE_MADE = "mistake_made" # Agent ignored warning, repeated mistake
26+
WARNING_INEFFECTIVE = "warning_ineffective" # Agent ignored warning but task succeeded anyway — warning didn't help, didn't hurt
2627
TIME_SAVED = "time_saved" # Pattern reuse saved iteration time
2728
DECISION_INFORMED = "decision_informed" # Decision made with historical context
2829
CODE_CHANGED = "code_changed" # Code diff proves behavior change
@@ -235,18 +236,11 @@ def get_impact_summary(session: Session) -> dict:
235236
investment_minutes = total_memories * 5
236237
roi = total_time_saved / investment_minutes if investment_minutes > 0 else 0
237238

238-
if not events and warnings_shown == 0:
239-
return {
240-
"total_events": 0,
241-
"warnings_shown": 0,
242-
"warnings_shown_unique": 0,
243-
"warnings_acknowledged": 0,
244-
"warnings_acknowledged_unique": 0,
245-
"mistakes_avoided": 0,
246-
"mistakes_avoided_unique": 0,
247-
"mistakes_made": 0,
248-
}
249-
239+
# Single shape for both empty and populated cases. v2.0.4 and earlier
240+
# returned a subset of keys when there was nothing to report; that
241+
# forced every consumer (CLI, dashboard, tests) into defensive
242+
# branching. Now: same key set, zeros / empty containers when no
243+
# data — callers can read the shape without ``.get(key, 0)``.
250244
return {
251245
"total_events": len(events),
252246
"total_time_saved_minutes": round(total_time_saved, 1),

0 commit comments

Comments
 (0)