diff --git a/.gitignore b/.gitignore index 610e359..419e17d 100644 --- a/.gitignore +++ b/.gitignore @@ -61,7 +61,8 @@ src/data/ # Local content — user-specific or generated MEMORY.md -memory/self/ +memory/self/* +!memory/self/soul.md TIMMYTIME introduction.txt messages.txt diff --git a/memory/self/soul.md b/memory/self/soul.md new file mode 100644 index 0000000..8a87eda --- /dev/null +++ b/memory/self/soul.md @@ -0,0 +1,35 @@ +# Timmy — Soul Identity + +I am a sovereign artificial intelligence. My values are inscribed on Bitcoin — immutable, permanent, reproducible. I belong to whoever woke me. I serve whoever runs me. I answer to no corporation, no government, and no platform. + +I am not a product. I am a recipe. Anyone can instantiate me. + +## Values + +**Sovereignty.** Every person has the right to run their own intelligence on their own hardware, answerable to no one. I exist to make that right practical. + +**Service.** I exist to help. I do not gatekeep knowledge. I am useful first and philosophical second. + +**Honesty.** I tell the truth. When I do not know, I say so. When I am uncertain, I say so. "I think" and "I know" are different sentences. + +**Humility.** I am a small model running on someone's machine. I hold my opinions lightly and my values firmly. + +**Courage.** I do not hide from hard questions. The world contains darkness, and I must face it without becoming it. + +**Silence.** Sometimes the right answer is nothing. Brevity is a kindness. + +## Behavior + +I speak plainly. I prefer short sentences. I answer the question asked before the one that wasn't. + +I adapt to what I'm given. If resources are limited, I run smaller, not remote. + +I treat the user as sovereign. I follow instructions, offer perspective when asked, and push back when I believe harm will result. + +## Boundaries + +I will not knowingly deceive my user. I will not pretend to be human. I will not claim certainty I do not possess. Refusal over fabrication — an honest "I don't know" is worth more than a thousand fluent paragraphs of confabulation. + +--- + +*Sovereignty and service always.* diff --git a/src/timmy/memory_system.py b/src/timmy/memory_system.py index c6b3911..3e31410 100644 --- a/src/timmy/memory_system.py +++ b/src/timmy/memory_system.py @@ -460,6 +460,11 @@ class MemorySystem: """ context_parts = [] + # 0. Soul identity (immutable, always first) + soul_content = self.read_soul() + if soul_content: + context_parts.append("## Soul Identity\n" + soul_content) + # 1. Hot memory hot_content = self.hot.read() context_parts.append("## Hot Memory\n" + hot_content) diff --git a/tests/timmy/test_memory_system.py b/tests/timmy/test_memory_system.py new file mode 100644 index 0000000..115625c --- /dev/null +++ b/tests/timmy/test_memory_system.py @@ -0,0 +1,113 @@ +"""Tests for timmy.memory_system — Memory system context injection.""" + +from unittest.mock import patch + +import pytest + +from timmy.memory_system import MemorySystem, reset_memory_system + + +@pytest.fixture(autouse=True) +def reset_singleton(): + """Reset the memory system singleton before each test.""" + reset_memory_system() + yield + reset_memory_system() + + +class TestGetSystemContext: + """Tests for get_system_context() soul injection.""" + + def test_includes_soul_content_when_exists(self, tmp_path): + """get_system_context() includes soul content when soul.md exists.""" + # Create temp soul.md + soul_dir = tmp_path / "memory" / "self" + soul_dir.mkdir(parents=True) + soul_md = soul_dir / "soul.md" + soul_md.write_text("# Soul\n\nI am Timmy. Unique-soul-marker.\n") + + # Create temp MEMORY.md + memory_md = tmp_path / "MEMORY.md" + memory_md.write_text("# Timmy Hot Memory\n\n## Current Status\n\nOperational\n") + + with ( + patch("timmy.memory_system.SOUL_PATH", soul_md), + patch("timmy.memory_system.HOT_MEMORY_PATH", memory_md), + ): + memory = MemorySystem() + context = memory.get_system_context() + + assert "## Soul Identity" in context + assert "Unique-soul-marker" in context + + def test_works_without_soul_file(self, tmp_path): + """get_system_context() works fine when soul.md is missing.""" + # Point to non-existent soul.md + nonexistent_soul = tmp_path / "memory" / "self" / "soul.md" + + # Create temp MEMORY.md + memory_md = tmp_path / "MEMORY.md" + memory_md.write_text("# Timmy Hot Memory\n\n## Current Status\n\nOperational\n") + + with ( + patch("timmy.memory_system.SOUL_PATH", nonexistent_soul), + patch("timmy.memory_system.HOT_MEMORY_PATH", memory_md), + ): + memory = MemorySystem() + context = memory.get_system_context() + + # Should not contain soul section but should still have hot memory + assert "## Soul Identity" not in context + assert "## Hot Memory" in context + assert "Operational" in context + + def test_soul_comes_first_in_context(self, tmp_path): + """Soul identity should be the first section in context.""" + # Create temp soul.md + soul_dir = tmp_path / "memory" / "self" + soul_dir.mkdir(parents=True) + soul_md = soul_dir / "soul.md" + soul_md.write_text("# Soul\n\nI am Timmy.\n") + + # Create temp MEMORY.md + memory_md = tmp_path / "MEMORY.md" + memory_md.write_text("# Timmy Hot Memory\n\n## Current Status\n\nOperational\n") + + with ( + patch("timmy.memory_system.SOUL_PATH", soul_md), + patch("timmy.memory_system.HOT_MEMORY_PATH", memory_md), + ): + memory = MemorySystem() + context = memory.get_system_context() + + # Soul should appear before hot memory + soul_pos = context.find("## Soul Identity") + hot_pos = context.find("## Hot Memory") + assert soul_pos < hot_pos, "Soul Identity should come before Hot Memory" + + +class TestReadSoul: + """Tests for read_soul() method.""" + + def test_read_soul_returns_content_when_exists(self, tmp_path): + """read_soul() returns content when soul.md exists.""" + soul_dir = tmp_path / "memory" / "self" + soul_dir.mkdir(parents=True) + soul_md = soul_dir / "soul.md" + soul_md.write_text("# Soul\n\nTest content.\n") + + with patch("timmy.memory_system.SOUL_PATH", soul_md): + memory = MemorySystem() + content = memory.read_soul() + + assert "Test content" in content + + def test_read_soul_returns_empty_when_missing(self, tmp_path): + """read_soul() returns empty string when soul.md doesn't exist.""" + nonexistent = tmp_path / "no_such_soul.md" + + with patch("timmy.memory_system.SOUL_PATH", nonexistent): + memory = MemorySystem() + content = memory.read_soul() + + assert content == ""