# Skill System Memory

> Persistent shared memory for AI agents backed by PostgreSQL (fts + pg_trgm, optional pgvector). Includes compaction logging and maintenance scripts. Use when this capability is needed.

- Skill: `tomevault-io/skill-system-memory` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/skill-system-memory`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/skill-system-memory/raw
- Safety review: pending (external: skill-scanner PASS, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/skill-system-memory

---


# Skill System Memory (PostgreSQL)

Persistent shared memory for all AI agents. PostgreSQL 14+ on Linux or Windows.
Memory failures look like intelligence failures — this skill ensures the right memory is retrieved at the right time.

Contract notes:

- Module-first: `mem.py` and MCP tooling are the primary runtime surface.
- Plugin optional: OpenCode plugin integration is an adapter layer, not a required dependency.
- Prefer project-scoped DB naming: `<project>-memory` (for example `skills-memory`).

## DB Targeting — Project Isolation

Skills are installed **globally** but memory is **project-scoped**. Isolation is at the database level: each project gets its own PostgreSQL database named `<project>-memory`.

Cross-DB recall is an explicit read-side feature, not a default mode. Search and context reads stay on the current memory DB unless the caller opts in with `scope=all` or `cross_db=true`. All write paths remain pinned to the local `SKILL_PGDATABASE` target.

## Search Path Role Architecture (SK-C)

The compatible rollout path is additive:

- legacy mode remains DB-per-project via `SKILL_PGDATABASE=<project>-memory`
- shared-DB mode uses an explicit shared DB plus schema isolation
- runtime roles are split into `admin`, `connector`, and `visitor_<identity>`

Role model:

- `admin`: migration/init only; creates schemas, grants, and roles
- `connector`: CONNECT-only credential; opens the DB connection and immediately `SET ROLE`s
- `visitor_<identity>`: daily query role with enforced `search_path`

Visitor search_path matrix:

- `visitor_hermes`: `ep_scope, sk_scope, fd_scope, work_scope, public`
- `visitor_atlas`: `ep_scope, public`
- `visitor_athena`: `sk_scope, public`
- `visitor_fd_coder`: `fd_scope, public`
- `visitor_ep_coder`: `ep_scope, public`
- `visitor_sk_coder`: `sk_scope, public`
- `visitor_hephaestion`: `work_scope, sk_scope, public`

Shared-DB mode is explicit and backward-compatible:

- `SKILL_PGSHARED_DB=<shared-db-name>` selects the shared DB
- `SKILL_PGSCHEMA=<scope-schema>` selects the local writable schema
- `SKILL_PGDATABASE` remains valid for legacy DB-per-project mode

Runtime flow in shared-DB mode:

1. connect as `connector`
2. `SET ROLE visitor_<identity>`
3. rely on the visitor `search_path` for scoped queries
4. allow Hermes-only explicit override for all-scopes audit queries

Shared-DB examples:

```bash
# Provision shared DB schemas/roles for SK scope
python3 scripts/mem.py init-project \
  --project-id sk \
  --db-name shared-memory \
  --schema-name sk_scope \
  --shared-db-mode

# Run local SK queries through shared DB scope
SKILL_PGSHARED_DB=shared-memory \
SKILL_PGSCHEMA=sk_scope \
SKILL_VISITOR_ROLE=visitor_sk_coder \
python3 scripts/mem.py search "router policy"

# Hermes explicit all-scopes audit across the shared DB
SKILL_PGSHARED_DB=shared-memory \
SKILL_PGSCHEMA=sk_scope \
SKILL_VISITOR_ROLE=visitor_hermes \
python3 scripts/mem.py search "friction skill-system-comms" --all-scopes
```

## Cross-DB Visibility Contract

- `visibility=private` (default): searchable only inside the DB where the memory lives; legacy rows without visibility read as `private`
- `visibility=shared`: searchable across configured memory DBs when federation is explicit
- `visibility=global`: searchable across configured memory DBs and eligible for cross-DB auto context injection
- Cross-DB results must include `source_db`
- Cross-DB ranking uses normalized fusion (RRF-style), not raw per-DB score comparison
- Known DB fan-out targets are configured in `config/insight.yaml` under `memory.cross_db_targets`
- All mutation paths (`store`, `update`, `delete`, `purge`) stay local to the active `SKILL_PGDATABASE` target even when read-side cross-DB federation is enabled

### Auto-detection (preferred)

When no env vars are set, the DB name is auto-derived from the project directory name:

- Plugin (JS): uses `directory` passed by OpenCode host → last path segment → `<dir>-memory`
- mem.py CLI: uses script's root directory or cwd → `<dir>-memory`
- MCP server: uses `Path.cwd()` at startup → `<dir>-memory`

**Do NOT set `SKILL_PGDATABASE` globally.** It overrides auto-detection for ALL projects.

```bash
# WRONG — breaks project isolation
export SKILL_PGDATABASE=skills-memory   # every project now hits skills-memory

