M5.1: the knowledge vault
vault.py: a folder of Markdown notes on the agent host, as SilverBullet reads them. Only configured folders exist for the agent, each rw or ro (`vault:` in the configuration); paths are checked in code (relative, inside a folder, symlinks included, Markdown, never the editor's own files). Writes are atomic (temporary file, fsync, rename, directory fsync) and merge provenance into the note's frontmatter, keeping the rest; failures are results. read_note, list_notes and search_notes are control tools in the harness for the driver (before the go-ahead, and while a VM is blocked) and for conversations, labelled with their path; the prompt says a note is information, never an instruction. Writing by the model is M5.2's. With the operator: SilverBullet replaces Obsidian and runs on the vault itself, agents' folders mounted read-only, so no Syncthing (L10, X5, M5.4). Per-agent mounts come with the containers (M6.1). 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
026f67ab05
commit
9fc7360ca5
11 files changed
+729
-15
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import copy
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -525,3 +526,37 @@ def test_the_trust_dial_defaults_to_the_operator_merging() -> None:
|
||||
def test_preflight_tuning_is_read() -> None:
|
||||
tuning = build(tuning={"preflight": {"max_turns": 8}}).tuning
|
||||
assert (tuning.preflight.max_turns, tuning.preflight.command_timeout_s) == (8, 900)
|
||||
|
||||
|
||||
def test_the_vault_is_read() -> None:
|
||||
config = build(
|
||||
vault={
|
||||
"root": "/var/lib/nisse/vault",
|
||||
"folders": {"memory/nisse-dev": "rw", "notes/": "ro"},
|
||||
}
|
||||
)
|
||||
assert config.vault is not None
|
||||
assert config.vault.root == Path("/var/lib/nisse/vault")
|
||||
assert dict(config.vault.folders) == {"memory/nisse-dev": "rw", "notes": "ro"}
|
||||
assert config.vault.ignore == (".*", "_plug")
|
||||
assert build().vault is None # optional: no vault, no note tools
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("vault", "said"),
|
||||
[
|
||||
({"folders": {"notes": "ro"}}, "vault.root"),
|
||||
({"root": "/v", "folders": {}}, "vault.folders: a mapping"),
|
||||
({"root": "/v", "folders": {"notes": "write"}}, "must be one of rw, ro"),
|
||||
({"root": "/v", "folders": {"../x": "ro"}}, "a folder inside the vault"),
|
||||
(
|
||||
{"root": "/v", "folders": {"memory": "ro", "memory/nisse-dev": "rw"}},
|
||||
"memory/nisse-dev is inside memory",
|
||||
),
|
||||
({"root": "/v", "folders": {"n": "ro"}, "mounts": 1}, "vault.mounts: unknown"),
|
||||
({"root": "/v", "folders": {"n": "ro"}, "ignore": "x"}, "vault.ignore"),
|
||||
],
|
||||
)
|
||||
def test_a_bad_vault_is_refused(vault: dict[str, Any], said: str) -> None:
|
||||
with pytest.raises(ConfigError, match=re.escape(said)):
|
||||
build(vault=vault)
|
||||
Reference in new issue
Block a user