fix: README audit — 42 TDD tests + hall detection + 7 claim fixes (#835)
* fix: README audit — match every claim to shipped code + add hall detection TDD audit: wrote 42 tests verifying README claims against codebase. Fixed all 7 failures: 1. Tool count: 19 → 29 (10 tools were undocumented) 2. Added tool table rows for tunnels, drawer management, system tools 3. Version badge: 3.1.0 → 3.2.0 4. dialect.py file reference: "30x lossless" → "AAAK index format for closet pointers" 5. Wake-up token cost: "~170 tokens" → "~600-900 tokens" (matches layers.py) 6. pyproject.toml version in project structure: v3.0.0 → v3.2.0 7. Hall detection: added detect_hall() to miner.py — drawers now tagged with hall metadata so palace_graph.py can build hall connections New code: - miner.py: detect_hall() — keyword scoring against config hall_keywords, writes hall field to every drawer's metadata - tests/test_hall_detection.py — 12 TDD tests (written before code) - tests/test_readme_claims.py — 42 TDD tests verifying README accuracy 859/859 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve ruff lint — unused imports and variables Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: ruff format with CI-pinned 0.4.x Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use conftest fixtures in hall tests for Windows compat Windows CI fails with NotADirectoryError when ChromaDB tries to write HNSW files in short-lived TemporaryDirectory. Use conftest palace_path and tmp_dir fixtures instead — same pattern as all other tests that touch ChromaDB. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Igor's review — convo_miner halls, cached config, markdown typo TDD: wrote tests for convo_miner hall metadata and config caching BEFORE verifying the code changes. 1. README markdown typo: extra ** in wake-up token row (line 195) 2. convo_miner.py: added _detect_hall_cached() — conversation drawers now get hall metadata (was missing, Igor caught it) 3. miner.py + convo_miner.py: cached hall_keywords at module level so config.json isn't re-read per drawer during bulk mine 4. New tests: TestConvoMinerWritesHalls, TestDetectHallCaching 861/861 tests pass. ruff clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
"""TDD tests for hall detection in miners.
|
||||
|
||||
Written BEFORE the code — these define what correct hall assignment looks like.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
class TestDetectHall:
|
||||
"""The detect_hall function should exist and route content to the right hall."""
|
||||
|
||||
def test_function_exists(self):
|
||||
from mempalace.miner import detect_hall
|
||||
|
||||
assert callable(detect_hall)
|
||||
|
||||
def test_technical_content(self):
|
||||
from mempalace.miner import detect_hall
|
||||
|
||||
text = "Fixed the python script bug in the error handler code"
|
||||
assert detect_hall(text) == "technical"
|
||||
|
||||
def test_emotions_content(self):
|
||||
from mempalace.miner import detect_hall
|
||||
|
||||
text = "I feel so happy today, tears of joy, I love this"
|
||||
assert detect_hall(text) == "emotions"
|
||||
|
||||
def test_family_content(self):
|
||||
from mempalace.miner import detect_hall
|
||||
|
||||
text = "The kids had a great day, my daughter was amazing"
|
||||
assert detect_hall(text) == "family"
|
||||
|
||||
def test_memory_content(self):
|
||||
from mempalace.miner import detect_hall
|
||||
|
||||
text = "I remember when we archived all those files, recall the conversation"
|
||||
assert detect_hall(text) == "memory"
|
||||
|
||||
def test_creative_content(self):
|
||||
from mempalace.miner import detect_hall
|
||||
|
||||
text = "The game design for the player app looks great"
|
||||
assert detect_hall(text) == "creative"
|
||||
|
||||
def test_identity_content(self):
|
||||
from mempalace.miner import detect_hall
|
||||
|
||||
text = "Who am I really? My identity and persona and sense of self"
|
||||
assert detect_hall(text) == "identity"
|
||||
|
||||
def test_consciousness_content(self):
|
||||
from mempalace.miner import detect_hall
|
||||
|
||||
text = "Am I conscious? Is this awareness real? Does my soul exist?"
|
||||
assert detect_hall(text) == "consciousness"
|
||||
|
||||
def test_general_fallback(self):
|
||||
from mempalace.miner import detect_hall
|
||||
|
||||
text = "The weather is nice today in California"
|
||||
assert detect_hall(text) == "general"
|
||||
|
||||
def test_highest_score_wins(self):
|
||||
from mempalace.miner import detect_hall
|
||||
|
||||
# More technical keywords than emotional
|
||||
text = "Fixed the python bug in the code script, felt happy about it"
|
||||
assert detect_hall(text) == "technical"
|
||||
|
||||
|
||||
class TestDrawerHasHallMetadata:
|
||||
"""When a drawer is created, it must have a hall field in metadata."""
|
||||
|
||||
def test_add_drawer_includes_hall(self, palace_path):
|
||||
from mempalace.palace import get_collection
|
||||
from mempalace.miner import add_drawer
|
||||
|
||||
col = get_collection(palace_path)
|
||||
add_drawer(
|
||||
collection=col,
|
||||
wing="test",
|
||||
room="general",
|
||||
content="Fixed the python script bug in the error handler code",
|
||||
source_file=os.path.join(palace_path, "test.py"),
|
||||
chunk_index=0,
|
||||
agent="test",
|
||||
)
|
||||
results = col.get(limit=1, include=["metadatas"])
|
||||
meta = results["metadatas"][0]
|
||||
assert "hall" in meta, "Drawer metadata must include 'hall' field"
|
||||
assert meta["hall"] == "technical"
|
||||
|
||||
|
||||
class TestConvoMinerWritesHalls:
|
||||
"""Conversation miner must also tag drawers with hall metadata."""
|
||||
|
||||
def test_convo_miner_drawers_have_hall(self, tmp_dir):
|
||||
from mempalace.palace import get_collection
|
||||
from mempalace.convo_miner import mine_convos
|
||||
|
||||
palace_dir = os.path.join(tmp_dir, "palace")
|
||||
os.makedirs(palace_dir)
|
||||
convo_dir = os.path.join(tmp_dir, "convos")
|
||||
os.makedirs(convo_dir)
|
||||
# Create a conversation file with technical content
|
||||
with open(os.path.join(convo_dir, "session.txt"), "w") as f:
|
||||
f.write("> How do I fix the python script bug?\n")
|
||||
f.write("You need to check the error handler code and fix the traceback.\n")
|
||||
f.write("> What about the database migration?\n")
|
||||
f.write("Run the migration script to update the schema.\n")
|
||||
|
||||
mine_convos(convo_dir, palace_dir, wing="test", agent="test")
|
||||
|
||||
col = get_collection(palace_dir, create=False)
|
||||
results = col.get(limit=10, include=["metadatas"])
|
||||
# At least some drawers should exist and have hall
|
||||
assert len(results["ids"]) > 0, "No drawers created by convo_miner"
|
||||
for meta in results["metadatas"]:
|
||||
if meta.get("ingest_mode") == "convos":
|
||||
assert "hall" in meta, f"Convo drawer missing hall metadata: {meta}"
|
||||
|
||||
|
||||
class TestDetectHallCaching:
|
||||
"""detect_hall should cache config to avoid disk reads per drawer."""
|
||||
|
||||
def test_detect_hall_does_not_reread_config(self):
|
||||
"""After first call, config should be cached — no new MempalaceConfig()."""
|
||||
import mempalace.miner as miner_mod
|
||||
|
||||
# Reset cache
|
||||
miner_mod._HALL_KEYWORDS_CACHE = None
|
||||
|
||||
# First call loads config
|
||||
miner_mod.detect_hall("Fixed the python bug in the code")
|
||||
assert miner_mod._HALL_KEYWORDS_CACHE is not None
|
||||
|
||||
# Save reference
|
||||
cached_ref = miner_mod._HALL_KEYWORDS_CACHE
|
||||
|
||||
# Second call should use same cached object
|
||||
miner_mod.detect_hall("I feel so happy today")
|
||||
assert miner_mod._HALL_KEYWORDS_CACHE is cached_ref
|
||||
|
||||
|
||||
class TestMineProjectWritesHalls:
|
||||
"""Full mine pipeline must produce drawers with hall metadata."""
|
||||
|
||||
def test_mined_drawers_have_hall(self, tmp_dir):
|
||||
from mempalace.palace import get_collection
|
||||
from mempalace.miner import mine
|
||||
|
||||
palace_dir = os.path.join(tmp_dir, "palace")
|
||||
os.makedirs(palace_dir)
|
||||
project_dir = os.path.join(tmp_dir, "project")
|
||||
os.makedirs(project_dir)
|
||||
# Create config
|
||||
config = {"wing": "test", "rooms": [{"name": "general", "description": "all"}]}
|
||||
with open(os.path.join(project_dir, "mempalace.yaml"), "w") as f:
|
||||
yaml.dump(config, f)
|
||||
# Create test file with technical content
|
||||
with open(os.path.join(project_dir, "code.py"), "w") as f:
|
||||
f.write("def fix_bug():\n # Fixed python script error in handler\n pass\n")
|
||||
|
||||
mine(project_dir, palace_dir, wing_override="test", agent="test")
|
||||
|
||||
col = get_collection(palace_dir, create=False)
|
||||
results = col.get(limit=10, include=["metadatas"])
|
||||
for meta in results["metadatas"]:
|
||||
assert "hall" in meta, f"Drawer missing hall metadata: {meta}"
|
||||
Reference in New Issue
Block a user