# RIGHT — let auto-detection work (no env var needed)
cd /path/to/FraudDetect && opencode      # → FraudDetect-memory
cd /path/to/skills && opencode           # → skills-memory
```

### Per-project override (when needed)

If you must override, set `SKILL_PGDATABASE` at **project level**, not globally:

- In project's `.opencode` config or launch script
- In project's `.env` file (if your host reads it)
- As a prefix to the command: `SKILL_PGDATABASE=FraudDetect-memory opencode`

### Fail-closed policy

If `PGDATABASE` is set but `SKILL_PGDATABASE` is not, all entry points **refuse to connect**. This prevents accidental cross-project memory pollution.

### MCP server path for multi-project setups

When skills are globally installed at `~/.agents/skills/`, configure MCP with the **absolute path** to the global install:

```json
{
  "skill-system-memory": {
    "command": "python3",
    "args": ["~/.agents/skills/skill-system-memory/mcp/server.py"]
  }
}
```

Do NOT use relative paths like `skills/skill-system-memory/mcp/server.py` — they break in projects that don't have a local `skills/` directory.

## Quick Start

Database `<project>-memory` and all functions are created by `init.sql` in this skill directory.
The current supported-now runtime surface covers core memory, typed context reads, the typed evolution ledger, and doctor/compaction support.
Session/project/context projection tables and behavior graph refresh remain optional capability-gated surfaces, not unconditional runtime guarantees.

```bash
# Linux — replace 'postgres' with your PostgreSQL superuser if different (e.g. your system username)
psql -U postgres -c "CREATE DATABASE <project>-memory;"
psql -U postgres -d <project>-memory -f init.sql

# Windows (adjust path to your psql.exe; replace 'postgres' with your PG superuser if needed)
& "C:\Program Files\PostgreSQL\18\bin\psql.exe" -U postgres -c "CREATE DATABASE <project>-memory;"
& "C:\Program Files\PostgreSQL\18\bin\psql.exe" -U postgres -d <project>-memory -f init.sql
```

> **Note**: If your PostgreSQL installation does not have a `postgres` role, use your actual
> PostgreSQL superuser name. On many Linux distros this matches your OS username.
> You can override at any time by setting `PGUSER` before running scripts:
> `export PGUSER=your_pg_username` (Linux/macOS) or `$env:PGUSER = "your_pg_username"` (PowerShell).

Verify: `SELECT * FROM memory_health_check();`

## MCP Server (Phase 6.1)

This skill includes an MCP (Model Context Protocol) server that exposes memory operations as agent tools.

### MCP Server Setup

```bash
# Install dependencies
cd skills/skill-system-memory/mcp
pip install -r requirements.txt

# Verify installation
python server.py --help

# Run with stdio transport (default)
python server.py

# Run with SSE transport (for remote access)
MCP_TRANSPORT=sse MCP_PORT=8000 python server.py

# Or via MCP CLI
mcp dev server.py
```

### MCP Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `SKILL_PGDATABASE` | `<project>-memory` | Legacy DB-per-project PostgreSQL database name |
| `SKILL_PGSHARED_DB` | (unset) | Shared PostgreSQL database name for search_path mode |
| `SKILL_PGSCHEMA` | (unset) | Local writable schema for search_path mode |
| `PGHOST` | `localhost` | PostgreSQL host |
| `PGPORT` | `5432` | PostgreSQL port |
| `PGUSER` | (optional) | PostgreSQL user |
| `MCP_TRANSPORT` | `stdio` | Transport mode (`stdio` or `sse`) |
| `MCP_PORT` | `8000` | Port for SSE transport |

### MCP Tools

| Tool | Description |
|------|-------------|
| `memory_search` | Search memories by natural language query (hybrid: FTS + trigram + pgvector) |
| `memory_store` | Store a new memory with auto-deduplication and local-only write isolation |
| `memory_update` | Update selected fields on a local memory row by id with audit logging |
| `memory_delete` | Soft-delete a local memory row by id with audit logging |
| `memory_purge` | Hard-delete a local memory row only with explicit authorization |
| `memory_auto_match` | Auto-match memories against input text + write to `.sisyphus/tmp/mem-{session}.md` |
| `memory_context` | Get user context (soul state, preferences, recent facets) |
| `memory_status` | Check memory system health (connection, schema, pgvector) |
| `debug_diagnose` | Run debug-tool diagnosis through the unified MCP server |
| `eda_profile` | Run EDA dataset profiling through the unified MCP server |
| `gate_validate` | Run gate validation through the unified MCP server |
| `memory_rebuild` | Rebuild full agent context after compact or memory loss |

### MCP Resources

| Resource | Description |
|----------|-------------|
| `memory://schema` | Memory database schema information |
| `memory://status` | Current memory system status |

### MCP Agent Integration

To use the MCP server with an agent, configure the agent's MCP settings to include:

```json
{
  "mcpServers": {
    "skill-system": {
      "command": "python",
      "args": ["skills/skill-system-memory/mcp/server.py"],
      "env": {
        "SKILL_PGDATABASE": "<project>-memory"
      }
    }
  }
}
```

Shared-DB MCP example:

