A shared memory layer for AI coding agents. Different agentic AI platforms — Claude Code, Cursor, Codex, OpenCode, Pi Agent, Windsurf — read and write to the same episodic store, so decisions, discoveries, and behavioral patterns persist across sessions and across tools.
Works with: Claude Code, Cursor, Codex (OpenAI), OpenCode, Pi Agent, Windsurf / Continue
The design principles guiding the system are documented in PRINCIPLES.md; the capability families the substrate supports are charted in CAPABILITIES.md.
The enforcement layer (behavior-pattern gates/hooks) is being decoupled from the memory substrate, and the system is moving to a typed plugin model —
enforcement,recall-strategy,store-strategy, andlearningplugins, each a registered, schema-validated, versioned contract. The capability families and the rule for extending them are the guiding post in CAPABILITIES.md; the full design is RFC-008 (accepted; building in phases — see the RFC-008 phase index for current status).This does not break current usage. The file-based store, the CLI, and the cross-tool behavior described below are unaffected today — the substrate (
em-store/em-recall/em-search) stays a stable, zero-dependency store-and-recall API throughout the migration. Enforcement plugins beyond Claude Code arrive incrementally as later phases land.
- Capabilities
- Agentic Coding Disciplines
- How it works
- Installation
- Supported Tools
- Skills
- Episode Categories
- Data Locations
- Self-Correction: Revision Chains
- Behavioral Patterns
- RFCs
- Scripts Reference
- License
- Cross-tool memory sharing — A decision Claude Code made yesterday surfaces when Cursor or Codex starts working on the same project today. All four agents read/write the same episode files; no tool is siloed.
- Two-tier memory: global + local
- Global (
~/.episodic-memory/) — episodes shared across every project on your machine. Default write target. Use for: architectural lessons, tooling conventions, behavioral patterns. - Local (
<project>/.episodic-memory/) — project-scoped episodes. Use for: project-specific decisions you don't want leaking into other repos. - Searches read both stores by default; pass
--scope globalor--scope localto narrow. Onem-store,--scopeselects the write target.
- Global (
- Behavioral patterns — Reusable rules-of-thumb (currently 11, see patterns table below) that codify how AI agents should behave: when to checkpoint, when to push, when to log violations. Seeded into global memory so they surface during normal search and recall.
- Self-correcting revision chains — When a past decision proves wrong, the original is marked superseded and a corrected episode replaces it. Searches return only the latest active version.
- Proactive recall — On session start, agents pull recent decisions, lessons, and prior violations relevant to the current project as pre-flight context.
- Violation tracking — Structured records of behavioral-pattern violations, used to detect repeat offenses and escalate weak rules to mechanical enforcement (hooks).
- Zero dependencies — Plain markdown + JSONL on the local filesystem. Node.js stdlib only. No database, no daemon, no network.
This repo is built by AI agents, and it applies the same disciplines it ships to its own development. The practices below are not aspirational. Most are enforced mechanically by hooks in this repo, and the rest are encoded in the plan template that every non-trivial change follows.
- Plan before code (Rule 18 workflow). Non-trivial work runs a fixed sequence: plan, then an independent second-opinion review on the plan, then fix findings, then final plan and approval, then implement with tests written alongside the code, then code review, then fix, then an end-to-end test, then file every deferred finding. The
plan-gatehook blocks repo-source writes until the plan is approved. Template: docs/PLAN_TEMPLATE.md. - Enforcement is mechanical, not documentation (bp-010). Documentation-tier rules fail under task-end momentum, so the most-violated ones escalate to PreToolUse and Stop hooks:
plan-gate,checkpoint-gate, andstop-gate. A command classifier gates only repo-source writes. It never blocks episode writes, reads, or work outside the repo. Gates fail closed. - Verify by artifact, not narrative. Every claim that something is done names the command and its output. Install, hook, and gate behavior is proven by an isolated-
HOMEmock-project test that drives the real deployed hook, never by reading the code and never by a stub. The plan template's falsifiable-verify rule requires every step's check to fail when the implementation is stubbed. - Adversarial, multi-layer review. Changes pass through a pluggable second-opinion harness and adversarial subagents. Review runs in three layers, per-artifact, cross-file, and whole-PR, because each layer catches a different class of bug.
- Self-correction over patching. A wrong decision is superseded through a revision chain rather than edited away. Pattern violations are stored as evidence that escalates weak rules into hooks.
- One concern per PR, deploy after merge. Each PR covers a single concern and carries a required review trailer. After merge the running hooks are redeployed and audited with
tools/deploy-audit.mjs, because a merge is not the same as deployed.
Governing docs: PRINCIPLES.md, CAPABILITIES.md, and docs/PLAN_TEMPLATE.md.
Storage is entirely file-based — no database required. Episodes are plain markdown files (.md) with YAML frontmatter, stored on your local filesystem. A JSONL text file serves as a lightweight index for fast filtering. Zero external dependencies beyond Node.js.
The AI assistant stores significant events during sessions and recalls relevant episodes when starting work or when asked.
When a decision proves wrong, the system creates a revision chain — the original is marked superseded and a new corrected episode takes its place. Future searches show only the latest active version.
sequenceDiagram
participant U as User
participant AI as AI Assistant
participant S as em-store.mjs
participant R as em-search.mjs
participant FS as Data Store
rect rgb(230, 245, 255)
Note over U,FS: Store Flow
U->>AI: Makes significant decision
AI->>S: em-store.mjs --category decision ...
S->>FS: Write episode .md + append index
S-->>AI: { status: ok, id }
AI-->>U: Stored: "chose Express over Fastify"
end
rect rgb(255, 240, 240)
Note over U,FS: Self-Correction Flow
U->>AI: "That framework choice was wrong"
AI->>R: em-search.mjs --query "framework"
R-->>AI: Original episode found
AI->>S: em-revise.mjs --original <id> ...
S->>FS: Mark original superseded + write revision
AI-->>U: Revised: now recommends Fastify
end
rect rgb(255, 245, 230)
Note over U,FS: Recall Flow
U->>AI: "What did we decide about the framework?"
AI->>R: em-search.mjs --project X --query "framework"
R->>FS: Search index (superseded filtered out)
R-->>AI: Latest active decision only
AI-->>U: "We revised to Fastify because..."
end
# Clone the repo
git clone <repo-url> episodic-memory
cd episodic-memory
# Install for a specific tool in a target project
node install.mjs --tool cursor --project /path/to/my-project
# Install for all bundled targets except OpenCode
node install.mjs --tool all --project /path/to/my-projectThe installer:
- Copies scripts to
~/.episodic-memory/scripts/ - Copies
patterns/_index.jsonto~/.episodic-memory/patterns/for global pattern validation - Creates
.episodic-memory/in the target project for local episodes - Copies the appropriate instruction file for your tool
- With
--install-hooks: copiesplugins/claude-code/hooks/*.shinto~/.claude/hooks/andplugins/claude-code/hooks/lib/*.shinto~/.claude/hooks/lib/, then registers PreToolUse (checkpoint-gate + plan-gate + stop-gate, from~/.claude/hooks/), SessionStart (em-recall-sessionstart + BP-1 fallback sweep, from~/.claude/hooks/), and SessionEnd (em-session-end-prompt, run directly from~/.episodic-memory/scripts/) hooks in~/.claude/settings.json(Claude Code only, opt-in). Re-running the installer warns when an installed hook has drifted from the source-of-truth copy (#201). Use--install-hooks-forceto overwrite locally edited hook files.
Per-project enforcement config (optional, RFC-008 P4). With --install-enforcement, the installer seeds a default <project>/.episodic-memory/enforce-config.json = {"active": true} (create-if-absent, never overwriting your edits — a deliberate {"active": false} survives reinstalls, even with --install-hooks-force). Tune enforcement per project by editing it — no need to touch global hooks:
{"active": false}— layer-wide kill switch: silences all episodic-memory gates (checkpoint, plan, stop, preflight, second-opinion) for that project only; your other repos keep their hooks (R5).{"bp-001": {"plan_approval": "MEDIUM"}}— relax an individual gate (clamps tier DOWN only; never raises).
Fail-closed by design: a missing, empty, or malformed file leaves enforcement fully ON.
| Tool | Instruction file | Install location |
|---|---|---|
| Claude Code | SKILL.md |
.claude/skills/episodic-memory/SKILL.md |
| Cursor | cursor.mdc |
.cursor/rules/episodic-memory.mdc |
| Codex | SKILL.md |
.agents/skills/episodic-memory/SKILL.md |
| OpenCode | SKILL.md |
.opencode/skills/episodic-memory/SKILL.md |
| Pi Agent | SKILL.md |
.agents/skills/episodic-memory/SKILL.md |
| Windsurf | windsurf.md |
.windsurfrules (appended if exists) |
OpenCode is explicit-only: run node install.mjs --tool opencode --project <path>. It is intentionally excluded from --tool all because current OpenCode discovery loads .opencode/skills, .claude/skills, and .agents/skills; a broad all-tools install can otherwise expose duplicate episodic-memory skills. This path contract is pinned to the current official OpenCode skill docs observed 2026-05-16: https://opencode.ai/docs/skills.
Pi Agent shares the .agents/skills/episodic-memory/SKILL.md destination with Codex. --tool all includes Pi Agent by reusing that shared destination.
Instruction-only support is deliberately modest for OpenCode and Pi Agent:
| Capability | OpenCode | Pi Agent |
|---|---|---|
| Manual recall/search/store/revise via scripts | MEDIUM when shell/script execution is available; otherwise WEAK/manual | MEDIUM when shell/script execution is available; otherwise WEAK/manual |
| Proactive session-start recall | WEAK; depends on the agent loading/following the skill | WEAK; depends on the agent loading/following the skill |
| Enforcement/hooks | Unsupported in this installer slice | Unsupported in this installer slice |
| Live MCP tools | Deferred | Deferred |
| Cross-tool typed requests | Deferred except manual existing-script use | Deferred except manual existing-script use |
Future MCP support should be a thin wrapper around existing scripts (recall, search, store, revise) with no second store, no daemon, no polling, explicit activation, and reversible uninstall.
This project ships two in-tree skills. Each is a self-contained SKILL.md the agent loads on demand.
| Skill | Location | What it does |
|---|---|---|
episodic-memory |
instructions/SKILL.md → installed to .claude/skills/episodic-memory/, .agents/skills/episodic-memory/, etc. (see Supported Tools) |
The main skill the agent reads at session start to learn how to recall, store, revise, and surface episodes. Wraps every em-* script. |
classify-correction |
skills/classify-correction/SKILL.md (PR #327) |
Records a per-project override for the LLM checkpoint-gate classifier when a command was mislabeled (e.g. a read-only inspector blocked as shared_write). Writes to <project>/.episodic-memory/classifier-overrides.jsonl. |
Three additional SKILLs are invoked from launchd rather than the agent's session loop — see Scheduled Routines: episodic-memory-daily-mining, episodic-memory-weekly-digest, instruction-hygiene-maintenance.
| Category | Use for |
|---|---|
decision |
Technology choices, architecture, trade-offs |
discovery |
Bug root causes, undocumented behavior, insights |
milestone |
Features shipped, migrations completed |
context |
Constraints, dependencies, environment quirks |
research |
Web research distilled for future reference |
lesson |
Consolidated lessons from multiple episodes |
violation |
Behavioral pattern violations with structured sequences |
~/.episodic-memory/ # Global (cross-project)
├── scripts/ # Installed scripts
├── episodes/ # Global episode .md files
├── patterns/ # Pattern registry (_index.json)
├── index.jsonl # Global index
└── tags.json # Inverted tag index
<project>/.episodic-memory/ # Per-project (local)
├── episodes/ # Project-local episode .md files
├── index.jsonl # Project-local index
└── tags.json # Inverted tag index
patterns/ # Behavioral patterns (shipped with repo)
├── _index.json # Machine-readable pattern registry
├── TEMPLATE.md # Template for new patterns
└── *.md # Individual pattern files
All episodes go to the global common store by default, making them available across all projects. Use --scope local for decisions private to one project. Scripts search both local and global by default.
Worktrees: scripts resolve the local
.episodic-memory/viagit rev-parse --git-common-dir, so all worktrees of the same repo share one local store (the one at the main checkout). Fixed in #105.
When a past decision proves wrong:
# Find the original decision
node ~/.episodic-memory/scripts/em-search.mjs --query "framework" --full
# Create a revision (original is auto-marked superseded)
# --scope defaults to "inherit" — revision lands in the same store as the
# original. Pass --scope local|global only to force a cross-store revision.
node ~/.episodic-memory/scripts/em-revise.mjs \
--original <episode-id> \
--summary "Switched from Express to Fastify" \
--body "Express middleware overhead became a bottleneck..."
# View the full revision history
node ~/.episodic-memory/scripts/em-search.mjs --history <episode-id> --fullThe system ships with behavioral patterns — reusable lessons learned from real sessions that AI assistants can recall proactively.
| ID | Pattern |
|---|---|
| bp-001 | Standard implementation workflow |
| bp-002 | Proactive milestone storage |
| bp-003 | Promote project-specific best practices to global memory |
| bp-004 | Machine-readable index for token efficiency |
| bp-005 | Enforcement lives in consuming repos, not rule repos |
| bp-006 | Push only after all verification steps complete |
| bp-008 | Redo properly instead of patching retroactively |
| bp-009 | Store rule violations as evidence for enforcement |
| bp-010 | Habits override knowledge — always add mechanical enforcement |
| bp-011 | Local files first, external actions only after confirmation |
| bp-012 | Complete session wrap-up — episodic memory, changes, handoff |
Patterns are seeded into the global episode store via em-seed-patterns.mjs so they surface during normal search and recall. See patterns/TEMPLATE.md to create new ones.
Behavioral patterns are documentation by default — but the most-violated patterns get escalated to mechanical enforcement (see BP-1 Auto-Pilot below). The system provides two layers:
Built-in (episodic-memory):
- Violation tracking (
em-violation.mjs) — structured storage with pattern linkage, searchable by--category violationand--tag violated:<pattern_id> - Session-end prompt (
em-session-end-prompt.mjs) — SessionEnd hook that asks about violations - Proactive recall (
em-recall.mjs) — surfaces past violations at session start as pre-flight warnings - Checkpoint enforcement gate (RFC-002 Phase 3b, shipped + activated 2026-05-02 via #78 and #84) — PreToolUse hook that blocks code edits until the implementation checkpoint is printed, and blocks pushes until E2E + bug logging are done. Opt-in via
node install.mjs --tool claude-code --install-hooks --project <path>; registers SessionStart + PreToolUse + SessionEnd hooks in~/.claude/settings.json. - LLM intent classifier for the checkpoint gate (Tier 2/3 shipped 2026-05-22 via #326; replaced by agent-self-classify marker 2026-05-23 via #331) — when the Tier 1 heuristic table doesn't recognize a
Bashcommand, the gate consults a per-session marker (scripts/classifier-marker.mjs) populated by the active agent's own reasoning, then falls through to the Tier 1 safe-default if no marker exists. Configuration loaded byscripts/classifier-config-loader.mjsfrom<project>/.episodic-memory/classifier-config.json(or the global file under~/.episodic-memory/); setenabled: falseto disable LLM dispatch entirely and run on the Tier 1 heuristic alone. False-positives (read-only inspectors blocked asshared_write) are corrected via theclassify-correctionskill — see Skills below. - BP-1 Auto-Pilot (RFC-004, M0 + M1 + M2 shipped 2026-05-06..23 via #181, #186, #188, #200, #206, #305, #309, #313, #322) — activation gate, deadline sweep, finalize-replay state machine, HMAC-signed run manifests, per-session preflight markers, and the M2 finish-line
bp1-flag-flipcutover that mechanically enforce bp-001 (implementation workflow). Replaces documentation-only enforcement for the workflow lifecycle.
External (user-preferences):
- Pre-tool hooks (e.g.,
plan-gate.sh) that block writes during the planning phase - CI workflow templates for status checks
- PR templates with checklists mapped to behavioral patterns
Episodic-memory and user-preferences are fully independent — install either or both.
| RFC | Title | Status |
|---|---|---|
| RFC-001 | Intelligent Memory: Tag Index, Relevance Scoring, Proactive Recall, Semantic Consolidation | Accepted (Phases 1-3 shipped) |
| RFC-002 | Learning Loop: Violation Tracking, Pattern Refinement, Actionable Recall | Accepted (Phases 1-3 + 3b shipped + runtime-deployed) |
| RFC-003 | Pluggable Tool Adapters: Per-Platform Enforcement and Cross-Tool Messaging | Accepted (Phase 1 not yet started) |
| RFC-004 | BP-1 Auto-Pilot: Automated Rule-18 Implementation Workflow | Accepted (M0 + M1 + M2 shipped) |
| RFC-005 | em-move — atomic episode relocation between scopes | Draft |
| RFC-006 | Codex Review Adapter: Typed-Request Consumer with Failure Classification and Local Fallback | Accepted (harness shipped PR #222) |
All scripts are zero-dependency .mjs files using Node.js stdlib only. They output JSON to stdout.
node ~/.episodic-memory/scripts/em-store.mjs \
--project my-project \
--category decision \
--tags "auth,security" \
--summary "Chose JWT over session cookies" \
--body "JWT simplifies our stateless API design..." \
--scope global
# For long bodies (e.g. plan documents), use --body-file instead of --body (#196)
node ~/.episodic-memory/scripts/em-store.mjs \
--project my-project --category decision --summary "..." \
--body-file ./decision-body.mdnode ~/.episodic-memory/scripts/em-revise.mjs \
--original <episode-id> \
--summary "Switched to session cookies" \
--body "JWT token size became a problem..." \
--tags "auth,security"
# --body-file is also supported (#196)node ~/.episodic-memory/scripts/em-search.mjs --project my-project
node ~/.episodic-memory/scripts/em-search.mjs --query "JWT" --full
node ~/.episodic-memory/scripts/em-search.mjs --tag auth --category decision --since 2026-01-01
node ~/.episodic-memory/scripts/em-search.mjs --history <id> --full
node ~/.episodic-memory/scripts/em-search.mjs --include-supersedednode ~/.episodic-memory/scripts/em-list.mjs --project my-project --limit 5node ~/.episodic-memory/scripts/em-check-stale.mjs --project my-projectnode ~/.episodic-memory/scripts/em-seed-patterns.mjsnode ~/.episodic-memory/scripts/em-recall.mjs
node ~/.episodic-memory/scripts/em-recall.mjs --project my-project --limit 5
node ~/.episodic-memory/scripts/em-recall.mjs --days 14 --no-tracknode ~/.episodic-memory/scripts/em-prune.mjs --dry-run
node ~/.episodic-memory/scripts/em-prune.mjs --scope global --threshold 0.15
node ~/.episodic-memory/scripts/em-prune.mjs --check # exit 1 if prunable episodes exist# Scan all sources, report what would be redacted, no writes
node ~/.episodic-memory/scripts/em-backup.mjs --audit
# One-time setup: create the private GitHub repo + initial commit + push
node ~/.episodic-memory/scripts/em-backup.mjs --init
# Daily run: rsync sources, redact, commit, push
node ~/.episodic-memory/scripts/em-backup.mjs --sync
# Built-in redaction unit tests
node ~/.episodic-memory/scripts/em-backup.mjs --self-test
# Inspect resolved config (with secrets/PII masked)
node ~/.episodic-memory/scripts/em-backup.mjs --show-configMirrors ~/.episodic-memory/ (and any other configured sources) to a private GitHub repo, applying PII / secret redaction to the staging copy. Source files on disk are never modified. Config lives at ~/.config/em-backup/config.json or $EM_BACKUP_CONFIG; see examples/em-backup.config.example.json. Refuses --init / --sync without a config to prevent shipping raw personal memory.
# Dry-run (default): show what would happen, no disk writes
node ~/.episodic-memory/scripts/em-restore.mjs \
--from /path/to/cloned-backup-repo \
--source-map home-em=$HOME/.episodic-memory \
--source-map project-em=./.episodic-memory \
--tag workplan --from-date 2026-04-01
# Apply with full doc tree (MEMORY.md, feedback_*.md, knowledge_base/)
node ~/.episodic-memory/scripts/em-restore.mjs \
--from /path/to/backup --source-map home-em=$HOME/.episodic-memory \
--include-docs --apply
# Built-in tests
node ~/.episodic-memory/scripts/em-restore.mjs --self-testLossy-data note. em-backup applies content + path redaction. Restore CANNOT undo redaction — it materializes the backup as-is. Frame as "spin up a fresh machine from backup," not "recover the original." Files redacted via extra_redact_strings retain their [REDACTED] tokens; binary / oversized / symlinked files are absent and only summarized in the report.
Filters. --from-date / --to-date / --tag / --category / --source AND-compose; supersedes-chain ancestors are pulled in transitively so revision chains are intact.
Conflicts. Four-bucket classification (clean / identical / normalized-equal / overwrite). Default is fail-closed: existing target files are skipped and listed in the report. Override with --force, or write to side-by-side files with --conflict-mode=sidecar (writes <filename>.from-backup).
Project CLAUDE.md is git-tracked; restore refuses by default. Pass --restore-claude-md to override (or just use git restore CLAUDE.md). User-global ~/.claude/CLAUDE.md follows the standard conflict model.
Indexes. index.jsonl and tags.json are merged (union by id, set-union per tag) — local-only entries at the target are preserved. --rebuild-index is on by default after --apply (pass --no-rebuild-index to skip).
Common modes: --include-docs (whole memory tree, atomic via staging dir), --source LABEL (narrow to one source), --allow-duplicate-id (cross-source same-id), --allow-symlink-overwrite (target-side).
node ~/.episodic-memory/scripts/em-violation.mjs \
--pattern bp-001-implementation-workflow \
--summary "Skipped checkpoints" \
--body "Details..." \
--sequence "plan,code,push" \
--correct "plan,checkpoint,code,review,push"
# --body-file is also supported (#196)# Validate the workflow.lifecycle chain for a task at a given gate
node ~/.episodic-memory/scripts/em-workflow-validate.mjs \
--task <task-id> \
--gate <pre-checkpoint|post-checkpoint|push-allowed> \
[--pattern-id bp-001-implementation-workflow] \
[--worktree <abs-path>] [--branch <name>] [--head <sha>] \
[--scope local|global|all] [--strict]Pure validator (no side effects) for RFC-002 Phase 3b-H1 hook gates. Checks that the required workflow.lifecycle episodes exist for the task and gate, and verifies context (worktree / branch / HEAD) when those args are passed. Exits 0 on pass, 1 on fail, 2 on usage/IO error; always emits JSON { status, valid, gate, task, missing[], errors[], warnings[], episodes[] }. Hooks shell out to it and act on the result.
# Full report — per-pattern violation counts + classification
node ~/.episodic-memory/scripts/em-pattern-health.mjs
# CI/hook gate — exit 1 if any pattern needs attention
node ~/.episodic-memory/scripts/em-pattern-health.mjs --check
# Single pattern
node ~/.episodic-memory/scripts/em-pattern-health.mjs --pattern bp-001-implementation-workflow
# One-line summary
node ~/.episodic-memory/scripts/em-pattern-health.mjs --summary
# Tighter window + threshold (default: 30 days, 3 violations)
node ~/.episodic-memory/scripts/em-pattern-health.mjs --window-days 7 --min-violations 2
# Manual override when enforcement detection misses a hook
node ~/.episodic-memory/scripts/em-pattern-health.mjs --has-enforcement bp-006-push-after-verifySearches ~/.claude/hooks/, <project>/.claude/hooks/, <project>/.git/hooks/, and <project>/.github/workflows/ for pattern_id references to detect mechanical enforcement. Patterns flagged needs-enforcement are violated repeatedly with no hook to stop them; needs-attention means an enforcement file exists but violations still occur (escalate to a human).
Pluggable cross-tool review at scripts/second-opinion.mjs (RFC-006 implementation, shipped PR #222). One callable entry point handles em-store request → provider dispatch → preamble composition → reply parsing → consensus iteration. Replaces the manual em-store + codex exec + watcher + reply-episode recipe.
# Single-shot: write request → dispatch → write reply (synchronous)
node scripts/second-opinion.mjs request \
--provider codex --project . --storage episodic \
--body "review this diff..." --summary "diff review" --dispatch
# Consensus loop: dispatch → parse verdict → rebuttal-cb → next round
node scripts/second-opinion.mjs request \
--provider codex --project . --storage episodic \
--body-file plan.md --summary "plan review" \
--consensus --max-rounds 5 --rebuttal-cb scripts/my-rebuttal.mjsProviders: codex, claude-subagent, gemini, opencode (OpenCode CLI; DeepSeek v4-pro by default, overridable via OPENCODE_MODEL), stub (testing). Storage backends: files (.review-store/) or episodic (uses em-store; default for the cross-tool message bus). Preambles: per-provider defaults at scripts/second-opinion/preambles/, overridable via --preamble <id> or <project>/.review-store/preambles/<provider>.md.
Bootstrap (writes the install snapshot consumed by validators + the Claude Code PreToolUse gate hook):
node install.mjs --tool claude-code --install-second-opinionThe PreToolUse hook (plugins/claude-code/hooks/second-opinion-gate.mjs) blocks direct provider invocations (Bash + Agent variants) so reviews route through the harness. Fail-closed cases:
- Missing or malformed snapshot (parse error, missing
source_hash). - Invalid providers — empty
providers[], duplicateid, missing/non-compilablecli_matchregex, missingbinary, or non-arrayagent_block_patterns/agent_allow_patterns. Hook blocks with reasonsnapshot-invalid-providers. - Validator lib unloadable (orphan hook without
~/.claude/hooks/lib/registry-validator.mjs). Hook blocks with reasonsnapshot-validator-load-failed.
The shared validateProviderRegistry (scripts/second-opinion/lib/registry-validator.mjs) runs at install (Gate 1 + Gate 2), readSnapshot, and the hook — one contract, three enforcement points (PR #221 / #227).
--install-second-opinion is atomic across five failure paths. Gate 1 hard-stops before any file copy if the source registry is invalid. Beyond Gate 1, any failure — runtime-copy throw, validator-lib throw, Gate 2 throw, or writeSnapshot throw — renames any pre-existing ~/.claude/hooks/second-opinion-providers.json to second-opinion-providers.json.stale.<unix-ms> so the hook keeps fail-closing rather than reading a stale snapshot against newly-updated source. Re-run the install command to recover.
Underlying cursor mechanism for the harness's episodic storage backend, also usable standalone for the manual review fallback.
# Poll project-local memory for new Codex replies (default scope: local)
node ~/.episodic-memory/scripts/em-watch-codex.mjs
# Both stores with independent cursors
node ~/.episodic-memory/scripts/em-watch-codex.mjs --scope all
# Preview without advancing the cursor
node ~/.episodic-memory/scripts/em-watch-codex.mjs --no-update
# Replay from a specific episode id
node ~/.episodic-memory/scripts/em-watch-codex.mjs --since 20260501-073215-codex-review-of-em-watch-codex-mjs-plan-5420
# Override the project root (otherwise walks up from cwd to nearest .episodic-memory/index.jsonl)
node ~/.episodic-memory/scripts/em-watch-codex.mjs --project-root /path/to/repoReturns Codex-authored episodes (tag codex, codex-review, or codex-reply) newer than the last seen id. Cursor lives at <store>/state/codex-watcher.json with independent per-scope keys. Episode ids are total-ordered (YYYYMMDD-HHMMSS-<slug>-<hash>), so the cursor is immune to clock skew across tools. Cursor advances only to the max id of returned episodes — partial-line writes during concurrent em-store appends are skipped and re-read on the next invocation.
node ~/.episodic-memory/scripts/em-session-end-prompt.mjsnode ~/.episodic-memory/scripts/em-rebuild-index.mjs --scope allThe BP-1 Auto-Pilot suite mechanically enforces the bp-001 implementation workflow. These scripts are normally driven by install.mjs --bp1 and the SessionStart hook — operators usually don't invoke them directly.
# Activation gate — every gated artifact reads via flag-check (RFC-004 §158-167)
node ~/.episodic-memory/scripts/bp1-flag-check.mjs --project <root>
# Build the runtime-artifact manifest (single source of truth, RFC-004 §107-152)
node ~/.episodic-memory/scripts/bp1-build-artifact-manifest.mjs [--project <root>] [--yaml]
# Canonicalize an episode for HMAC signing (debug/inspection)
node ~/.episodic-memory/scripts/bp1-canonicalize.mjs --episode <path> [--pretty]
# Path A + Path B fallback executor (auto-wired as SessionStart H2 hook)
node ~/.episodic-memory/scripts/bp1-deadline-sweep.mjs --once [--project <root>]
# Orchestrator subcommands: init-run, finalize-run, finalize-recover (M1)
node ~/.episodic-memory/scripts/bp1-orchestrator.mjs init-run --project <root>
node ~/.episodic-memory/scripts/bp1-orchestrator.mjs finalize-run --run-id <id>
node ~/.episodic-memory/scripts/bp1-orchestrator.mjs finalize-recover --run-id <id>
# Marker validation — verify a checkpoint marker's HMAC + shape (slice 2d-W, #305)
node ~/.episodic-memory/scripts/bp1-marker-validate.mjs --marker <path>
# Crash-class classification for SessionStart resumption (slice 2e, #313)
node ~/.episodic-memory/scripts/bp1-crash-classify.mjs --run-id <id>
# Emit a marker-invalid evidence episode (used by the deadline-firing path, #313)
node ~/.episodic-memory/scripts/bp1-emit-marker-invalid-evidence.mjs --run-id <id> --reason <code>
# M2 finish-line: flip the BP-1 activation flag (slice 2f, #322)
node ~/.episodic-memory/scripts/bp1-flag-flip.mjs --enable # or --disableThe orchestrator also auto-stubs a missing run on first interaction via scripts/bp1-auto-stub.mjs — not normally invoked directly.
# Measure rule-skip rates from Claude Code session JSONL transcripts
# (heuristic — false positives expected; useful for trend tracking)
node ~/.episodic-memory/scripts/em-audit-compliance.mjs \
[--since <ISO>] [--slug <substr>] [--exclude-worktrees] [--format json|markdown]
# Surface decisions/lessons/violations buried in transcripts that were never
# captured as episodes; writes a staging file under .claude/scratch/, never
# calls em-store directly (cold-storage discipline)
node ~/.episodic-memory/scripts/em-mine-transcripts.mjs \
[--since <ISO>] [--slug <substr>] [--output <path>] [--dry-run]Bootstrap (install-launchd-routines.sh) installs four LaunchAgent plists so the recurring chores run without manual invocation:
| Plist label | Schedule | What runs |
|---|---|---|
com.charltonho.em-daily-mining |
daily 19:30 | episodic-memory-daily-mining SKILL — mines today's transcripts into a staging file under .claude/scratch/ |
com.charltonho.em-weekly-digest |
Sunday 09:00 | episodic-memory-weekly-digest SKILL — writes the weekly digest episode |
com.charltonho.instruction-hygiene |
Sunday 11:00 | instruction-hygiene-maintenance SKILL — lesson-set audit |
com.charltonho.em-backup-sync |
daily 23:00 | em-backup-sync-wrapper.sh — pushes the em-backup repo to remote |
The three SKILL-driven jobs invoke through a rendered wrapper (~/.claude/scheduled-tasks/em-skill-wrapper.sh) that reads the SKILL.md body and passes it to claude -p --. The -- separator is required because SKILL.md begins with YAML frontmatter (---) — without it, the arg parser treats the body as a long-option flag (PR #228).
bash install-launchd-routines.sh --dry-run # preview, no writes
bash install-launchd-routines.sh # install (no smoke test)
bash install-launchd-routines.sh --smoke # install + kickstart daily-mining
bash install-launchd-routines.sh --uninstall # bootout + delete plists (logs preserved)Logs land at ~/Library/Logs/episodic-memory/<job>.log. Each plist sets CLAUDE_SCHEDULED_TASK=1 so the SessionStart handoff prompt self-suppresses; WorkingDirectory and the wrapper both pin the project root so cwd-sensitive resolvers don't drift under launchd.
Lifecycle event for the workflow audit trail — orthogonal to the second-opinion harness above. This script records that a review happened (with refs to plan/approval/checkpoints/tests/etc.); the harness runs the review.
# Build + store a workflow.lifecycle review-request event with full ref
# validation (plan, approval, pre/post-checkpoint, tests, code review,
# bug log, command inventory). RFC-002 Phase 3b-H1 PR-D (#118).
node ~/.episodic-memory/scripts/em-review-request.mjs \
--task <id> \
--plan-ref <episode:id|file:path|url> \
--approval-ref <episode:id> \
--pre-checkpoint-ref <episode:id> \
--post-checkpoint-ref <episode:id> \
--tests-ref <episode:id|file:path> \
--code-review-ref <episode:id> \
[--bug-log-ref <issue-url>]+ | [--no-new-bugs] \
[--command-inventory-ref <ref>] [--dry-run]These are CI-only validators that diff prose-tier RFC content against the machine-readable source of truth. Per Rule 14 (machine-readable blocks for drift-prone state).
# Cross-check _index.json + RFC frontmatter + Active RFCs table in README.md
node ~/.episodic-memory/scripts/em-rfc-validate.mjs [--json]
# RFC-004 §107-152: artifact-manifest YAML block ↔ builder output parity
node ~/.episodic-memory/scripts/validate-rfc-artifact-manifest.mjs [--json]
# RFC-004 §689-719: canonical-fields spec ↔ bp1-canonicalize.mjs lib parity
node ~/.episodic-memory/scripts/validate-rfc-canonical-fields.mjs [--json]
# RFC-004 §1072: §11.5 failure-table prose markdown ↔ YAML mirror parity
node ~/.episodic-memory/scripts/validate-rfc-failure-table.mjs [--json]MIT