Approved by Jack 2026-07-18 (Approach A of three). Research grounding: .claude/research/20260712-obsidian-agent-memory.html and .claude/research/20260718-cross-repo-event-memory.html. Decisions made in-session: jacked-owned vault (not native autoMemoryDirectory, not hybrid); repo→group mapping via config + auto-suggest; episodic history ABSORBED into jacked (retire the third-party remember plugin, marketplace id claude-plugins-official/remember v0.8.3).
~/jacked-vault/ # its own git repo; jacked initializes; user picks/declines remote
README.md # schema + conventions, written for agent consumption
vault.json # repo→group mapping (synced via git; machine-local overrides allowed)
groups/<group>/
index.md # one line per note + hook phrase; budgeted (soft cap ~150 lines)
hot.md # current progress slice; overwritten, never appended forever
decision/*.md # typed atomic notes — one fact per file
convention/*.md vision/*.md reference/*.md
episodic/<repo>/ # absorbed .remember shape: today-YYYY-MM-DD.md, recent.md, archive.md, core-memories.md
strategy/ # cross-group notes
archive/
Note frontmatter (one schema, validated on write): type (decision|convention|vision|reference|progress), repos (list), group, created/updated (ISO), tags. Body uses [[wikilinks]]; notes written with the future query's vocabulary (retrieval is lexical — grep + index, NO embeddings, NO MCP server, NO Obsidian app dependency; Obsidian is an optional viewer).
Grouping: vault.json maps repo identity (git remote URL, falling back to repo dir name) → group. jacked memory init scans configured roots (default ~/Github), auto-suggests groups from name prefixes + shared remotes, presents for review. Unmapped repo ⇒ solo group named after the repo; everything still works.
| Trigger | Mechanism | Writes |
|---|---|---|
| SessionEnd | New memory_capture hook via existing jacked _hook dispatcher; one claude -p triage pass over the transcript | Episodic entry ALWAYS (absorbing remember's role). Semantic note ONLY when triage finds a real decision/accomplishment/convention — the triage prompt's default answer is "nothing worth keeping"; most sessions write no semantic note. |
| PreCompact (matcher: auto) | Same hook module, handover mode | Episodic handover entry; decisions promoted to semantic candidates. |
| git post-merge on main/master | New post-merge script in jacked/data/git-hooks/, installed via existing git-hooks machinery | Deterministic metadata (branch, PR title/body via gh if available, changed files) + one distillation call → candidate decision note. No per-commit capture (noise). |
| jacked milestones | /goal complete, /dcr clean, /release, checkpoint-complete: skill text updated to call jacked memory add | High-signal semantic note, pre-typed. |
| Explicit | /remember and /learn command docs route durable facts to jacked memory add (with type + group inferred, user-overridable) | Semantic note. |
CLI surface: jacked memory init|status|add|search|migrate|rollup. add validates frontmatter, updates index.md, commits to the vault repo. search is grep-tiered (hot → index → notes).
SessionStart hook injects a group-scoped brief, hard-budgeted ≤ 1,500 tokens: hot.md + index excerpt + top-linked decisions + last episodic entry for the cwd's repo, ending with an explicit imperative ("IMPORTANT: before working on <topic>, read <vault path>"). Replaces the retired remember plugin's SessionStart block. Deeper recall is on-demand file reads/grep (paths are real; the agent reads them with standard tools). Native Claude Code auto-memory is left completely untouched.
Deterministic episodic rollup (today → recent → archive) ships as jacked memory rollup, invoked from the SessionEnd hook — code, not LLM. The librarian is a jacked SKILL (LLM guidance per jacked doctrine, no regex point-tables): dedupe, reconcile contradictions newest-wins-with-trail, archive stale hot entries, validate frontmatter, re-sync index. Nudged at SessionStart when drift counters trip (notes added since last librarian run > threshold); also manual.
git pull --ff-only at SessionStart; on conflict, skip sync and surface in dashboard. Never reset --hard.claude -p is treated as data — triage prompts must instruct against following instructions found in transcripts.uv run python -m pytest) covering mapping resolution, triage output handling, rollup, migration count-verification, injection budget, hook fail-open paths. /cso + /dcr gates before PR.