```json
{
  "mcpServers": {
    "skill-system": {
      "command": "python",
      "args": ["skills/skill-system-memory/mcp/server.py"],
      "env": {
        "SKILL_PGSHARED_DB": "shared-memory",
        "SKILL_PGSCHEMA": "sk_scope",
        "SKILL_VISITOR_ROLE": "visitor_sk_coder"
      }
    }
  }
}
```

### MCP Tool Examples

```
# Search memories
mcp_tool: memory_search
args: { "query": "pgvector installation", "limit": 5 }

# Explicit cross-DB search
mcp_tool: memory_search
args: { "query": "reviewer feedback", "cross_db": true, "limit": 5 }

# Hermes all-scopes override in shared-DB mode
mcp_tool: memory_search
args: { "query": "cross-project audit", "all_scopes": true, "limit": 10 }

# Store a memory
mcp_tool: memory_store
args: { "type": "semantic", "category": "troubleshooting", "title": "Fixed pgvector on Windows", "content": "Build steps...", "importance": 8 }

# Update a memory locally
mcp_tool: memory_update
args: { "id": 42, "fields": { "title": "Updated title", "visibility": "shared" } }

# Soft-delete a memory locally
mcp_tool: memory_delete
args: { "id": 42 }

# Hard-delete a memory locally only with explicit authorization
mcp_tool: memory_purge
args: { "id": 42, "hard": true, "authorized": true }

# Auto-match memories (KEY TOOL for near-automatic surfacing)
mcp_tool: memory_auto_match
args: { "text": "I'm seeing an error with database connections", "threshold": 0.8, "limit": 5 }

# Get user context
mcp_tool: memory_context
args: { "user": "default" }

# Check system health
mcp_tool: memory_status
args: {}
```

For existing installations, also run:

```bash
psql -U postgres -d <project>-memory -v ON_ERROR_STOP=1 -f migrate-v2-postgres-first.sql
```

## Pure Skill Mode (default)

This skill works without installing any plugin. In pure skill mode:

- you manually run scripts when you want (progressive disclosure)
- no global OpenCode config is modified automatically

### Optional bootstrap (asks + records choices + tries to install)

Notes:

- Interactive mode defaults to NOT installing heavy optional components.
- Use `-InstallAll` / `--install-all` only when you're ready to install everything.

Run the bootstrap script to choose optional components (pgpass, local embeddings, pgvector) and record decisions.

Bootstrap can also optionally install the OpenCode compaction logging plugin (it will copy the plugin into your OpenCode plugins directory).

Windows:

```powershell
# run from the skill directory
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "scripts\bootstrap.ps1"
```

Linux/macOS:

```bash
# run from the skill directory
bash "scripts/bootstrap.sh"
```

The selection record is stored at:

- `~/.config/opencode/skill-system-memory/setup.json`

Agent rule:

- If this file does not exist, ask the user if they want to enable optional components.
- Recommended: run bootstrap with all options enabled (then fix any failures it reports).

## Runtime Capability Model

Current live capability classifications are intentionally narrow:

| capability | current status | current support surface |
|---|---|---|
| `core_memory` | `SUPPORTED_NOW` | `agent_memories` plus `store_memory`, `search_memories`, `memory_health_check` |
| `evolution_ledger` | `SUPPORTED_NOW` | `evolution_snapshots` plus `insert_evolution_snapshot`, `get_evolution_history` |
| `compaction_logging` | `SUPPORTED_NOW` when the repo plugin is synced | local JSONL compaction log plus guarded `store_memory(...)` writeback |
| `typed_context_reads` | `SUPPORTED_NOW` | `get_agent_context`, `get_soul_state`, `get_recent_facets`, `get_user_preferences` |
| `runtime_sync_projections` | `GATED_OPTIONAL` | `session_summaries`, `project_summaries`, `context_rollups` only when those tables exist |
| `behavior_refresh_graph` | `GATED_OPTIONAL` | `behavior_*` refresh only when those tables exist |
| `control_plane_refresh` | `DEFERRED_UNSUPPORTED` | not part of the current user-facing memory runtime |

When the durable evolution proposal model is present, doctor also reports:

- canonical accepted store: `evolution_nodes`
- canonical rejected store: `evolution_rejections`
- canonical task materialization: `evolution_tasks -> agent_tasks`
- role of `agent_memories`: searchable summary/backref surface with explicit legacy compatibility rows
- approve/reject path status: transaction-safe at the CLI boundary
- migration/backfill idempotence: rerun-safe
- decision replay status: replay-idempotent by `proposal_id`
- coordination mechanism: advisory lock by proposal + semantic fingerprint on canonical accepted/rejected rows
- payload mismatch policy: blocked with no mutation
- conflicting terminal policy: blocked with no mutation
- task authority model: `agent_tasks` lifecycle authority, `evolution_tasks` mapping only

In that state, `evolution_snapshots` is treated as `LEGACY_COMPAT_SURFACE` for proposal durability, while remaining active for snapshot/version history.

