M5.3: scope memory
A project's notes have their own folder, memory/nisse-dev/projects/<project>/, with its own index; remember and forget take scope (general, or project: always the current item's own, so no item writes another project's notes, and a conversation has none). At an item's start the transcript gets the general index and its project's, never another's. Index links are pages from the vault's root, as SilverBullet names them. The closing pass's memory_done can name repository documents the item left out of date; the harness says so in the thread and records it. The prompt: the repository is the authority, and a note that disagrees is corrected. With the operator: ARCHITECTURE.md isn't put into every item's context (its size); the infrastructure world model waits for nisse-ops. The walk writes project notes and checks they come only from that project's items. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
This commit is contained in:
1 parent
427278ac80
commit
fe4548459f
8 files changed
+301
-66
No files matched your search
@@ -72,7 +72,7 @@ def test_the_model_remembers_and_forgets_without_asking(
|
||||
assert "Remembered: Count words with split" in said(transport)
|
||||
assert "Forgot: old-style (the operator changed it)" in said(transport)
|
||||
index = memory.snapshot().text
|
||||
assert "[[uses-split]]" in index and "old-style" not in index
|
||||
assert "[[memory/nisse-dev/uses-split]]" in index and "old-style" not in index
|
||||
|
||||
|
||||
def test_the_index_is_shown_once_per_item(
|
||||
@@ -86,7 +86,7 @@ def test_the_index_is_shown_once_per_item(
|
||||
shown = [f for f in facts(world) if f.startswith("your memory's index")]
|
||||
assert len(shown) == 1
|
||||
assert f"sha {memory.snapshot().sha}" in shown[0]
|
||||
assert "[[uses-split]] — Count words with split" in shown[0]
|
||||
assert "[[memory/nisse-dev/uses-split]] — Count words with split" in shown[0]
|
||||
|
||||
|
||||
def test_a_pass_at_the_close_records_what_will_matter(
|
||||
@@ -109,7 +109,7 @@ def test_a_pass_at_the_close_records_what_will_matter(
|
||||
say(transport, world, "abandon")
|
||||
assert the_item(world).status == "closed"
|
||||
assert "memory pass: recording what this item decided and learned" in facts(world)
|
||||
assert "[[no-count-words]]" in memory.snapshot().text
|
||||
assert "[[memory/nisse-dev/no-count-words]]" in memory.snapshot().text
|
||||
offered = model.offered[-1]
|
||||
assert offered == {
|
||||
"read_note",
|
||||
@@ -156,5 +156,80 @@ def test_a_conversation_can_remember(world: dict[str, Any], memory: Memory) -> N
|
||||
)
|
||||
_, transport = agent(world, model, memory)
|
||||
transport.receive(DON, "I have a 4090 for local models.")
|
||||
assert "[[has-4090]]" in memory.snapshot().text
|
||||
assert "[[memory/nisse-dev/has-4090]]" in memory.snapshot().text
|
||||
assert "Remembered: Has a 4090" in said(transport)
|
||||
|
||||
|
||||
def test_a_project_note_goes_with_the_items_project(
|
||||
world: dict[str, Any], memory: Memory
|
||||
) -> None:
|
||||
memory.remember("other-fact", "Other's", "o", provenance={}, project="other")
|
||||
memory.remember("old", "Sandbox's", "s", provenance={}, project="sandbox")
|
||||
model = Spy(
|
||||
[
|
||||
call(
|
||||
"remember",
|
||||
name="deploys-by-tag",
|
||||
description="Sandbox deploys by tag",
|
||||
fact="f",
|
||||
scope="project",
|
||||
),
|
||||
call("remember", name="x", description="d", fact="f", scope="team"),
|
||||
],
|
||||
RESTATE,
|
||||
)
|
||||
loop, _ = agent(world, model, memory)
|
||||
loop.intake()
|
||||
note = memory._vault.read(f"{FOLDER}/projects/sandbox/deploys-by-tag.md")
|
||||
assert "project: sandbox" in note
|
||||
[shown] = [f for f in facts(world) if f.startswith("your memory's index")]
|
||||
assert "projects/sandbox/old]]" in shown # this project's notes
|
||||
assert "other-fact" not in shown # never another's
|
||||
results = [
|
||||
e.event.result
|
||||
for e in world["store"].transcript(the_item(world).id, "driver").read()
|
||||
if hasattr(e.event, "result")
|
||||
]
|
||||
assert "scope is general or project, not 'team'" in results
|
||||
|
||||
|
||||
def test_a_conversation_has_no_project_scope(
|
||||
world: dict[str, Any], memory: Memory
|
||||
) -> None:
|
||||
world["forge"].issues.clear()
|
||||
model = Spy(
|
||||
[call("remember", name="x", description="d", fact="f", scope="project")],
|
||||
[call("reply", text="Noted.")],
|
||||
)
|
||||
_, transport = agent(world, model, memory)
|
||||
transport.receive(DON, "remember this")
|
||||
assert not (memory._vault.root / FOLDER / "projects").exists()
|
||||
log = world["store"].all_events()
|
||||
assert any(
|
||||
getattr(e.event, "result", "") == "a conversation has no project: use general"
|
||||
for e in log
|
||||
)
|
||||
|
||||
|
||||
def test_the_pass_flags_documents_left_out_of_date(
|
||||
world: dict[str, Any], memory: Memory
|
||||
) -> None:
|
||||
model = Spy(
|
||||
RESTATE,
|
||||
[
|
||||
call(
|
||||
"memory_done",
|
||||
summary="nothing new",
|
||||
docs="ARCHITECTURE.md still describes the old text module",
|
||||
)
|
||||
],
|
||||
)
|
||||
loop, transport = agent(world, model, memory)
|
||||
loop.intake()
|
||||
say(transport, world, "abandon")
|
||||
line = (
|
||||
"Docs to update in don/sandbox: ARCHITECTURE.md still describes the old "
|
||||
"text module"
|
||||
)
|
||||
assert line in said(transport)
|
||||
assert line in facts(world)
|
||||
Reference in new issue
Block a user