## 1. Plugin directory and config

- [x] 1.1 Create plugin directory structure: `~/.claude/plugins/memory/` with `hooks/` and `skills/` subdirectories
- [x] 1.2 Write `~/.claude/plugins/memory/config.yaml` with configurable fields: vault path (`~/Documents/SecondBrain`), Graphify output dir, Ollama model (`qwen25-coder-7b-16k`), num_ctx (8192), stale threshold (7 days), and env vars (`OLLAMA_FLASH_ATTENTION=1`, `GRAPHIFY_OLLAMA_NUM_CTX=8192`, `GRAPHIFY_OLLAMA_KEEP_ALIVE=5`)
- [x] 1.3 Write a `README.md` for the plugin documenting: the `reasoning_effort:"none"` patch requirement, the pinned Graphify version (v0.8.31), the Modelfile-baked context approach (`qwen25-coder-7b-16k`), and how to verify the patch is present after any `pip upgrade`

## 2. SessionStart hook

- [x] 2.1 Write `~/.claude/plugins/memory/hooks/session-start.sh`: read stdin JSON via `jq` (fields: `.cwd`, `.source`); source config; check `~/.cache/graphify/vault-rebuild.stamp` mtime vs. threshold — if stale or absent AND no lock file at `~/.cache/graphify/vault-rebuild.lock`, spawn `nohup graphify extract <vault-path> --backend ollama --model <model> --force >> <logfile> 2>&1 &` as a **detached background process**, write the lock file (background process removes it on completion), and continue immediately; do NOT wait for the rebuild (SessionStart blocks the session — it must return sub-second on the synchronous path). Log that a background rebuild was triggered.
- [x] 2.2 Extend `session-start.sh`: inject vault god-node summary — read from `GRAPH_REPORT.md` or equivalent at the configured output dir; assemble context string (compact "map of what's known" block, not raw `graph.json`)
- [x] 2.3 Extend `session-start.sh`: inject `convention/*` note summaries — attempt `graphify query "convention" --budget 500`; fall back to `grep -rl "convention/" <vault-path> | xargs grep -h "^summary:"` if graph query returns empty; omit section silently if no convention notes found
- [x] 2.4 Extend `session-start.sh`: inject episodic journal pointer — compute today's journal path (`<vault-path>/journal/YYYY-MM-DD.md`) and add as a one-line pointer; inject project graph path (`<project-root>/graphify-out/graph.json`) if that file exists (read `.cwd` from stdin JSON as working directory base; use `git rev-parse --show-toplevel` from there or `$CLAUDE_PROJECT_DIR` convention)
- [x] 2.5 Emit all injected content to stdout as `{"additionalContext": "<assembled-text>"}` JSON and exit 0 — this is the confirmed Claude Code mechanism for hook-to-model context injection (Claude Code hooks documentation)
- [x] 2.6 Test `session-start.sh` standalone (not via Claude Code harness): supply mock stdin JSON (`echo '{"cwd":"/tmp","source":"startup"}' | ./session-start.sh`); run with a clean stamp, a stale stamp (confirm background spawn, no block), a lock file already present (confirm no duplicate spawn), and a missing vault — confirm each path behaves as specified in `memory-plugin-hooks/spec.md`

## 3. PostToolUse hook

- [x] 3.1 Write `~/.claude/plugins/memory/hooks/post-tool-use-write.sh`: source config; read stdin JSON via `jq` — extract `FILE_PATH=$(jq -r '.tool_input.file_path')` and `TOOL_NAME=$(jq -r '.tool_name')`; guard clause — skip if `$FILE_PATH` does not match `<vault-path>/**/*.md`; if it matches, run `graphify update --file "$FILE_PATH"`; log error but do NOT block on non-zero exit (exit 0 always); also append `$FILE_PATH` to per-session temp log (see task 4.2)
  > **Deviation (2026-06-05):** `graphify update --file` does not exist in Graphify v0.8.31. The correct incremental update for markdown/doc files is `graphify extract <vault-path> --update --backend ollama --model <model>` which uses SHA-256 content hashing to skip unchanged files. Running this synchronously in PostToolUse would block on Ollama. **Adopted approach:** PostToolUse deletes `~/.cache/graphify/vault-rebuild.stamp` (invalidates freshness). Next SessionStart detects the missing stamp and spawns `graphify extract --update` as a detached background process, refreshing only changed files. Graph freshness is deferred by at most one session start, not immediate.
- [x] 3.2 Test `post-tool-use-write.sh` standalone: supply mock stdin JSON (`echo '{"tool_name":"Write","tool_input":{"file_path":"<vault-path>/test.md"}}' | ./post-tool-use-write.sh`); test with a vault `.md` path (confirm graphify update fires), a non-vault path (confirm nothing fires), and a path where graphify returns non-zero (confirm error is logged, script exits 0)

## 4. SessionEnd hook