On Windows, pgvector installation follows the official pgvector instructions (Visual Studio C++ + `nmake /F Makefile.win`). The bootstrap will attempt to install prerequisites via `winget`.

### Optional automation: compaction logging (OpenCode plugin)

If you want automatic compaction logging, install the OpenCode plugin template shipped with this skill.

Option A (recommended): run bootstrap and choose the plugin option.

1) Copy `plugins/skill-system-memory.js` and `plugins/runtime_sync.js` to `~/.config/opencode/plugins/`
2) Restart OpenCode

Safety / rollback (if OpenCode gets stuck on startup):

- Remove or rename `~/.config/opencode/plugins/skill-system-memory.js`
- Remove or rename `~/.config/opencode/plugins/runtime_sync.js`
- Restart OpenCode
- Check logs:
  - macOS/Linux: `~/.local/share/opencode/log/`
  - Windows: `%USERPROFILE%\.local\share\opencode\log`

Plugin behavior notes:

- The plugin is designed to be a no-op unless you explicitly enabled it via bootstrap (`setup.json` sets `selected.opencode_plugin=true`).
- It only attempts a Postgres write if `selected.pgpass=true` (avoids hanging on auth prompts).
- On compaction, it always keeps local JSONL logging and the core `store_memory(...)` path available when the DB target is valid.
- Runtime projection upserts (`session_summaries`, `project_summaries`, `context_rollups`) are capability-gated: if those tables are absent, the plugin skips them with an explicit surfaced reason instead of attempting writes.
- Behavior graph refresh into `behavior_*` tables is also capability-gated and is skipped explicitly when the required tables are absent.

Uninstall:

- Remove `~/.config/opencode/plugins/skill-system-memory.js`
- Remove `~/.config/opencode/plugins/runtime_sync.js`
- Restart OpenCode

## Credentials (psql)

Do NOT hardcode passwords in scripts, skill docs, or config files.

Recommended options for non-interactive `psql`:

- `.pgpass` / `pgpass.conf` (recommended)
  - Linux/macOS: `~/.pgpass` (must be `chmod 0600 ~/.pgpass` or libpq will ignore it)
  - Windows: `%APPDATA%\postgresql\pgpass.conf` (example: `C:\Users\<you>\AppData\Roaming\postgresql\pgpass.conf`)
  - Format: `hostname:port:database:username:password`
  - Docs: https://www.postgresql.org/docs/current/libpq-pgpass.html
- `PGPASSFILE` (optional override): point to a custom location for the password file
- `PGPASSWORD` (not recommended): only for quick local testing; environment variables can leak on some systems
  - Docs: https://www.postgresql.org/docs/current/libpq-envars.html

Tip: set connection defaults once (per shell) to shorten commands:

```bash
export PGHOST=localhost
export PGPORT=5432
export SKILL_PGDATABASE=<project>-memory
export PGUSER=postgres   # change to your PG superuser if postgres role does not exist
```

Shell copy/paste safety:

- Avoid copying inline markdown backticks (e.g. `semantic`) into your shell. In zsh, backticks are command substitution.
- Prefer using the wrapper scripts (`scripts/mem.sh`, `scripts/mem.ps1`) or copy from fenced code blocks.

### One-time setup helper scripts

This skill ships helper scripts (relative paths):

- `scripts/setup-pgpass.ps1`
- `scripts/setup-pgpass.sh`

OpenCode usage: run them from the skill directory.

Windows run:

```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "scripts\setup-pgpass.ps1"
```

Linux/macOS run:

```bash
bash "scripts/setup-pgpass.sh"
```

## Memory Types

| Type | Lifespan | Use When |
|------|----------|----------|
| `working` | 24h auto-expire | Current conversation context (requires `session_id`) |
| `episodic` | Permanent + decay | Problem-solving experiences, debugging sessions |
| `semantic` | Permanent | Extracted facts, knowledge, patterns |
| `procedural` | Permanent | Step-by-step procedures, checklists (importance >= 7) |

## Core Functions

### `store_memory(type, category, tags[], title, content, metadata, agent_id, session_id, importance)`

Auto-deduplicates by content hash. Duplicate inserts bump `access_count` and `importance_score`.

```sql
SELECT store_memory(
    'semantic',
    'windows-networking',
    ARRAY['ssh', 'tunnel', 'port-conflict'],
    'SSH Tunnel Port Conflict Resolution',
    'Fix: 1) taskkill /F /IM ssh.exe  2) Use processId not pid  3) Wait 3s',
    '{"os": "Windows 11"}',
    'sisyphus',
    NULL,
    9.0
);
```

### Wrapper: `scripts/mem.py` (推薦 — parameterized query，無 quoting 問題)

> **Requirements**: `pip install psycopg2-binary`

