Skip to content

Commit bc4e44f

Browse files
author
Murat
committed
feat(think): classify training_value into temporal thought metadata
- think() computes classify_training_value(text) and stores it in the temporal thought event metadata (non-fatal, existing suppress block) - extend test_think_records_temporal_event to assert training_value=medium - ruff format pass on training.py + test_training.py - docs: reference.md think row + CHANGELOG bullet Closes research backlog P1 item 3 (icarus).
1 parent 357f953 commit bc4e44f

6 files changed

Lines changed: 11 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
1212
- **CONTEXT.md persistence**`memory_context_inject` now also writes a 3-section markdown snapshot (frontmatter + Context + 6 Perspectives + Recent Episodes) to `<MCP_MEMORY_DATA_DIR>/<layer>/CONTEXT.md`. Per-agent isolation via the existing `MCP_MEMORY_DATA_DIR` env var (hermes/mimocode/cowagent each have separate data dirs, so the file is naturally per-agent with no race). Each perspective is fetched via `wiki_summarize(perspective=p, layer=layer, limit=3)` (≤200 tokens). Two new non-breaking result fields: `context_md_path` (str|None) and `perspectives_count` (int=6). Write failures are non-fatal (logged at WARNING, inject never breaks). Closes research backlog Priority 3.13 (pluton).
1313
- **Session quality scoring**`memory_session_end` computes a deterministic 4-component score (depth / decision / linked_entries / user_engagement, 0-80) on close and persists `quality_score` + `quality_parts` (JSON) on the session row. Two new optional params (`topics` / `state_deltas`) feed the engagement component. Score is surfaced via `memory_stats.avg_session_quality`. The 5th `recall_usage` component is reserved for when research-backlog P1 item 2 (recall telemetry) lands. Closes research backlog P1 item 1 (icarus).
1414
- **Recall telemetry** — every `dream` call is recorded in a new `recall_events` table (query, intent, result_count, layer, user_id, timestamp). `memory_stats` now returns `recall_count`. The session quality score gains its 5th component `recall_usage` (scale 4x20=80 → 5x20=100). Timeline shown by `dream(intent="recent")` is unaffected (recall events live in a separate table). Closes research backlog P1 item 2 (icarus).
15+
- **Auto training_value classification**`think` classifies each thought as high / medium / low (decision + outcome regexes, RU+EN) and stores it in the temporal thought-event metadata. Pure function, no new tool/config/DB. Closes research backlog P1 item 3 (icarus).
1516

1617
## [1.8.0] - 2026-08-26
1718

docs/tools/reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ All tools accept a `layer` parameter (`user` or `agent`). Layers are fully isola
2222

2323
### `think`
2424

25-
Universal write primitive: routes a thought to the correct storage based on content size, importance, and emotional weight. Never silently drops content.
25+
Universal write primitive: routes a thought to the correct storage based on content size, importance, and emotional weight. Never silently drops content. Auto-classifies each thought as high / medium / low `training_value` (decision + outcome regexes, RU+EN) into its temporal event metadata.
2626

2727
```json
2828
{ "text": "We decided to use SQLite over Postgres — zero-config deployment matters more than concurrency here.", "layer": "auto" }

mcp_server/tools/primitives/think.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
)
2424
from mcp_server.tools.primitives.routing import _auto_route
2525

26+
from shared.importance.training import classify_training_value
27+
2628
from mcp_server.context import AppContext # noqa: TC001 — runtime: MCPServer evaluates this annotation at registration
2729

2830
logger = logging.getLogger(__name__)
@@ -128,7 +130,7 @@ async def think(
128130
"thought",
129131
text[:200],
130132
importance=float(importance),
131-
metadata={"resolved_layer": resolved_layer, "actions": len(actions)},
133+
metadata={"resolved_layer": resolved_layer, "actions": len(actions), "training_value": classify_training_value(text)},
132134
layer=resolved_layer,
133135
)
134136

shared/importance/training.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
scanning for decision markers (DECISION_RE) and outcome markers (OUTCOME_RE).
55
Pure function; no DB, no IO. Value rides the temporal "thought" event metadata.
66
"""
7+
78
from __future__ import annotations
89

910
import re

tests/shared/test_training.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Tests for shared.importance.training (training_value classifier)."""
2+
23
from __future__ import annotations
34

45
from shared.importance.training import classify_training_value

tests/test_temporal_wiring.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@ def __init__(self, a):
9292
assert res["status"] == "ok"
9393

9494
events = await app.temporal.get_recent("tu", layer="agent")
95-
assert any(e.event_type == "thought" for e in events)
95+
thought_events = [e for e in events if e.event_type == "thought"]
96+
assert thought_events, "expected at least one thought event"
97+
# "I decided to use sqlite WAL for storage" has 'decided' -> medium
98+
assert thought_events[0].metadata.get("training_value") == "medium"
9699

97100

98101
@pytest.mark.asyncio

0 commit comments

Comments
 (0)