cc-os/openspec/specs/memory-plugin-hooks/spec.md

7.6 KiB

Spec: memory-plugin-hooks

Purpose: Define the Claude Code hook behaviors for the memory plugin — SessionStart context injection and staleness-driven graph rebuild, PostToolUse vault graph update, SessionEnd episodic journal append, and plugin configuration.

Last updated: 2026-06-05


Requirements

Requirement: SessionStart hook — staleness check and detached background rebuild

The plugin SHALL register a SessionStart hook that reads the rebuild stamp file at ~/.cache/graphify/vault-rebuild.stamp. If the stamp is absent or its mtime is older than the configured threshold (default: 7 days), the hook SHALL spawn graphify extract <vault-path> --backend ollama --model <configured-model> --force as a detached background process (nohup/setsid, output redirected to a log file) and SHALL return immediately without waiting for it. The background process SHALL update the stamp file upon successful completion and remove the lock file. The hook SHALL NOT block on the rebuild — SessionStart blocks the entire session until the hook returns, so the synchronous path must be sub-second. (Source: Claude Code hooks documentation — SessionStart has no async option.) The hook SHALL write a lock file (~/.cache/graphify/vault-rebuild.lock) before spawning, so concurrent sessions do not launch duplicate rebuilds.

Scenario: Stamp is fresh (within threshold)

  • WHEN the SessionStart hook fires and the rebuild stamp mtime is within the configured threshold
  • THEN no extraction run is triggered and the hook proceeds to context injection

Scenario: Stamp is stale or absent

  • WHEN the SessionStart hook fires and the rebuild stamp is absent or older than the configured threshold
  • THEN the hook checks for a lock file; if absent, spawns a detached background rebuild and writes the lock; in all cases the hook continues immediately to context injection using the existing graph; the rebuilt graph becomes available to the next session

Scenario: Rebuild already in progress (lock file present)

  • WHEN the SessionStart hook fires and the lock file already exists (rebuild spawned by a concurrent session)
  • THEN the hook does NOT spawn another rebuild; it logs that a rebuild is in progress and proceeds to context injection

Scenario: Extraction fails during stale rebuild

  • WHEN the background rebuild process exits non-zero
  • THEN the background process logs the error to its log file and removes the lock file without touching the stamp; the current session is unaffected (already past injection); the next session will attempt the rebuild again

Requirement: SessionStart hook — vault context injection

The plugin SHALL register a SessionStart hook that injects the following into Claude's context at the start of each session: (a) the vault graph's god-node summary (top N most-connected nodes by degree, sourced from GRAPH_REPORT.md or equivalent query), (b) the summaries of convention/* vault notes (sourced via graphify query "convention" or, as a fallback, by grepping vault frontmatter for convention/ tags and reading the summary: field), and (c) a pointer to the current day's episodic journal note path. The hook SHALL deliver this content by writing {"additionalContext": "<assembled-text>"} JSON to stdout and exiting 0 — this is the confirmed Claude Code mechanism for hook-to-model context injection. (Source: Claude Code hooks documentation.) The hook reads its input from stdin JSON; relevant fields include .cwd (working directory) and .source (startup|resume|clear|compact).

Scenario: Vault graph and convention notes exist

  • WHEN SessionStart fires and the vault graph and at least one convention/* note exist
  • THEN the injected context contains the god-node list, all convention/* summaries, and the journal note path

Scenario: No convention notes exist yet

  • WHEN SessionStart fires and no convention/* notes exist in the vault
  • THEN the injected context contains the god-node list and journal pointer; the conventions section is omitted without error

Scenario: Project graph exists for the current project

  • WHEN SessionStart fires and a file exists at <project-root>/graphify-out/graph.json
  • THEN the injected context also includes the project graph path, so Claude can query it via graphify query --graph <path>

Requirement: PostToolUse hook — event-driven vault graph update

The plugin SHALL register a PostToolUse hook that fires after any Write or Edit tool call. The hook SHALL check whether the target file path is a .md file under the configured vault path (~/Documents/SecondBrain). If it is, the hook SHALL run graphify update --file <path> to merge the changed note into the vault graph. If the target path is outside the vault, the hook SHALL do nothing.

Scenario: A vault note is written or edited

  • WHEN a Write or Edit tool call completes and the target path is under the vault directory
  • THEN the hook runs graphify update --file <target-path> synchronously before Claude proceeds

Scenario: A non-vault file is written or edited

  • WHEN a Write or Edit tool call completes and the target path is outside the vault directory
  • THEN the hook does nothing; no graphify call is made

Scenario: graphify update fails for a vault write

  • WHEN graphify update --file <path> exits non-zero
  • THEN the hook logs the error but does NOT block the tool call result from being returned; the graph may be transiently stale

Requirement: SessionEnd hook — episodic journal append

The plugin SHALL register a SessionEnd hook that appends a dated entry to the daily journal note at <vault-path>/journal/YYYY-MM-DD.md (creating the file if it does not exist). The entry SHALL include at minimum: the current project name (or path), the vault note paths touched during the session, and a UTC timestamp. The journal file is append-only; existing content is never overwritten.

Scenario: Session ends with vault notes touched

  • WHEN the SessionEnd hook fires and at least one vault note was written or edited during the session
  • THEN the hook appends a timestamped entry to the daily journal file listing the touched note paths and the project context

Scenario: Session ends with no vault writes

  • WHEN the SessionEnd hook fires and no vault notes were touched
  • THEN the hook still appends a minimal entry (timestamp + project) so the session is recorded for episodic recall

Scenario: Journal file does not exist yet for the day

  • WHEN the SessionEnd hook fires and <vault-path>/journal/YYYY-MM-DD.md does not exist
  • THEN the hook creates the file with appropriate frontmatter (summary, scope/global, type/log) and appends the session entry

Requirement: Plugin configuration block

The plugin SHALL expose a configuration file at cc-os/plugins/memory/config.yaml that allows setting: vault path, Graphify output directory, Ollama model name (Modelfile-baked variant), num_ctx, rebuild-stale threshold in days, and the env vars OLLAMA_FLASH_ATTENTION, GRAPHIFY_OLLAMA_NUM_CTX, GRAPHIFY_OLLAMA_KEEP_ALIVE. All hooks SHALL read from this config rather than hardcoding paths or values.

Scenario: Default config is used

  • WHEN the config file exists with no overrides for a setting
  • THEN the hook uses the default value for that setting (vault: ~/Documents/SecondBrain, threshold: 7 days, model: qwen25-coder-7b-16k)

Scenario: User overrides vault path

  • WHEN the config file specifies a non-default vault path
  • THEN all hooks use the configured path for vault detection, graph paths, and journal writes