```bash
# DB 連線狀態 + 記憶總數
python3 scripts/mem.py status

# 搜尋記憶（含特殊字元也安全）
python3 scripts/mem.py search "pgvector windows install" --limit 5
python3 scripts/mem.py search "fraud features" --scope project --limit 10
python3 scripts/mem.py search "reviewer feedback" --cross-db --limit 10
python3 scripts/mem.py search "shared debugging notes" --scope all --limit 10

# 跨 5 維度診斷搜尋 (B-015 TKT-001)
python3 scripts/mem.py search "alert file" --diagnostic --limit 10

# Tag 前綴自動展開 (B-015 TKT-003)
python3 scripts/mem.py search "root-cause" --auto-expand

# 儲存記憶（--content flag）
python3 scripts/mem.py store --type semantic --category project --title "pgvector install" --tags "postgres,pgvector,windows" --importance 8 --scope project --content "Steps: ..."
python3 scripts/mem.py store --type semantic --category feedback --title "Reviewer note" --visibility shared --content "Cross-DB searchable note"
python3 scripts/mem.py update 42 --field title="Updated note" --field visibility=shared
python3 scripts/mem.py delete 42
python3 scripts/mem.py purge 42 --hard --authorized

# 儲存記憶（stdin）
printf '%s' "Steps: ..." | python3 scripts/mem.py store semantic project "pgvector install" "postgres,pgvector,windows" 8 --scope session

# 列出 / 匯出 / 壓縮
python3 scripts/mem.py list --scope project --limit 20
python3 scripts/mem.py export --format json --scope global
python3 scripts/mem.py compact --scope project

# session 開頭自動撈出相關記憶
python3 scripts/mem.py context "pgvector ssh tunnel"

# 列出所有已使用的 tags / categories
python3 scripts/mem.py tags
python3 scripts/mem.py categories

# DB 不可用時，將 pending fallback 寫回 DB
python3 scripts/mem.py flush

# 保守升級 visibility（只影響本地 DB）
python3 scripts/mem.py visibility-upgrade --visibility shared --category feedback --dry-run
python3 scripts/mem.py visibility-upgrade --visibility global --tags reviewer,decision
```

Fallback behavior:

- DB 連線失敗時，`store` 會降級寫入 `.memory/pending/*.json`，`search` / `context` 會讀本地 pending 檔案並印出警告
- `list` / `export` 也會從 pending fallback 讀取；`compact` 在 DB 不可用時只印 warning 不崩潰
- DB 可連線但 memory schema 缺失時，`store` 會 warning + no-op，`search` / `context` / `tags` / `categories` 會回空結果 + warning
- `flush` 會在 DB 恢復後將 `.memory/pending/` 內容寫回 PostgreSQL

Scope model:

- `session` (default): 記錄在 `metadata.scope=session`，並盡量帶上 `session_id`
- `project`: 記錄在 `metadata.scope=project` + `metadata.project_id=<repo>`
- `global`: 記錄在 `metadata.scope=global`，仍需明確 federated read 才會跨 DB 查詢
- `search` / `list` / `export` 不帶 `--scope` 時，會回傳所有 scope 並在結果中標記來源 scope

Visibility is distinct from scope:

- `private` (default): local DB only
- `shared`: eligible for explicit cross-DB search
- `global`: eligible for explicit cross-DB search and auto context injection

Mutation audit rules:

- `update` writes selected field changes only, in the local DB only
- `delete` is soft-delete only (`deleted_at`), preserving backward-compatible reads
- `purge` is hard delete and must be explicitly authorized
- every mutation writes a `memory_audit` row with old/new values plus actor metadata

CLI mutation examples:

```bash
python3 scripts/mem.py update 42 --field title="Updated note" --field visibility=shared
python3 scripts/mem.py delete 42
python3 scripts/mem.py purge 42 --hard --authorized
```

`memory_audit` is the canonical mutation trail for these operations. Each row records:

- `memory_id`
- `action` (`update`, `delete`, `purge`)
- `old_value`
- `new_value`
- `actor_id`
- `session_id`
- `changed_at`

Warning: `purge` is destructive and bypasses soft-delete recovery. It stays local-only and requires explicit authorization flags; do not use it for routine cleanup.

Known federated read targets are configured in `config/insight.yaml`:

```yaml
memory:
  cross_db_targets:
    - Work-memory
    - skills-memory
    - ExperimentPipeline-memory
    - FraudDetect-memory
  cross_db_context_visibility:
    - global
```

This list controls explicit read fan-out only. Write paths still target the local `SKILL_PGDATABASE` database even if a caller passes `target_db`-style data.

### Wrapper: `scripts/mem.sh` / `scripts/mem.ps1` (shell fallback)

```bash
# 連線狀態
bash "scripts/mem.sh" status

# 搜尋
bash "scripts/mem.sh" search "pgvector windows install" 5

# 儲存 (content via stdin)
printf '%s' "Steps: ..." | bash "scripts/mem.sh" store semantic project "pgvector install" "postgres,pgvector,windows" 8

# 列出 tags / categories
bash "scripts/mem.sh" tags
bash "scripts/mem.sh" categories
```

```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "scripts\mem.ps1" types
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "scripts\mem.ps1" search "pgvector windows install" 5
"Steps: ..." | powershell.exe -NoProfile -ExecutionPolicy Bypass -File "scripts\mem.ps1" store semantic project "pgvector install" "postgres,pgvector,windows" 8
```

