#!/usr/bin/env node /** * Guard 4: MEMORY.md Direct Write Guard * Prevents new code from writing directly to MEMORY.md. * * → Architecture Freeze v2 (PR-1): docs/architecture-freeze-v2.md * → Forbidden Addition §6.6: 禁止直接写 MEMORY.md * * Only allowed writers (whitelisted): * - session-memory hook (bundled/session-memory) * - memory-core dreaming (extensions/memory-core) * - memory-core manager (extensions/memory-core) * - openclaw CLI (memory promote/write) * * FAIL: new code path writes to MEMORY.md */ import { readdirSync, readFileSync, existsSync } from "node:fs"; import { join } from "node:path"; import { homedir } from "node:os"; import { execSync } from "node:child_process"; const OPENCLAW_HOME = process.env.OPENCLAW_HOME || "/opt/homebrew/lib/node_modules/openclaw"; const DIST_DIR = join(OPENCLAW_HOME, "dist"); const WORKSPACE = join(homedir(), ".openclaw", "workspace"); // Known/allowed MEMORY.md writers const ALLOWED_MEMORY_MD_WRITERS = new Set([ "memory-core", "memory-wiki", "active-memory", // writes to prompt, not MEMORY.md directly ]); function guardMemoryDirectWrite() { const issues = []; const foundWriters = []; // Search JS files in dist for MEMORY.md write references // We grep for patterns that write to MEMORY.md const searchPatterns = [ "MEMORY.md", "'MEMORY.md'", '"MEMORY.md"', ]; try { // Use grep to find references to MEMORY.md in dist JS files const grepResult = execSync( `grep -rl "MEMORY.md" "${DIST_DIR}" 2>/dev/null | grep -v ".d.ts$" | grep -v "node_modules" | grep -v ".json$" | head -50`, { timeout: 30000, encoding: "utf8", maxBuffer: 10 * 1024 * 1024 } ).trim(); if (grepResult) { const files = grepResult.split("\n").filter(Boolean); for (const file of files) { const relativePath = file.replace(DIST_DIR + "/", ""); // Determine which module this belongs to let module = "unknown"; if (relativePath.includes("memory-core")) module = "memory-core"; else if (relativePath.includes("memory-wiki")) module = "memory-wiki"; else if (relativePath.includes("memory-lancedb")) module = "memory-lancedb"; else if (relativePath.includes("active-memory")) module = "active-memory"; else if (relativePath.includes("bundled/session-memory")) module = "bundled/session-memory"; else if (relativePath.includes("bundle")) module = "bundled"; else if (relativePath.includes("cli")) module = "cli"; else module = relativePath.split("/")[0] || "unknown"; foundWriters.push({ file: relativePath, module }); } } } catch (e) { // grep returns exit 1 if no matches found — that's OK if (e.status !== 1) { issues.push({ level: "WARN", msg: `grep error: ${e.message}` }); } } // Group by module const moduleGroups = {}; for (const w of foundWriters) { if (!moduleGroups[w.module]) moduleGroups[w.module] = []; moduleGroups[w.module].push(w.file); } const knownModules = new Set(Object.keys(moduleGroups)); console.log(""); console.log("═══════════════════════════════════════"); console.log(" Guard 4: MEMORY.md Direct Write Guard"); console.log("═══════════════════════════════════════"); console.log(""); console.log(` Modules referencing MEMORY.md: ${knownModules.size}`); for (const [mod, files] of Object.entries(moduleGroups)) { const status = ALLOWED_MEMORY_MD_WRITERS.has(mod) ? "✓ allowed" : "⚠ unknown"; console.log(` ${status} ${mod} (${files.length} files)`); if (files.length <= 3) { for (const f of files) { console.log(` - ${f}`); } } } // Check for unexpected writers const unexpectedModules = [...knownModules].filter(m => !ALLOWED_MEMORY_MD_WRITERS.has(m)); if (unexpectedModules.length > 0) { console.log(""); for (const mod of unexpectedModules) { // some modules like "bundled" are expected to reference MEMORY.md for reading // Only flag as WARN if they might be writing issues.push({ level: "WARN", msg: `Module "${mod}" references MEMORY.md — verify it's read-only` }); } } console.log(""); const failIssues = issues.filter(i => i.level === "FAIL"); const warnIssues = issues.filter(i => i.level === "WARN"); if (failIssues.length > 0) { for (const issue of failIssues) { console.log(` [FAIL] ${issue.msg}`); } console.log(" ✗ FAIL: new direct MEMORY.md writer(s) detected"); process.exit(1); } else { if (warnIssues.length > 0) { for (const issue of warnIssues) { console.log(` [WARN] ${issue.msg}`); } } console.log(" ✓ PASS: MEMORY.md direct write paths unchanged"); process.exit(0); } } guardMemoryDirectWrite();