refactor(P1): extract the turn pipeline into lyra/mind.py (behavior-preserving)

First step of the cognition control plane (docs/COGNITION.md). The chat turn is now
an explicit society of parts over a shared TurnContext blackboard:
  perceive (stub) -> route (session mode) -> compose (tiered prompt) -> deliberate.

- lyra/mind.py (new): TurnContext + the pipeline + assemble(); moved build_messages
  and the deliberation helpers here (the assembly belongs in the control plane).
- lyra/chat.py: slimmed to "speak + persist" — calls mind.assemble(), runs the
  tool/generation loop, persists. No behavior change (same prompt, same output).
- tests: point test_time/test_chat at mind; add an assemble() structure test;
  make test_chat/test_tools hermetic (CHAT_DELIBERATE off so respond() doesn't make
  a real LLM call). Suite 86 green in ~5s, ruff clean, no import cycle.

This is the frame; perceive/route/learn get filled in next phases — each opt-in.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-24 05:19:39 +00:00
parent f1f15972ac
commit 904eda3388
5 changed files with 345 additions and 299 deletions
+4 -4
View File
@@ -39,8 +39,8 @@ def lyra(tmp_path, monkeypatch):
def test_now_note_first_contact(lyra):
from lyra import chat
note = chat._now_note()["content"]
from lyra import mind
note = mind._now_note()["content"]
assert "current date and time is" in note
assert "first thing Brian has ever said" in note
@@ -48,6 +48,6 @@ def test_now_note_first_contact(lyra):
def test_now_note_reports_gap(lyra):
memory = lyra
memory.remember("s1", "user", "hey")
from lyra import chat
note = chat._now_note()["content"]
from lyra import mind
note = mind._now_note()["content"]
assert "since Brian last spoke with you" in note