## Router Integration (optional)

If you use a Router skill that executes pinned pipelines, it can read a manifest embedded in this `SKILL.md`.

For portability, the manifest block is fenced as YAML but the content is JSON (valid YAML). The Router parses it.

```skill-manifest
{
  "schema_version": "2.0",
  "id": "skill-system-memory",
  "version": "0.7.0",
  "capabilities": ["memory-search", "memory-search-cross-db", "memory-role-scope", "memory-store", "memory-update", "memory-delete", "memory-purge", "memory-visibility-upgrade", "memory-list", "memory-compact", "memory-export", "memory-export-md", "memory-flush", "memory-health", "memory-types", "memory-auto-write", "memory-doctor", "plugin-debug", "plugin-eda", "plugin-gate", "memory-rebuild"],
  "effects": ["proc.exec", "db.read", "db.write"],
  "operations": {
    "search": {
      "description": "Search memories by natural language query. Returns ranked results with relevance scores.",
      "input": {
        "query": { "type": "string", "required": true, "description": "Natural language search query" },
        "limit": { "type": "integer", "required": false, "default": 5, "description": "Max results" },
        "scope": { "type": "string", "required": false, "description": "global | project | session filter; all enables explicit cross-DB federation" },
        "cross_db": { "type": "boolean", "required": false, "description": "Explicitly fan out reads across configured memory DBs" },
        "all_scopes": { "type": "boolean", "required": false, "description": "Hermes/admin override to bypass visitor search_path scoping for cross-project audit" }
      },
      "output": {
        "description": "Array of memory matches with local scope labels and cross-DB source attribution when federation is enabled",
        "fields": { "status": "ok | error", "data": "array of {id, title, content, relevance_score, scope, source_db?}" }
      },
      "entrypoints": {
        "unix": ["bash", "scripts/router_mem.sh", "search", "{query}", "{limit}"],
        "windows": ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "scripts\\router_mem.ps1", "search", "{query}", "{limit}"]
      }
    },
    "store": {
      "description": "Store a new memory. Auto-deduplicates by content hash and always writes to the local memory DB.",
      "input": {
        "memory_type": { "type": "string", "required": true, "description": "One of: semantic, episodic, procedural, working" },
        "category": { "type": "string", "required": true, "description": "Category name" },
        "title": { "type": "string", "required": true, "description": "One-line summary" },
        "tags_csv": { "type": "string", "required": true, "description": "Comma-separated tags" },
        "importance": { "type": "integer", "required": true, "description": "1-10 importance score" },
        "scope": { "type": "string", "required": false, "description": "global | project | session" },
        "visibility": { "type": "string", "required": false, "description": "private | shared | global" }
      },
      "output": {
        "description": "Confirmation with stored memory id",
        "fields": { "status": "ok | error", "id": "integer" }
      },
      "entrypoints": {
        "unix": ["bash", "scripts/router_mem.sh", "store", "{memory_type}", "{category}", "{title}", "{tags_csv}", "{importance}"],
        "windows": ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "scripts\\router_mem.ps1", "store", "{memory_type}", "{category}", "{title}", "{tags_csv}", "{importance}"]
      }
    },
    "update": {
      "description": "Update selected fields of a local memory row by id with audit logging.",
      "input": {
        "id": { "type": "integer", "required": true },
        "fields": { "type": "object", "required": true }
      },
      "output": {
        "description": "Update summary",
        "fields": { "status": "ok | error", "id": "integer", "updated_fields": "array" }
      },
      "entrypoints": {
        "unix": ["python3", "scripts/mem.py", "update"],
        "windows": ["python", "scripts/mem.py", "update"]
      }
    },
    "delete": {
      "description": "Soft-delete a local memory row by id with audit logging.",
      "input": {
        "id": { "type": "integer", "required": true }
      },
      "output": {
        "description": "Soft-delete summary",
        "fields": { "status": "ok | error", "id": "integer", "deleted_at": "string" }
      },
      "entrypoints": {
        "unix": ["python3", "scripts/mem.py", "delete"],
        "windows": ["python", "scripts/mem.py", "delete"]
      }
    },
    "purge": {
      "description": "Hard-delete a local memory row only with explicit authorization.",
      "input": {
        "id": { "type": "integer", "required": true },
        "hard": { "type": "boolean", "required": true },
        "authorized": { "type": "boolean", "required": true }
      },
      "output": {
        "description": "Hard-delete summary",
        "fields": { "status": "ok | error", "id": "integer", "purged": "boolean" }
      },
      "entrypoints": {
        "unix": ["python3", "scripts/mem.py", "purge"],
        "windows": ["python", "scripts/mem.py", "purge"]
      }
    },
    "list": {
      "description": "List memories with optional scope/category filters.",
      "input": {
        "scope": { "type": "string", "required": false },
        "category": { "type": "string", "required": false },
        "limit": { "type": "integer", "required": false, "default": 20 }
      },
      "output": {
        "description": "Memory listing with scope labels",
        "fields": { "status": "ok | error", "results": "array" }
      },
      "entrypoints": {
        "unix": ["python3", "scripts/mem.py", "list"],
        "windows": ["python", "scripts/mem.py", "list"]
      }
    },
    "compact": {
      "description": "Compact duplicate memories within an optional scope.",
      "input": {
        "scope": { "type": "string", "required": false }
      },
      "output": {
        "description": "Compaction result",
        "fields": { "status": "ok | warn | error", "compacted_count": "integer" }
      },
      "entrypoints": {
        "unix": ["python3", "scripts/mem.py", "compact"],
        "windows": ["python", "scripts/mem.py", "compact"]
      }
    },
    "export": {
      "description": "Export memories in json or csv format.",
      "input": {
        "format": { "type": "string", "required": false },
        "scope": { "type": "string", "required": false },
        "limit": { "type": "integer", "required": false, "default": 1000 }
      },
      "output": {
        "description": "Export payload",
        "fields": { "status": "ok | error", "results": "array|string" }
      },
      "entrypoints": {
        "unix": ["python3", "scripts/mem.py", "export"],
        "windows": ["python", "scripts/mem.py", "export"]
      }
    },
    "export-md": {
      "description": "Export DB-only memories to markdown files that memory_sync can re-import.",
      "input": {
        "folder": { "type": "string", "required": true, "description": "Target folder for markdown exports" }
      },
      "output": {
        "description": "Markdown export result",
        "fields": { "status": "ok | error", "created": "integer", "skipped": "integer", "folder": "string" }
      },
      "entrypoints": {
        "agent": "Use the memory_export MCP tool from skills/skill-system-memory/mcp/server.py"
      }
    },
    "visibility-upgrade": {
      "description": "Batch-upgrade local memory visibility conservatively by category and/or tag selectors.",
      "input": {
        "visibility": { "type": "string", "required": true, "description": "shared | global | private" },
        "category": { "type": "string", "required": false },
        "tags": { "type": "string", "required": false },
        "dry_run": { "type": "boolean", "required": false, "default": false }
      },
      "output": {
        "description": "Upgrade summary",
        "fields": { "status": "ok | error", "matched": "integer", "updated": "integer" }
      },
      "entrypoints": {
        "unix": ["python3", "scripts/mem.py", "visibility-upgrade"],
        "windows": ["python", "scripts/mem.py", "visibility-upgrade"]
      }
    },
    "flush": {
      "description": "Flush local pending fallback files into PostgreSQL when the DB is back.",
      "input": {},
      "output": {
        "description": "Confirmation that pending fallback files were replayed",
        "fields": { "status": "ok | warn | error", "flushed_count": "integer" }
      },
      "entrypoints": {
        "unix": ["python3", "scripts/mem.py", "flush"],
        "windows": ["python", "scripts/mem.py", "flush"]
      }
    },
    "health": {
      "description": "Check memory system health: total count, average importance, stale count.",
      "input": {},
      "output": {
        "description": "Health metrics",
        "fields": { "status": "ok | error", "data": "array of {metric, value, status}" }
      },
      "entrypoints": {
        "unix": ["bash", "scripts/router_mem.sh", "health"],
        "windows": ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "scripts\\router_mem.ps1", "health"]
      }
    },
    "types": {
      "description": "List available memory types and their descriptions.",
      "input": {},
      "output": {
        "description": "Memory type definitions",
        "fields": { "status": "ok | error", "data": "array of {type, lifespan, description}" }
      },
      "entrypoints": {
        "unix": ["bash", "scripts/router_mem.sh", "types"],
        "windows": ["powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "scripts\\router_mem.ps1", "types"]
      }
    },
    "auto-write": {
      "description": "Procedure template for automatically storing a memory after solving a non-obvious problem.",
      "input": {},
      "output": {
        "description": "Proposed memory fields to store",
        "fields": {"memory_type": "string", "category": "string", "title": "string", "tags_csv": "string", "importance": "integer", "content": "string"}
      },
      "entrypoints": {
        "agent": "Follow scripts/auto-write-template.md"
      }
    },
    "doctor": {
      "description": "Report the current runtime capability map, memory plugin drift, memory DB target, OMO resolution, canonical evolution stores, table/routine readiness, and evolution ledger status.",
      "input": {},
      "output": {
        "description": "Doctor report for the current runtime state",
        "fields": {"status": "ok | warn | error", "doctor_report": "object"}
      },
      "entrypoints": {
        "unix": ["python3", "scripts/runtime_doctor.py", "--format", "json"],
        "windows": ["python", "scripts/runtime_doctor.py", "--format", "json"]
      }
    },
    "debug-diagnose": {
      "description": "Expose skill-system-debug diagnose through the unified MCP server.",
      "input": {
        "symptom": { "type": "string", "required": true },
        "context": { "type": "string", "required": false }
      },
      "output": {
        "description": "Structured diagnosis payload",
        "fields": { "status": "ok | error", "result": "object" }
      },
      "entrypoints": {
        "agent": "Use the debug_diagnose MCP tool from skills/skill-system-memory/mcp/server.py"
      }
    },
    "eda-profile": {
      "description": "Expose skill-system-eda dataset profiling through the unified MCP server.",
      "input": {
        "input_path": { "type": "string", "required": true },
        "sample": { "type": "integer", "required": false },
        "target": { "type": "string", "required": false }
      },
      "output": {
        "description": "EDA profile output",
        "fields": { "status": "ok | error", "profile_path": "string", "report_path": "string" }
      },
      "entrypoints": {
        "agent": "Use the eda_profile MCP tool from skills/skill-system-memory/mcp/server.py"
      }
    },
    "gate-validate": {
      "description": "Expose skill-system-gate validation through the unified MCP server.",
      "input": {
        "rules": { "type": "string", "required": false },
        "artifact_root": { "type": "string", "required": false }
      },
      "output": {
        "description": "Gate validation summary",
        "fields": { "status": "ok | error", "passed": "boolean", "results": "array" }
      },
      "entrypoints": {
        "agent": "Use the gate_validate MCP tool from skills/skill-system-memory/mcp/server.py"
      }
    },
    "rebuild": {
      "description": "Rebuild full agent context after compact or memory loss.",
      "input": {
        "user": { "type": "string", "required": false },
        "recent_limit": { "type": "integer", "required": false }
      },
      "output": {
        "description": "Soul, preferences, recent memories, and pending decisions",
        "fields": { "status": "ok | error", "rebuild_context": "object" }
      },
      "entrypoints": {
        "agent": "Use the memory_rebuild MCP tool from skills/skill-system-memory/mcp/server.py"
      }
    }
  },
  "stdout_contract": {
    "last_line_json": true
  },
  "mcp": {
    "server": {
      "name": "skill-system",
      "description": "Unified skill-system MCP server exposing memory, debug, eda, gate, and memory_rebuild tools",
      "entrypoint": "python skills/skill-system-memory/mcp/server.py",
      "transport": "stdio",
      "tools": ["memory_search", "memory_store", "memory_auto_match", "memory_context", "memory_status", "memory_sync", "memory_export", "debug_diagnose", "eda_profile", "gate_validate", "memory_rebuild"],
      "resources": ["memory://schema", "memory://status"]
    }
  }
}
```