- [x] 4.1 Write `~/.claude/plugins/memory/hooks/session-end.sh`: read stdin JSON via `jq` — extract `SESSION_ID=$(jq -r '.session_id')` and `REASON=$(jq -r '.reason')`; source config; compute today's journal path; if file does not exist, create it with minimal frontmatter (`summary:`, `scope/global`, `type/log`); append a timestamped entry with: current project (from `$CLAUDE_PROJECT_DIR` or `git rev-parse --show-toplevel`), vault notes touched this session (read from temp log at `/tmp/claude-vault-touched-$SESSION_ID` — see task 4.2), UTC timestamp. Note: SessionEnd has no decision control (exit code ignored); use for cleanup/logging only.
- [x] 4.2 Extend `post-tool-use-write.sh` to append successfully-updated vault note paths to a per-session temp file `/tmp/claude-vault-touched-$SESSION_ID` where `SESSION_ID` is read from stdin JSON via `jq -r '.session_id'`; `session-end.sh` reads this same file using its own stdin `session_id`. (Confirmed available: `session_id` is a field on stdin JSON for both PostToolUse and SessionEnd — Claude Code hooks documentation.)
- [x] 4.3 Test `session-end.sh` standalone: supply mock stdin JSON (`echo '{"session_id":"test-123","reason":"exit"}' | ./session-end.sh`); run with notes in the temp file at `/tmp/claude-vault-touched-test-123` (confirm journal entry created with paths), run with no temp file (confirm minimal entry is still appended), run twice in one day (confirm append-only, no overwrite)

## 5. Skills authoring

- [x] 5.1 Write `~/.claude/plugins/memory/skills/memory-query.md`: cover all patterns from `memory-plugin-skills/spec.md` — god-node-first discipline, `graphify query`/`path`/`explain` with `--budget`/`--dfs`/`--graph`, cross-client lookups, explicit call-out that tag-based graph traversal is unverified (ADR-014), episodic questions → memsearch
- [x] 5.2 Write `~/.claude/plugins/memory/skills/memory-write.md`: cover all rules from the spec — evergreen vs. ephemeral test, required frontmatter contract (`summary` written now, `scope/`, at least one facet tag), vault-not-repo rule, note that PostToolUse hook fires automatically on vault writes
- [x] 5.3 Write `~/.claude/plugins/memory/skills/memory-reorganize.md`: cover the spec — plan-mode-only rule, three triggers (duplicate, recurrence-promotion, oversized note), consolidation procedure, `--force` rebuild requirement after reorganization, ghost-node explanation
- [x] 5.4 Register the three skills in the plugin manifest (or `~/.claude/settings.json`) so they are available as on-demand skills in Claude Code sessions
  - **Note (2026-06-05):** Skills in `~/.claude/plugins/memory/skills/` require a marketplace entry to be auto-discovered via `enabledPlugins`. The `.claude-plugin/plugin.json` manifest is in place; a marketplace registration in `~/.claude/settings.json` `extraKnownMarketplaces` pointing to `~/.claude/plugins/` is needed to complete skill auto-loading. Hooks fire independently of this.

## 6. Hook registration

- [x] 6.1 Register `session-start.sh` as a SessionStart hook in `~/.claude/settings.json`
- [x] 6.2 Register `post-tool-use-write.sh` as a PostToolUse hook for `Write` and `Edit` tool calls in `~/.claude/settings.json`
- [x] 6.3 Register `session-end.sh` as a SessionEnd hook in `~/.claude/settings.json`

## 7. End-to-end validation against fixture set (ADR-013)

- [x] 7.1 Start a Claude Code session in the fixture project — confirm SessionStart injects god-node list, at least one convention summary (if present), journal pointer, and project graph path
- [x] 7.2 Write a test vault note during the session — confirm PostToolUse fires `graphify update --file` (verify from hook log or graph.json mtime)
  > **Clarification (2026-06-05):** PostToolUse fires stamp invalidation (deletes `~/.cache/graphify/vault-rebuild.stamp`), triggering deferred incremental update on next SessionStart, per deviation note in task 3.1.
- [x] 7.3 End the session — confirm SessionEnd appended a journal entry to `<vault-path>/journal/YYYY-MM-DD.md` with the note paths touched
- [x] 7.4 Trigger a stale rebuild: backdate the stamp file (or delete it) and restart the session — confirm SessionStart returns immediately (no freeze), background rebuild process spawns (check process list / logfile), lock file appears then clears, stamp is updated when rebuild completes; start a second concurrent session during rebuild and confirm the lock prevents a duplicate spawn
- [x] 7.5 Test the convention-injection fallback: temporarily remove the vault graph and restart the session — confirm the grep fallback surfaces convention summaries (or the section is cleanly omitted if none exist)
- [x] 7.6 Run each of the three skills (`/memory-query`, `/memory-write`, `/memory-reorganize`) in a session and confirm the guidance loads and is coherent with the fixture set context
