From 2a91f6c202821641467cea5416415fe00bc08861 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Mon, 2 Mar 2026 21:15:28 -0600 Subject: [PATCH] feat: hard-gate agent memory to OpenBrain via PreToolUse hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agents consistently ignore written instructions about memory routing and default to writing local MEMORY.md files regardless of rules in RUNTIME.md, CLAUDE.md, or MEMORY.md itself. Instructions alone are insufficient — a technical gate is required. Changes: - Add tools/qa/prevent-memory-write.sh — PreToolUse hook that blocks Write/Edit/MultiEdit to ~/.claude/projects/*/memory/*.md (exit 2) - Register hook in runtime/claude/settings.json PreToolUse array - Update runtime/claude/RUNTIME.md: replace soft "Memory Override" note with hard-gate policy, what-goes-where table, and rationale - Rewrite guides/MEMORY.md: OpenBrain as primary layer, blocked silos table, project continuity files, how-the-hook-works section The correct behavior is now the only possible behavior for Claude Code. All agent learnings route to OpenBrain where every harness can read them. Co-Authored-By: Claude Sonnet 4.6 --- guides/MEMORY.md | 53 +++++++++++++++++++++++--------- runtime/claude/RUNTIME.md | 32 +++++++++++++++++-- runtime/claude/settings.json | 12 ++++++++ tools/qa/prevent-memory-write.sh | 34 ++++++++++++++++++++ 4 files changed, 114 insertions(+), 17 deletions(-) create mode 100755 tools/qa/prevent-memory-write.sh diff --git a/guides/MEMORY.md b/guides/MEMORY.md index 40541f5..14bd8fa 100644 --- a/guides/MEMORY.md +++ b/guides/MEMORY.md @@ -1,27 +1,50 @@ # Memory and Retention Rules +## Primary Memory Layer: OpenBrain + +**OpenBrain is the canonical shared memory for all Mosaic agents across all harnesses and sessions.** + +Use the `capture` MCP tool (or REST `POST /v1/thoughts`) to store: +- Discovered gotchas and workarounds +- Architectural decisions and rationale +- Project state and context for handoffs +- Anything a future agent should know + +Use `search` or `recent` at session start to load prior context before acting. + +This is not optional. An agent that uses local file-based memory instead of OpenBrain is a broken agent — its knowledge is invisible to every other agent on the platform. + ## Hard Rules -1. You MUST store learned operational memory in `~/.config/mosaic/memory`. -2. You MUST NOT store durable memory in runtime-native memory silos. -3. You MUST write concise, reusable learnings that help future agents. -4. You MUST track active execution state in project documentation, not ad-hoc text files. +1. Agent learnings MUST go to OpenBrain — not to any file-based memory location. +2. You MUST NOT write to runtime-native memory silos (they are write-blocked by hook). +3. Active execution state belongs in project `docs/` — not in memory files. +4. `~/.config/mosaic/memory/` is for mosaic framework technical notes only, not project knowledge. -## Runtime-Native Memory Silos (FORBIDDEN for Durable Memory) +## Runtime-Native Memory Silos (WRITE-BLOCKED) -| Runtime | Native silo (forbidden for durable memory) | Required durable location | -|---|---|---| -| Claude Code | `~/.claude/projects/*/memory/` | `~/.config/mosaic/memory/` + project `docs/` | -| Codex | Runtime session/native memory under `~/.codex/` | `~/.config/mosaic/memory/` + project `docs/` | -| OpenCode | Runtime session/native memory under `~/.config/opencode/` | `~/.config/mosaic/memory/` + project `docs/` | +These locations are blocked by PreToolUse hooks. Attempting to write there fails at the tool level. -Treat runtime-native memory as volatile implementation detail. Do not rely on it for long-term project continuity. +| Runtime | Blocked silo | Use instead | +|---------|-------------|-------------| +| Claude Code | `~/.claude/projects/*/memory/*.md` | OpenBrain `capture` | +| Codex | Runtime session memory | OpenBrain `capture` | +| OpenCode | Runtime session memory | OpenBrain `capture` | + +MEMORY.md files may only contain behavioral guardrails that must be injected at load-path — not knowledge. ## Project Continuity Files (MANDATORY) | File | Purpose | Location | |---|---|---| -| `docs/PRD.md` or `docs/PRD.json` | Source of requirements for planning, coding, testing, and review | Project `docs/` | -| `docs/TASKS.md` | Canonical tracking for tasks, milestones, issues, status, and blockers | Project `docs/` | -| `docs/scratchpads/.md` | Task-specific working memory and verification evidence | Project `docs/scratchpads/` | -| `AGENTS.md` | Reusable local patterns and gotchas | Any working directory | +| `docs/PRD.md` or `docs/PRD.json` | Source of requirements | Project `docs/` | +| `docs/TASKS.md` | Task tracking, milestones, issues, status | Project `docs/` | +| `docs/scratchpads/.md` | Task-specific working memory | Project `docs/scratchpads/` | +| `AGENTS.md` | Project-local patterns and conventions | Project root | + +## How the Block Works + +`~/.config/mosaic/tools/qa/prevent-memory-write.sh` is registered as a `PreToolUse` hook in +`~/.claude/settings.json`. It intercepts Write/Edit/MultiEdit calls and rejects any targeting +`~/.claude/projects/*/memory/*.md` before the tool executes. Exit code 2 blocks the call and +the agent sees a message directing it to OpenBrain instead. diff --git a/runtime/claude/RUNTIME.md b/runtime/claude/RUNTIME.md index b4258de..dda6ebf 100644 --- a/runtime/claude/RUNTIME.md +++ b/runtime/claude/RUNTIME.md @@ -46,9 +46,37 @@ Task(subagent_type="Plan", model="opus", prompt="Design the multi-tenant isolati | Status/health checks | Test writing | Security/auth logic | | Simple one-liner fixes | Standard features | Ambiguous design decisions | -## Memory Override +## Memory Policy (Hard Gate) -Do NOT write durable memory to `~/.claude/projects/*/memory/`. All durable memory MUST be written to `~/.config/mosaic/memory/` per `~/.config/mosaic/guides/MEMORY.md`. Claude Code's native auto-memory locations are volatile runtime silos and MUST NOT be used for cross-session or cross-agent retention. +**OpenBrain is the primary cross-agent memory layer.** All agent learnings, gotchas, decisions, and project state MUST be captured to OpenBrain via the `capture` MCP tool or REST API. + +`~/.claude/projects/*/memory/MEMORY.md` files are **write-blocked by PreToolUse hook** (`prevent-memory-write.sh`). Any attempt to write agent learnings there will be rejected with an error directing you to OpenBrain. + +### What belongs where + +| Content | Location | +|---------|----------| +| Discoveries, gotchas, decisions, observations | OpenBrain `capture` — searchable by all agents | +| Active task state | `docs/TASKS.md` or `docs/scratchpads/` | +| Behavioral guardrails that MUST be in load-path | `MEMORY.md` (read-mostly; write only for genuine behavioral overrides) | +| Mosaic framework technical notes | `~/.config/mosaic/memory/` | + +### Using OpenBrain + +At session start, load prior context: +``` +search("topic or project name") # semantic search +recent(limit=5) # what's been happening +``` + +When you discover something: +``` +capture("The thing you learned", source="project/context", metadata={"type": "gotcha", ...}) +``` + +### Why the hook exists + +Instructions in RUNTIME.md, CLAUDE.md, and MEMORY.md are insufficient — agents default to writing local MEMORY.md regardless of written rules. The PreToolUse hook is a hard technical gate that makes the correct behavior the only possible behavior. ## MCP Requirement diff --git a/runtime/claude/settings.json b/runtime/claude/settings.json index 9fc9df4..93b7ad4 100644 --- a/runtime/claude/settings.json +++ b/runtime/claude/settings.json @@ -1,6 +1,18 @@ { "model": "opus", "hooks": { + "PreToolUse": [ + { + "matcher": "Write|Edit|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "~/.config/mosaic/tools/qa/prevent-memory-write.sh", + "timeout": 10 + } + ] + } + ], "PostToolUse": [ { "matcher": "Edit|MultiEdit|Write", diff --git a/tools/qa/prevent-memory-write.sh b/tools/qa/prevent-memory-write.sh new file mode 100755 index 0000000..eebf821 --- /dev/null +++ b/tools/qa/prevent-memory-write.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# prevent-memory-write.sh — PreToolUse hook +# +# Blocks Write/Edit/MultiEdit calls targeting Claude Code's native auto-memory +# files (~/.claude/projects/*/memory/*.md). +# +# These files are runtime-specific silos that no other agent harness can read. +# All agent learnings MUST go to OpenBrain (capture MCP tool or REST API). +# MEMORY.md files may only contain load-path behavioral guardrails — not knowledge. +# +# Exit codes (Claude Code PreToolUse): +# 0 = allow +# 2 = block with message shown to agent + +set -euo pipefail + +INPUT="$(cat)" + +FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || true)" + +[[ -z "$FILE_PATH" ]] && exit 0 + +# Resolve ~ to HOME +FILE_PATH="${FILE_PATH/#\~/$HOME}" + +# Block writes to Claude Code auto-memory files +if [[ "$FILE_PATH" =~ /.claude/projects/.+/memory/.*\.md$ ]]; then + echo "BLOCKED: Do not write agent learnings to ~/.claude/projects/*/memory/ — this is a runtime-specific silo." + echo "Use OpenBrain instead: MCP 'capture' tool or REST POST https://brain.woltje.com/v1/thoughts" + echo "File blocked: $FILE_PATH" + exit 2 +fi + +exit 0