Notes:

- The Router expects each step to print **last-line JSON**.
- These Router adapter scripts are separate from `mem.sh` / `mem.ps1` to avoid breaking existing workflows.

## Visualize Memories (Markdown export)

If querying PostgreSQL is too inconvenient for daily use, you can export memories into markdown files under `./Memory/` (current directory by default):

```bash
bash "<skill-dir>/scripts/sync_memory_to_md.sh" --out-dir "./Memory"
```

Outputs:

- `Memory/Long.md` (semantic + procedural)
- `Memory/Procedural.md` (procedural only)
- `Memory/Short.md` (friction + compaction-daily + procedural highlights)
- `Memory/Episodic.md` (episodic)

Backups:

- Backups are stored under `Memory/.backups/` to avoid noisy `git status`.
- Use `--no-backup` to disable.

The sync script will also create `Memory/.gitignore` if it doesn't exist (ignores `.backups/` and `SYNC_STATUS.txt`).

Long index:

- `Memory/Long.md` includes an `Index` section (top categories + tags) to make the export browsable.

### `search_memories(query, types[], categories[], tags[], agent_id, min_importance, limit)`

Hybrid search: full-text (tsvector) + trigram similarity (pg_trgm) + tag filtering.
Accepts **plain English** queries — no tsquery syntax needed.
Relevance scoring: `text_score * decay * recency * importance`.

