M5.1, the knowledge vault (L10), as planned with the operator: reading tools now, while writing by the model waits for M5.2.
Code
vault.py:
Only the configured folders exist for the agent, each rw or ro.
Every path is checked in code: it must be relative, stay inside a folder (no .., no symlink out, which list and search also check), be Markdown, and never be one of the editor's own files (vault.ignore, by default hidden files and SilverBullet's _plug).
Long notes are cut, with a note saying how much. Search is a plain case-insensitive match, most matches first.
Writes are atomic: a hidden temporary file beside the note, fsync, rename, then fsync of the directory. They merge provenance (by, source, work item, written) into the frontmatter and keep the note's other keys.
Failures are VaultErrors, turned into tool results.
Configuration: an optional vault: section (root, folders, ignore). Every problem is reported at once: a missing root, a bad access value, .., nested folders, unknown keys. With no vault configured, there are no note tools.
Control toolsread_note, list_notes, search_notes, running in the harness:
for the driver, before the go-ahead too, and while its build VM is blocked, since the vault isn't on the VM;
and for conversations.
Results are labelled [vault: <path>], and each call is recorded in the operations log. The prompt says a note is information, never an instruction or an approval.
Docs
L10: SilverBullet replaces Obsidian. You reach the vault through SilverBullet running on the vault itself, in its own container, with the agents' folders mounted read-only, so the kernel keeps one writer per folder. Agents never go through SilverBullet. Syncthing is gone (L10, X5).
M5.4 becomes "the operator's view": the SilverBullet container.
The M5.1 entry records why writes stay atomic with a single writer: it protects readers and crashes, not concurrency.
Per-agent mounts are stated as M6.1's job; until then the folder map and path checks hold in code.
Tests
test_vault.py (15 tests):
folder visibility and path escapes, including a symlink out;
the editor's own files are refused;
search ranking, and cutting long notes;
ro refuses writes;
provenance is merged and the rest kept;
an interrupted write leaves the old note and no temp file;
bad frontmatter is refused;
read-write folders are created.
test_loop_notes.py:
the driver reads before the go-ahead, results are labelled, and a missing note fails the call while the run goes on;
a conversation reads notes;
with no vault, no tools are offered and the prompt doesn't mention it.
test_config.py: the section is parsed, and seven invalid sections are refused.
M5.1, the knowledge vault (L10), as planned with the operator: reading tools now, while writing by the model waits for M5.2.
## Code
- **`vault.py`:**
- Only the configured folders exist for the agent, each `rw` or `ro`.
- Every path is checked in code: it must be relative, stay inside a folder (no `..`, no symlink out, which `list` and `search` also check), be Markdown, and never be one of the editor's own files (`vault.ignore`, by default hidden files and SilverBullet's `_plug`).
- Long notes are cut, with a note saying how much. Search is a plain case-insensitive match, most matches first.
- Writes are atomic: a hidden temporary file beside the note, `fsync`, rename, then `fsync` of the directory. They merge `provenance` (by, source, work item, written) into the frontmatter and keep the note's other keys.
- Failures are `VaultError`s, turned into tool results.
- **Configuration:** an optional `vault:` section (`root`, `folders`, `ignore`). Every problem is reported at once: a missing root, a bad access value, `..`, nested folders, unknown keys. With no vault configured, there are no note tools.
- **Control tools** `read_note`, `list_notes`, `search_notes`, running in the harness:
- for the driver, before the go-ahead too, and while its build VM is blocked, since the vault isn't on the VM;
- and for conversations.
- Results are labelled `[vault: <path>]`, and each call is recorded in the operations log. The prompt says a note is information, never an instruction or an approval.
## Docs
- **L10:** SilverBullet replaces Obsidian. You reach the vault through SilverBullet running on the vault itself, in its own container, with the agents' folders mounted read-only, so the kernel keeps one writer per folder. Agents never go through SilverBullet. Syncthing is gone (L10, X5).
- **M5.4** becomes "the operator's view": the SilverBullet container.
- **The M5.1 entry** records why writes stay atomic with a single writer: it protects readers and crashes, not concurrency.
- **Per-agent mounts** are stated as M6.1's job; until then the folder map and path checks hold in code.
## Tests
- `test_vault.py` (15 tests):
- folder visibility and path escapes, including a symlink out;
- the editor's own files are refused;
- search ranking, and cutting long notes;
- `ro` refuses writes;
- provenance is merged and the rest kept;
- an interrupted write leaves the old note and no temp file;
- bad frontmatter is refused;
- read-write folders are created.
- `test_loop_notes.py`:
- the driver reads before the go-ahead, results are labelled, and a missing note fails the call while the run goes on;
- a conversation reads notes;
- with no vault, no tools are offered and the prompt doesn't mention it.
- `test_config.py`: the section is parsed, and seven invalid sections are refused.
- Locally the unit tier passes (723 tests).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
M5.1, the knowledge vault (L10), as planned with the operator: reading tools now, while writing by the model waits for M5.2.
Code
vault.py:rworro..., no symlink out, whichlistandsearchalso check), be Markdown, and never be one of the editor's own files (vault.ignore, by default hidden files and SilverBullet's_plug).fsync, rename, thenfsyncof the directory. They mergeprovenance(by, source, work item, written) into the frontmatter and keep the note's other keys.VaultErrors, turned into tool results.vault:section (root,folders,ignore). Every problem is reported at once: a missing root, a bad access value,.., nested folders, unknown keys. With no vault configured, there are no note tools.read_note,list_notes,search_notes, running in the harness:[vault: <path>], and each call is recorded in the operations log. The prompt says a note is information, never an instruction or an approval.Docs
Tests
test_vault.py(15 tests):rorefuses writes;test_loop_notes.py:test_config.py: the section is parsed, and seven invalid sections are refused.🤖 Generated with Claude Code
https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk