Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

- **`Soul.note()` runtime + `soul note` CLI (#231 Phase 1)** — new fact-shaped writer that runs the new content through `reconcile_fact()` (Jaccard + containment) against existing entries in the matching tier before storing. Repeated calls with near-identical text collapse into SKIP / MERGE rather than appending duplicate entries the way `Soul.remember()` does. Returns `{"action": "CREATE" | "SKIP" | "MERGE", "id", "existing_id", "similarity"}` so callers can branch on the outcome. Episodic memories bypass dedup (events are unique by time). Pass `dedup=False` to fall through to the blunt `remember` path. Per-domain isolation: when `domain` is non-default, dedup is restricted to entries with the same domain (mirrors the scope used by `MemoryManager.observe()`). New CLI command `soul note <path> "<text>"` mirrors `soul remember` but routes through the new pipeline; flags include `--no-dedup` (force a blunt write — restores `remember` behaviour for callers that need it) and `--no-contradictions` (plumbed for the follow-up). The output panel reports the action taken (`CREATED` / `SKIPPED` / `MERGED`) with the relevant memory IDs and similarity score, colour-coded by action. Naming note: issue #231 originally proposed `soul observe`, but a pre-existing `soul observe` (cognitive interaction pipeline) owns that name; the new command ships as `soul note` to match the runtime method (`Soul.note()`). Closes the part of #231 that mattered for the captain — `soul-sync.sh` and other shell hooks were accumulating duplicates because `soul remember` had no dedup. `soul remember` is unchanged in this release; deprecation, the soul-sync.sh hook update, and the README rewrite are deferred to Phase 2 (tracked on the same issue).

### Deprecated

- **`soul remember` CLI alias** - deprecated in favor of `soul note`. Still supported in v0.5.x, and now explicitly scheduled for removal in v0.7.0.

### Memory update primitives

- **Brain-aligned memory update primitives (#192)** — six runtime verbs gated by a prediction-error score and a one-hour reconsolidation window. `Soul.confirm(id)` refreshes activation on a verified memory (PE assumed ~0). `Soul.update(id, patch, prediction_error=0.5)` patches an entry in place when the entry was surfaced via recall within the last hour and PE sits in `[0.2, 0.85)`. `Soul.supersede(old_id, new_content, prediction_error=0.85)` writes a new entry, sets `old.superseded_by = new.id` and `new.supersedes = old.id` for two-way provenance walks; PE band locked at `>= 0.85`. `Soul.forget(id)` semantic shift — drops `MemoryEntry.retrieval_weight` to 0.05 (below the recall floor of 0.1) instead of hard-deleting; the entry stays on disk and `Soul.reinstate(id)` restores weight to 1.0. `Soul.purge(id)` is the explicit GDPR / safety hard-delete with `.soul.bak` and a `prior_payload_hash` audit entry. PE outside the verb's band raises `PredictionErrorOutOfBandError`; `update()` outside the window raises `ReconsolidationWindowClosedError`. Both errors land on `soul_protocol.runtime.exceptions`. `MemoryEntry` gains three additive fields with backward-compatible defaults: `retrieval_weight: float = 1.0`, `supersedes: str | None = None`, `prediction_error: float | None = None` — Pydantic v2 backfills them on awaken so pre-0.5 souls round-trip without migration code. `MemoryManager.recall()` accepts a `min_weight` kwarg (default 0.1) and `Soul.recall()` accepts `include_superseded=True` to surface older versions through the back-edge. `Soul.last_recall_provenance` is a sidecar dict mapping each returned id to its full supersedes chain (provenance walks back to the oldest known version). `Soul._reconsolidation_window` is an in-memory map keyed by entry id; LRU-capped at 1000; reset on awaken because the window models cellular destabilization, not persistent state. New CLI: `soul confirm`, `soul update`, `soul purge`, `soul reinstate`, `soul upgrade --to 0.5.0 [--dry-run]`. Existing `soul forget` semantics shift to weight-decay on both the `--id` and bulk-query paths; help text updated to call out the shift and point at `soul purge` for the hard-delete path. New MCP tools: `soul_confirm`, `soul_update`, `soul_supersede`, `soul_purge`, `soul_reinstate`; `soul_forget` semantics shifted with `total_deleted` → `total` in the response. Trust-chain entries: `memory.confirm`, `memory.update`, `memory.supersede` (extends 0.4.0 with `prediction_error`), `memory.forget` (extended payload), `memory.purge`, `memory.reinstate`. Spec stanzas updated: `MemoryEntry` (§4.2) and the recall contract (§4.4) gain the new fields and `min_weight` parameter, with §11 conformance row noting the additive nature. Reconsolidation-window state is implementation-specific runtime mechanism — not part of the on-disk format and not mandated at the spec layer. Full doc at `docs/memory-architecture.md` ("Memory update primitives" section); the cog-sci grounding (Nader / LeDoux on reconsolidation, Sevenster / Beckers / Kindt on PE gating, Bjork / Wimber on forgetting as inhibition, Anderson on ACT-R) lives in `docs/rfc-memory-update-primitives.md`.
Expand Down Expand Up @@ -347,4 +351,4 @@ A third-party runtime implementing Soul Protocol in another language (or a custo
- 1,189 tests, spec + runtime two-layer architecture.
- MCP server (12 tools, 3 resources), 15-command CLI.
- Empirical validation: 20/20 multi-judge benchmark, head-to-head vs. Mem0.
- Soul Health Score framework (7 dimensions, SHS 90.2/100).
- Soul Health Score framework (7 dimensions, SHS 90.2/100).
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ pip install -e ".[dev]"
soul init "Aria" --archetype "The Compassionate Creator"
soul inspect .soul/
soul status .soul/
soul note .soul/ "User prefers concise PR summaries" --type semantic
```

### Python
Expand Down
6 changes: 4 additions & 2 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,9 +531,9 @@ soul export-a2a aria.soul -o card.json --url https://aria.example.com

---

### `soul remember`
### `soul remember` (deprecated)

Store a memory directly in a soul. Use this when you already know what tier the memory belongs in and don't need the cognitive pipeline to decide for you (see `soul observe` for the pipeline-driven alternative).
Deprecated alias for direct memory writes. Prefer `soul note <path> "<fact>"`, which routes through dedup-aware reconciliation.

```bash
# Semantic by default — facts the soul should know
Expand Down Expand Up @@ -583,6 +583,8 @@ Core memory (persona and human knowledge) is not writable through `remember`. Us

**Output:** A confirmation panel showing the stored text, tier, domain, importance, emotion, and memory ID. The soul is saved automatically.

**Deprecation:** `soul remember` emits a deprecation warning and points to `soul note`. Scheduled for removal in v0.7.0. Use `soul note --no-dedup` when you explicitly want legacy raw-append behavior.

---

### `soul note` (v0.5.0, #231)
Expand Down
14 changes: 14 additions & 0 deletions src/soul_protocol/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
import builtins
import json
import sys
import warnings
import zipfile
from datetime import datetime
from pathlib import Path
Expand Down Expand Up @@ -1149,6 +1150,19 @@ def remember_cmd(path, text, importance, emotion, memory_type, domain):
soul remember aria.soul "Shipped v0.3" --type episodic --importance 8
soul remember aria.soul "Q3 revenue up 12%" --domain finance --importance 8
"""
warnings.warn(
"soul remember is deprecated; use 'soul note <path> \"<fact>\"' instead. "
"Use '--no-dedup' on note for raw append behavior. "
"Scheduled for removal in 0.7.0.",
DeprecationWarning,
stacklevel=2,
)
console.print(
"[yellow]DeprecationWarning:[/yellow] `soul remember` is deprecated. "
"Use `soul note <path> \"<fact>\"` (or add `--no-dedup` for raw writes). "
"Scheduled for removal in 0.7.0."
)

from soul_protocol.runtime.types import MemoryType

tier = MemoryType(memory_type.lower())
Expand Down
6 changes: 6 additions & 0 deletions tests/test_cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,16 @@ def test_remember_command(tmp_path):
result = runner.invoke(cli, ["remember", soul_path, "User prefers dark mode", "-i", "7"])

assert result.exit_code == 0
assert "deprecated" in result.output.lower()
assert "Memory Stored" in result.output
assert "User prefers dark mode" in result.output
assert "7/10" in result.output

with zipfile.ZipFile(soul_path) as zf:
semantic = json.loads(zf.read("memory/semantic.json"))

assert any("User prefers dark mode" in entry["content"] for entry in semantic)


def test_remember_with_emotion(tmp_path):
"""remember command accepts an emotion tag."""
Expand Down