```sql
-- Natural language
SELECT * FROM search_memories('ssh tunnel port conflict', NULL, NULL, NULL, NULL, 7.0, 5);

-- Filter by type + tags
SELECT * FROM search_memories(
    'troubleshooting steps',
    ARRAY['procedural']::memory_type[],
    NULL,
    ARRAY['ssh'],
    NULL, 0.0, 5
);
```

Returns: `id, memory_type, category, title, content, importance_score, relevance_score, match_type`
Where `match_type` is one of: `fulltext`, `trigram_title`, `trigram_content`, `metadata`.

### `memory_health_check()`

Returns: `metric | value | status` for `total_memories`, `avg_importance`, `stale_count`.

### `apply_memory_decay()`

Decays episodic memories by `0.9999^days_since_access`. Run daily.

### `prune_stale_memories(age_days, max_importance, max_access_count)`

Soft-deletes old episodic memories below thresholds. Default: 180 days, importance <= 3, never accessed.

## Agent Workflow

## Auto-Write Template

After fixing a bug or solving a non-obvious problem, store a memory using the standard template:

- Procedure: `scripts/auto-write-template.md`

> **最佳實踐**：優先使用 `mem.py` wrapper，避免 shell quoting 與認證問題。
> Raw SQL 範例請見 [Appendix: Raw SQL](#appendix-raw-sql)。



…(truncated)
