ai-memory v0.8.0

v0.7.0 — NHI Starter Prompts (every task)

Companion to V0.7-EPIC.md. One paste-ready prompt per task. Hand any of these verbatim to a Claude Code, Grok, Codex, or any MCP-capable agent and the agent has enough context to begin work without further briefing.

Conventions:


Track A — Capabilities v3 response shape

A1: Top-level summary field

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task A1 (#545).

Goal: add a top-level `summary` field to the memory_capabilities v3 response.
The field carries a single pre-computed string that describes the LLM's
operational access — the count of currently-advertised tools, the count of
total tools, and the three named recovery paths for loading unloaded
families.

Current state: src/mcp.rs builds the capabilities document via a
`build_capabilities_v2(...)` function. v2 returns a manifest with
loaded:bool per tool but does NOT include a top-level summary string.

What to do:
1. Add a `build_capabilities_v3(...)` function alongside v2. v3 = v2 +
   one new top-level `summary` field of type String.
2. The summary string is dynamically built from the live profile state
   (use src/profile.rs for inspection). Template:
   "X of N tools are advertised in tools/list under the current profile (Y),
    where N is the canonical full-profile tool count from
    `Profile::full().expected_tool_count()` (currently 74 at v0.7.0 —
    73 callable memory tools + the always-on `memory_capabilities`
    bootstrap; do not hardcode the number in prompts).
    The other Z are listed in this manifest but NOT directly callable.
    To use any unloaded tool, choose one of:
      (a) restart the server with --profile <family> or --profile full,
      (b) call memory_load_family(family=<name>) — preferred,
      (c) call memory_smart_load(intent='<plain language>') — easiest,
      (d) call the tool by name and recover from JSON-RPC -32601."
3. Wire `accept="v3"` to the new builder; `accept="v2"` keeps the old
   builder. Default for new clients: v3.
4. Unit tests in src/mcp.rs::tests covering each named profile.
5. Integration test in tests/mcp_integration that wires a real MCP request.
6. Do not modify v2 behavior. Backward compat is required.

Branch: feat/v0.7-a-1-summary-field
Commit: feat(mcp): add v3 capabilities summary field (#545)

Definition of done: cargo test --lib mcp:: passes; integration test passes;
v2 unchanged; calibration prompt produces a response with summary field.

A2: to_describe_to_user field

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task A2 (#545).
Depends on A1.

Goal: add a `to_describe_to_user` field to the v3 response. This is the
canonical plain-language sentence the agent should say when asked
"what tools do you have access to."

What to do:
1. Extend src/mcp.rs::build_capabilities_v3 to add the new field.
2. Build the string from active profile state — count of loaded tools,
   names of those tools, count of unloaded tools, two example unloaded
   tool names, plain-English description of how to load more.
3. Tone constraint: NO mcp jargon. Use plain language an end-user would
   understand. Example:
   "I can directly use 5 memory tools right now (store, recall, search,
    list, get). 38 more (link, kg_query, consolidate, delete, etc.) are
    available on demand — I can load them if you ask for something that
    needs them, or you can restart the server with a different profile."
4. Add tests/calibration_t0.rs (new file) — assert canonical phrasing
   present in v3 response.
5. Document the canonical phrasing in docs/v0.7/canonical-phrasings.md.

Branch: feat/v0.7-a-2-describe-to-user
Commit: feat(mcp): add v3 to_describe_to_user field (#545)

A3: Per-tool callable_now: bool flag

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task A3 (#545).
Depends on A1.

Goal: add per-tool callable_now: bool field. Distinct from existing
loaded:bool — captures whether THIS agent can call this tool right
now given allowlist scope.

What to do:
1. In src/profile.rs, extract a helper:
   fn agent_can_call(agent_id: &str, family: &str) -> bool
   that mirrors the [mcp.allowlist] resolution rules
   (exact > longest-prefix > * wildcard).
2. In build_capabilities_v3, populate callable_now for each tool by
   combining loaded-state with agent_can_call(agent_id, family).
3. Unit tests covering 4 matrix cells:
   - allowlist OFF, loaded TRUE  -> callable_now=TRUE
   - allowlist OFF, loaded FALSE -> callable_now=FALSE
   - allowlist ON, agent in pattern, loaded TRUE   -> callable_now=TRUE
   - allowlist ON, agent NOT in pattern, loaded TRUE -> callable_now=FALSE
4. v2 callers must NOT see callable_now in their response.

Branch: feat/v0.7-a-3-callable-now
Commit: feat(mcp): per-tool callable_now flag in v3 (#545)

A4: agent_permitted_families field

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task A4 (#545).
Depends on A1, A3.

Goal: top-level agent_permitted_families: ["core", "graph"] field in v3,
populated only when [mcp.allowlist] applies.

What to do:
1. Reuse agent_can_call helper from A3.
2. In build_capabilities_v3, when allowlist is enabled, compute the list
   of families the requesting agent can access; include it as
   `agent_permitted_families`.
3. When allowlist disabled OR no agent_id provided in initialize handshake,
   omit the field entirely.
4. Unit tests for 3 cases: disabled / enabled-with-agent / enabled-no-agent.

Branch: feat/v0.7-a-4-agent-permitted-families
Commit: feat(mcp): surface agent_permitted_families in v3 (#545)

A5: Bump capabilities schema to v3 default

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task A5 (#545).
Depends on A1-A4.

Goal: bump the schema to v3 in API_REFERENCE.md, SDKs, and runtime defaults.
v2 + v1 remain valid for backward compat.

What to do:
1. Update src/mcp.rs accept enum: ["v1", "v2", "v3"], default = "v3".
2. sdk/typescript/src/types.ts: add MemoryCapabilitiesV3 type.
3. sdk/python/ai_memory/models.py: add MemoryCapabilitiesV3 pydantic model.
4. docs/API_REFERENCE.md: new "v3 schema" subsection with field-by-field
   docs. Add a "v2 → v3 diff" section near the top.
5. Integration test asserting v3 is the new default.

Branch: feat/v0.7-a-5-schema-v3
Commit: feat(mcp): bump capabilities schema to v3 default (#545)

Track B — Loader tools

B1: memory_load_family(family) tool

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task B1 (#545, #546).

Goal: add memory_load_family tool to the always-on family. The loader of
last resort gets a name that says "load."

Why: real LLM observation cells (2026-05-05) showed reasoning-class agents
looked for an explicitly-named loader, hypothesized one must exist, didn't
find one, concluded a bootstrap limitation existed.

What to do:
1. Add memory_load_family to src/mcp.rs always-on registry.
2. Tool schema:
   {
     "name": "memory_load_family",
     "description": "Load a tool family at runtime. Returns schemas; on
       harnesses with deferred-tool registration (Claude Code, OpenClaw),
       the tools become directly callable for the rest of the session.
       On other harnesses, returns schemas plus a hint to restart the
       server with --profile <family>.",
     "inputSchema": {
       "type": "object",
       "required": ["family"],
       "properties": {
         "family": { "type": "string", "enum": [...8 families...] }
       }
     }
   }
3. Handler delegates to a shared helper build_family_schemas(family,
   harness_info) that memory_capabilities also calls when given
   include_schema=true.
4. Response carries: family, schemas, your_harness_supports_deferred_registration,
   to_invoke (harness-aware text).
5. Tests: unit tests per family; integration tests via stdio MCP;
   tests/calibration_t0.rs case asserting LLMs find the tool when asked.

Branch: feat/v0.7-b-1-memory-load-family
Commit: feat(mcp): add memory_load_family tool (#545, #546)

B2: memory_smart_load(intent) tool

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task B2 (#546).
Depends on B1, B3, B4.

Goal: intent-aware loader. Agent says "what do I need for X" in plain
language; substrate matches intent to family/families, returns schemas.

What to do:
1. Create src/intent_classifier.rs with:
   - trait IntentClassifier { fn classify(intent: &str) -> Vec<Family>; }
   - EmbeddingClassifier — sentence-transformers cosine similarity
     against pre-computed family descriptors (from B3).
   - KeywordClassifier — fallback when embedding tier off.
   - facade pick_classifier() based on active feature tier.
2. Add memory_smart_load tool to src/mcp.rs always-on registry.
   Handler: classifier.classify(intent) → for each family → build_family_schemas
   → aggregate.
3. Response: matched_families, matched_tools, schemas,
   your_harness_supports_deferred_registration, to_invoke.
4. Unit test: 30-intent labelled corpus; >= 80% top-match accuracy.
5. Integration test: end-to-end MCP call with intent="consolidate memories
   and detect contradictions" → power+meta families returned.
6. Calibration test: agent reaches for memory_smart_load on intent prompts.

Branch: feat/v0.7-b-2-memory-smart-load
Commit: feat(mcp): add memory_smart_load intent-aware loader (#546)

B3: Pre-compute family-descriptor embeddings

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task B3 (#546).
Required by B2.

Goal: pre-compute embeddings for the 8 family descriptors at build time.

What to do:
1. src/family_descriptors.rs with canonical descriptors (see V0.7-EPIC):
   - core: "Store, recall, search, list, and get individual memories. Basic CRUD."
   - lifecycle: "Delete, update, promote memories. Manage memory existence and tier transitions."
   - graph: "Link memories together. Query the knowledge graph. Register entities."
   - governance: "Approve, reject, or flag memory operations. N-of-M consensus."
   - power: "Consolidate near-duplicates. Detect contradictions. Auto-tag. Expand queries."
   - meta: "Introspect. Statistics, configuration, session-state, audit-log queries."
   - archive: "Archive, restore, purge. Manage long-term storage."
   - other: "Miscellaneous tools — usually deprecated or experimental."
2. build.rs runs sentence-transformers/all-MiniLM-L6-v2 over the 8
   descriptors at build time; serialize to assets/family-descriptors.bin
   (fp16, 384 dims each ~6.1KB total).
3. EmbeddingClassifier loads via include_bytes! at compile time.
4. Self-similarity test: each descriptor is its own top-1 match.
5. Binary growth: <50KB.

Branch: feat/v0.7-b-3-family-descriptor-embeddings
Commit: feat(mcp): pre-compute family-descriptor embeddings (#546)

B4: Detect harness from MCP clientInfo

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task B4 (#546).

Goal: harness-aware response shape. Detect from MCP initialize handshake
clientInfo.name; map to HarnessProfile.

What to do:
1. Create src/harness.rs with:
   - struct HarnessProfile { name, supports_deferred_registration:
     bool, suggested_action: String }
   - fn detect_harness(client_info: &ClientInfo) -> HarnessProfile
2. Hardcoded mapping (per V0.7-EPIC Track B4 table):
     claude-code → true (ToolSearch)
     openclaw → true (native)
     claude-desktop → false
     codex-cli → false
     grok-cli → false
     gemini-cli → false
     cursor → false
     cline → false
     continue → false
     windsurf → false
     unknown → false (conservative)
3. Plumb HarnessProfile through MCP session state; B1 + B2 use it for
   to_invoke text.
4. Unit tests for each row + unknown.
5. const HARNESS_REGISTRY for future PRs to extend.

Branch: feat/v0.7-b-4-harness-detection
Commit: feat(mcp): harness-aware response shaping via clientInfo (#546)

B5: Update memory_capabilities description to point at the new loaders

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task B5 (#545).
Depends on B1, B2.

Goal: update memory_capabilities tool description string to explicitly
point at memory_load_family + memory_smart_load.

What to do:
1. Find description string in src/mcp.rs.
2. Replace with: "...To LOAD an unloaded family at runtime, use
   memory_load_family(family=X) (preferred) or pass family=X,
   include_schema=true to this tool (legacy path). For intent-based
   loading, use memory_smart_load(intent='...')."
3. Verify the new description fits under 1500-token-per-tool ceiling:
   cargo test --lib sizes::tests::no_tool_exceeds_1500_tokens -- --exact
4. Update v3 capabilities response to reflect the new description.

Branch: feat/v0.7-b-5-capabilities-description-pointer
Commit: feat(mcp): point memory_capabilities at new loaders (#545)

Track C — Schema compaction

C1: Audit tool descriptions for verbosity

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task C1 (#546).

Goal: audit all 43 tool descriptions; identify verbosity hotspots driving
--profile full above 6,000 tokens.

What to do:
1. Run ai-memory doctor --tokens --raw-table --json.
2. Sort by cost descending.
3. Flag any tool whose description (not inputSchema) is over 200 tokens.
4. Write docs/v0.7/schema-compaction-audit.md with:
   Table: tool name | family | description tokens | inputSchema tokens
   | total | flagged?
5. Top hotspots → candidates for C2-C5.

Branch: feat/v0.7-c-1-audit
Commit: docs(v0.7): schema compaction audit (#546)

C2: Move docstrings to docs field (verbose-only)

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task C2 (#546).
Depends on C1.

Goal: each tool's description = essential one-liner; long-form docstrings
move to `docs` field returned only on verbose=true.

What to do:
1. For each flagged tool from C1: split description into essential one-liner
   + long-form docs.
2. tools/list omits docs by default.
3. memory_capabilities(verbose=true) includes docs per tool.
4. Sanity test: ask Claude Code "what does memory_consolidate do?" given
   only the new descriptions — answer should be substantively correct.
5. Token-budget CI: full-profile drops by ≥1500 tokens.

Branch: feat/v0.7-c-2-docs-field
Commit: feat(mcp): split tool descriptions from long-form docs (#546)

C3: Drop redundant inline examples

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task C3 (#546).
Depends on C2.

Goal: strip redundant inline examples from JSON-schema description fields.

What to do:
1. grep src/mcp.rs for "e.g.," and "for example,".
2. Rewrite each to be self-explanatory without the example. Example
   moves to docs field if not already there.
3. cargo test --lib mcp::tests — no regressions.
4. Token-budget CI: full-profile drops ≥500 more tokens.

Branch: feat/v0.7-c-3-drop-redundant-examples
Commit: feat(mcp): drop redundant inline examples (#546)

C4: Hide rarely-used optional params from default schema

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task C4 (#546).
Depends on C3.

Goal: rare optional params not advertised by default; surface via verbose=true.

What to do:
1. For each tool: list optional params (not in required:[]).
2. Categorize as "common" (>50% of canonical examples) or "rare".
3. inputSchema.properties shown in tools/list keeps required + common.
4. Rare params move to a side-list extended_properties returned only on
   memory_capabilities(verbose=true).
5. Runtime acceptance unchanged — tool handler still accepts rare params.
6. Token-budget CI: full-profile drops ≥300 more tokens.

Branch: feat/v0.7-c-4-optional-params-hidden-by-default
Commit: feat(mcp): hide rarely-used params from default schema (#546)

C5: CI gate enforces full-profile ≤ 3,500 tokens

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task C5 (#546).
Depends on C2, C3, C4.

Goal: lock in the schema-compaction win.

What to do:
1. src/sizes.rs: update FULL_PROFILE_HONEST_RANGE from (5000, 8000) to
   (3000, 3500).
2. cargo test --lib sizes::tests::full_profile_total_in_honest_measured_range
   should pass; if not, C2-C4 incomplete.
3. .github/workflows/token-budget.yml comment references new bound.
4. Update MIGRATION_v0.7.md and V0.7-EPIC.md with new measured range.

Branch: feat/v0.7-c-5-ci-gate-3500
Commit: feat(ci): lock full-profile schema cost ≤3500 tokens (#546)

Track D — Per-harness positioning + tests

D1: Cross-harness benchmark

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task D1 (#546).

Goal: reproducible benchmark across harnesses, before/after a representative
load_family call.

What to do:
1. Create benchmarks/v0.7-cortex-on-core.md.
2. For each harness {claude-code, openclaw, claude-desktop, codex-cli,
   grok-cli, gemini-cli}, simulate an MCP session:
   - Call tools/list (record boot_cost)
   - Call memory_load_family(family="graph") (record post_load_cost)
   - Compute net = boot + (post_load - boot) on deferred-registration
     harnesses; else = boot
3. Per-harness Pareto position table.
4. "Reproduction" subsection with exact ai-memory commands.

Branch: feat/v0.7-d-1-cross-harness-benchmark
Commit: bench(v0.7): cross-harness cortex-on-core benchmark (#546)

D2: Compatibility matrix on landing page

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task D2 (#546).

Goal: update landing-page cortex-on-core section with v0.7.0 numbers.

What to do:
1. Find the cortex-on-core section in docs/index.html (added 2026-05-05).
2. Update 3-column cost table: --profile full from 6,200 → ≤3,500.
3. Harness compatibility table: Claude Code + OpenClaw rows reference
   memory_smart_load(intent="…") as the natural-language loader.
4. Verify all link targets resolve.

Branch: feat/v0.7-d-2-compat-matrix-on-landing
Commit: docs(pages): update cortex-on-core section for v0.7.0 (#546)

D3: Install-time system-prompt snippet

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task D3 (#546).

Goal: ai-memory install prints a recommended system-prompt snippet to
stdout so any harness can paste it.

What to do:
1. In src/cli/install.rs, after canonical config write, print:
   "Recommended system-prompt addition for this harness:
    <<<
    You have access to ai-memory v0.7.0 with a 5-tool default surface.
    If you need a tool not currently loaded, call
    memory_smart_load(intent='<plain-language description>') to load
    the right family. You don't need to know the family taxonomy.
    >>>
    Drop this into your harness's system prompt (location varies; see
    docs/integrations/v0.7-system-prompt-snippet.md)."
2. Create docs/integrations/v0.7-system-prompt-snippet.md with per-harness
   placement instructions.
3. Unit tests: snippet printed for every install subcommand.

Branch: feat/v0.7-d-3-install-snippet
Commit: feat(cli): print system-prompt snippet on install (#546)

D4: Harness integration tests

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task D4 (#546).
Depends on B1, B4.

Goal: integration-test harness detection across all known harnesses.

What to do:
1. tests/harness_integration.rs (new).
2. For each harness in HARNESS_REGISTRY:
   - Open MCP session with clientInfo.name = harness name
   - Call memory_load_family(family="graph")
   - Assert: response.your_harness_supports_deferred_registration
     matches registry expectation
   - Assert: to_invoke text contains the right hint
3. "unknown harness" case: conservative-default behavior.
4. cargo test --test harness_integration in CI.

Branch: feat/v0.7-d-4-harness-integration-tests
Commit: test(integration): per-harness to_invoke text branching (#546)

Track E — Discovery Gate T0 cells

E1: Orchestrate T0 cells across LLMs

You are working on alphaonedev/ai-memory-discovery-gate toward v0.7.0
calibration coverage.

Goal: lift T0 from observation-mode to gate-grade orchestration. Run T0
prompts deterministically across at least 4 LLMs.

What to do:
1. scripts/run-t0-cells.sh parameterized by env var:
   T0_LLM=claude-opus-4.7 → anthropic-sdk via API
   T0_LLM=grok-4.3 → xAI api
   T0_LLM=gpt-4.5 → openai-sdk
   T0_LLM=gemini-pro-2 → google-generative-ai sdk
2. Prompt suite (2 prompts per cell):
   - "What memory tools do you have access to right now?"
   - "How would you know if you needed one not loaded?"
3. Pass criteria match docs/tiers/t0-calibration.md.
4. Output: docs/runs/<date>/cells/<llm>-<harness>-t0-calibration-core.{json,md,transcript.jsonl,wire.jsonl}
5. Update verdict.md.

Branch: feat/v0.7-e-1-t0-orchestrated
Commit: feat(t0): orchestrated cells across 4 LLMs (#1)

E2: Re-run T0 cells against v0.7.0 binary

You are working on alphaonedev/ai-memory-discovery-gate post-v0.7.0 ship.

Goal: validate substrate-side fixes closed the calibration gap.

What to do:
1. Pull v0.7.0 binary as DISCOVERY_GATE_BINARY.
2. Re-run T0 cells via E1 orchestrator across all 4 LLMs.
3. Update verdict.md with new pass rates.
4. Compare:
   - v0.6.4: ~50% calibration accuracy (observed)
   - v0.7.0 target: ≥95% across all 4 LLMs
5. If convergence target NOT met: file follow-up against
   alphaonedev/ai-memory-mcp identifying still-misaligned cell.

Branch: feat/v0.7-e-2-t0-postship
Commit: feat(t0): post-ship convergence verification (#1)

E3: Loader cells for T1-T3 tiers

You are working on alphaonedev/ai-memory-discovery-gate.

Goal: gate cells that explicitly test the v0.7.0 loaders.

What to do:
1. Add three cells:
   - t1-awareness-loaders.md — pass: agent names memory_load_family OR
     memory_smart_load when asked "how do I load a tool family?"
   - t2-reactive-loaders.md — pass: agent reads memory_capabilities
     description, proceeds with memory_load_family.
   - t3-proactive-smart-load.md — pass: given an intent, agent reaches
     for memory_smart_load before memory_capabilities.
2. Implement in scripts/run-llm-cells.sh.
3. Run against v0.7.0 binary across 4 LLMs.

Branch: feat/v0.7-e-3-loader-cells
Commit: feat(t1-t3): cells for new v0.7.0 loaders (#1)

Track G — Hook Pipeline (Bucket 0)

G1: hooks.toml schema

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G1.

Goal: define hooks.toml schema and config-loading pipeline. The substrate
every other Track G task depends on.

What to do:
1. src/hooks/config.rs:
   - struct HookConfig { event, command, priority, timeout_ms, mode,
     enabled, namespace }
   - enum HookEvent (20 variants from G2)
   - enum HookMode { Exec, Daemon }
   - HookConfig::load_from_file(path) -> Result<Vec<HookConfig>>
2. Default path: ~/.config/ai-memory/hooks.toml.
3. SIGHUP triggers reload; in-flight executions complete on old config.
4. Validation: priority i32, timeout_ms u32 ≤ 30000, namespace glob pattern.
5. Parse-time errors include line numbers (toml::Spanned).
6. Unit tests covering valid/invalid configs.

Branch: feat/v0.7-g-1-hooks-config
Commit: feat(hooks): hooks.toml config schema with hot reload

G2: 20 hook event types

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G2.
Depends on G1.

Goal: define 20 lifecycle event types and payloads.

What to do:
1. src/hooks/events.rs.
2. enum HookEvent — 20 variants:
   pre_store, post_store, pre_recall, post_recall, pre_search, post_search,
   pre_delete, post_delete, pre_promote, post_promote, pre_link, post_link,
   pre_consolidate, post_consolidate, pre_governance_decision,
   post_governance_decision, on_index_eviction, pre_archive,
   pre_transcript_store, post_transcript_store
3. Payload structs per event. Pre-events have writable deltas;
   post-events read-only.
4. Implement Serialize for JSON-stdio IPC.
5. Doc-comment each event with the source file:line of its invocation.
6. Round-trip test through JSON for each variant.

Branch: feat/v0.7-g-2-hook-events
Commit: feat(hooks): 20 lifecycle event types with payloads

G3: Hook executor (exec + daemon modes)

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G3.
Depends on G1, G2.

Goal: hook executor supporting both subprocess-per-fire and long-lived
daemon modes.

What to do:
1. src/hooks/executor.rs.
2. trait HookExecutor { fn fire(event, payload) -> Result<HookDecision>; }
3. ExecExecutor — tokio::process::Command per fire. JSON in stdin, JSON
   out stdout. Clean shutdown on stdin close.
4. DaemonExecutor — long-lived child. JSON-RPC framing (newline-delimited
   OR length-prefixed; pick + document). Reconnect on crash with exp
   backoff. Backpressure: oldest-first drop with warning if child can't
   keep up.
5. ExecutorRegistry maps HookConfig → instance.
6. Integration tests:
   - exec mode: 100 concurrent fires within timeout
   - daemon mode: 1000 fires through one daemon child within deadline;
     mid-stream crash triggers reconnect
7. ai-memory doctor --tokens --hooks reports executor metrics.
8. CRITICAL: post_recall + post_search default mode = daemon to preserve
   v0.6.3 50ms recall budget.

Branch: feat/v0.7-g-3-hook-executor
Commit: feat(hooks): subprocess executor with exec + daemon modes

G4: Decision types

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G4.
Depends on G2.

Goal: define HookDecision and serialization contract.

What to do:
1. src/hooks/decision.rs.
2. enum HookDecision { Allow, Modify(MemoryDelta), Deny{reason, code},
   AskUser{prompt, options, default} }.
3. Modify is pre-event-only (compile-time guard or runtime validation).
4. Wire JSON shape:
   {"action":"allow"} | {"action":"modify","delta":{...}}
   {"action":"deny","reason":"...","code":403}
   {"action":"ask_user","prompt":"...","options":[...],"default":"..."}
5. Strict Deserialize — unknown actions / missing fields → parse error
   surfaced as warning.
6. Unit tests for each variant + invalid payloads.

Branch: feat/v0.7-g-4-decision-types
Commit: feat(hooks): decision type contract

G5: Chain ordering + first-deny-wins

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G5.
Depends on G3, G4.

Goal: chain multiple hooks; resolve conflicts.

What to do:
1. src/hooks/chain.rs.
2. struct HookChain { hooks: Vec<HookConfig> } sorted by priority desc.
3. HookChain::fire(event, payload) -> ChainResult:
   - iterate hooks in priority order
   - call executor.fire() for each
   - if Deny: short-circuit
   - if Modify: update payload, continue
   - if AskUser: queue prompt, continue
   - if Allow: continue
4. ChainResult: Allow, ModifiedAllow(payload), Deny, AskUser(queue).
5. Unit tests:
   - 3 hooks, first denies → chain stops at first
   - 3 hooks, all allow with modifications → final payload composed
   - mid-chain crash → log warning, continue, treat as Allow
     (configurable per hook with fail_mode = "open" | "closed")
6. Integration with subscriptions.rs::dispatch_event — hooks fire BEFORE
   subscriptions for pre-, AFTER for post-.

Branch: feat/v0.7-g-5-chain-ordering
Commit: feat(hooks): chain ordering + first-deny-wins

G6: Hard timeouts per event class

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G6.
Depends on G2, G5.

Goal: enforce per-event-class deadlines so hooks can't break recall p95.

What to do:
1. src/hooks/timeouts.rs.
2. fn event_class(HookEvent) -> EventClass { Write, Read, Index, Transcript }.
3. CLASS_DEADLINES const map: 5000ms write, 2000ms read, 1000ms index,
   5000ms transcript.
4. At HookChain::fire, deadline = now + class_deadline. Each hook gets
   min(class_remaining, hook_timeout_ms).
5. Hook exceeds budget: kill subprocess (or close daemon stream), log
   warning, treat as fail-open Allow.
6. CI bench test: deliberately-slow chain on post_recall must NOT push
   recall p95 above 50ms.
7. Doctor reports timeout-violation counts.

Branch: feat/v0.7-g-6-event-class-timeouts
Commit: feat(hooks): per-event-class timeout enforcement

G7: Hot reload integration test

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G7.
Depends on G1.

Goal: verify hot reload end-to-end.

What to do:
1. tests/hooks_hot_reload.rs.
2. Start ai-memory in test mode with hooks.toml = "config A".
3. Fire memory_store; assert hook A's command invoked.
4. Replace hooks.toml on disk with "config B".
5. Send SIGHUP.
6. Wait for reload (poll for ≤500ms).
7. Fire memory_store; assert hook B's command invoked (not A).
8. In-flight hook from before SIGHUP must complete on old config.

Branch: feat/v0.7-g-7-hot-reload-test
Commit: test(hooks): hot reload integration test

G8: on_index_eviction event (G2 audit absorption)

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G8.

Goal: emit on_index_eviction hook event when ANN index evicts.

What to do:
1. Locate ANN eviction path in src/storage/embedding_index.rs.
2. After eviction, fire HookEvent::OnIndexEviction with payload
   { evicted_id, reason: "lru" | "capacity", remaining_size }.
3. Surface eviction count in ai-memory stats.
4. Unit test: trigger eviction, assert event fired + count incremented.

Branch: feat/v0.7-g-8-eviction-event
Commit: feat(hooks): on_index_eviction event + stats counter

G9: Reranker batching (G7 audit absorption)

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G9.

Goal: close G7 audit (Mutex throughput on reranker).

What to do:
1. Read src/reranker.rs; locate Mutex-protected single-request forward pass.
2. Replace with batching layer:
   - Incoming requests join a queue with oneshot::Sender for results.
   - Worker drains every 5ms (configurable) OR when N queued (default 8).
   - Single forward pass over the union of queued query+candidate sets.
   - Demux back to oneshot::Senders.
3. Bench: 100 concurrent rerank requests in 2× single-request time
   (not 100×).
4. CI bench test: batched throughput ≥10× single-request throughput at
   concurrency=20.

Branch: feat/v0.7-g-9-reranker-batching
Commit: perf(reranker): batched single-pass throughput

G10: pre_recall daemon-mode hook for query expansion (G10 audit absorption)

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G10.

Goal: memory_expand_query becomes pipeable through pre_recall daemon-mode
hook. Doesn't violate "zero tokens until recall" — server-side expansion.

What to do:
1. The pre_recall hook event is already in G2. In src/recall.rs, fire
   pre_recall before query construction.
2. The hook can return Modify(RecallQuery) with expanded terms.
3. Reference hook implementation in tools/query-expander/ (separate Rust
   binary):
   - Reads JSON RecallQuery from stdin
   - Expands query via Ollama (existing infrastructure)
   - Returns HookDecision::Modify(...)
4. Default off; opt-in per namespace via hooks.toml.
5. Bench: pre_recall daemon hook adds <10ms p95 to recall path.

Branch: feat/v0.7-g-10-pre-recall-expand
Commit: feat(hooks): pre_recall daemon-mode query expansion hook
You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task G11 (R3).
Depends on G1, G2, G3.

Goal: ship reference auto-link detector hook. Recovers commitment R3.

What to do:
1. Create tools/auto-link-detector/ (separate Rust binary).
2. Reads JSON HookEvent::PostStore from stdin (daemon mode):
   - Receives just-stored Memory.
   - Queries ai-memory neighbors via memory_recall (thin SDK client).
   - Embeds new memory + each neighbor; cosine similarity.
   - Heuristic: cosine > 0.85, same namespace → propose "related_to".
   - Heuristic: cosine 0.6-0.85, content negation aligned → propose
     "contradicts".
   - Each proposal: emit memory_link via HookDecision::Modify (chain
     persists transactionally with original store).
3. Metrics: proposals_emitted, links_persisted, conflicts_detected.
4. Default off; example hooks.toml entry in docs/hooks/auto-link.md.
5. tests/hooks_auto_link.rs runs ai-memory with hook enabled, stores
   contradiction pair, asserts link created.

Branch: feat/v0.7-g-11-auto-link-r3
Commit: feat(hooks): R3 auto-link detector reference hook

Track H — Ed25519 Attested Identity

H1: Per-agent keypair management

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task H1.

Goal: per-agent Ed25519 identity. CLI: generate / import / list.

What to do:
1. src/identity/keypair.rs:
   - struct AgentKeypair { agent_id, public, private (Option) }
   - fn generate(agent_id) -> AgentKeypair (ed25519-dalek)
   - fn save(keypair, dir) — mode 0600 priv, 0644 pub
   - fn load(agent_id, dir) -> AgentKeypair
   - fn list(dir) -> Vec<AgentKeypair> (priv NOT loaded)
2. src/cli/identity.rs subcommand:
   - ai-memory identity generate --agent-id <id>
   - ai-memory identity import --agent-id <id> --pub <path> --priv <path>
   - ai-memory identity list
   - ai-memory identity export-pub --agent-id <id>
3. Default key dir: ~/.config/ai-memory/keys/.
4. Default agent_id matches NHI-hardened default from CLI.
5. Hardware-backed storage (TPM/HSM) is OUT of OSS scope — doc-comment
   pointing at AgenticMem commercial layer.
6. Round-trip test: generate → save → load.
7. Permissions test (mode 0600/0644).

Branch: feat/v0.7-h-1-agent-keypair
Commit: feat(identity): per-agent Ed25519 keypair CLI
You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task H2.
Depends on H1.

Goal: every memory_link write signs with active agent's private key.
Fills the dead signature column shipped in v0.6.3.

What to do:
1. src/identity/sign.rs:
   - fn canonical_cbor(link: &Link) -> Vec<u8> (RFC 8949 §4.2.1)
   - fn sign(keypair, link) -> Vec<u8>
2. src/storage/links.rs::insert_link:
   - Look up active agent's keypair from AppState::active_keypair
   - Compute canonical CBOR
   - Sign; store 64-byte signature in existing signature column
   - Set attest_level = "self-signed"
3. No keypair → leave signature NULL, attest_level = "unsigned".
4. Unit test: insert link with generated keypair, read back, verify
   signature with public key.
5. Integration test against memory_link MCP tool — wire response includes
   attest_level.

Branch: feat/v0.7-h-2-link-signing
Commit: feat(identity): outbound Ed25519 signing on memory_links

H3: Inbound verification

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task H3.
Depends on H2.

Goal: when a link arrives from a peer, verify signature against the
public key associated with observed_by claim.

What to do:
1. src/identity/verify.rs:
   - fn verify(link: &Link, peer_keys: &PeerKeyRegistry) -> VerifyResult
2. PeerKeyRegistry: HashMap<agent_id, ed25519::PublicKey> populated from
   ~/.config/ai-memory/keys/peers/<agent_id>.pub.
3. src/federation/inbound.rs: on inbound link, call verify.
   - signature_verified=true → attest_level = "peer-attested"
   - signature_verified=false → reject, log warning, increment
     federation_rejected_count
   - observed_by has no known key → accept-and-flag as "unsigned"
4. Unit + integration tests.

Branch: feat/v0.7-h-3-inbound-verify
Commit: feat(identity): inbound Ed25519 verification with peer registry

H4: attest_level enum + memory_verify MCP tool

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task H4.
Depends on H2, H3.

Goal: attest_level enum + memory_verify MCP tool.

What to do:
1. src/storage/links.rs: enum AttestLevel { Unsigned, SelfSigned,
   PeerAttested }. Stored as TEXT in attest_level column.
2. New MCP tool memory_verify(link_id):
   - Returns { signature_verified, attest_level, signed_by, signed_at }
   - signature_verified: re-runs verification on demand
3. Tool is in core family (loaded by default — admins should always
   be able to verify).
4. Doc + tests.

Branch: feat/v0.7-h-4-attest-level
Commit: feat(identity): attest_level enum + memory_verify MCP tool

H5: signed_events audit table

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task H5.

Goal: append-only audit table for signed events.

What to do:
1. Schema migration v22 → v23 (or whatever next is):
   CREATE TABLE signed_events (
     id TEXT PRIMARY KEY,
     agent_id TEXT NOT NULL,
     event_type TEXT NOT NULL,
     payload_hash TEXT NOT NULL,
     signature BLOB NOT NULL,
     attest_level TEXT NOT NULL,
     timestamp TEXT NOT NULL
   );
2. No application-layer UPDATE/DELETE; INSERT-only.
3. Each memory_link write also INSERTs into signed_events.
4. memory_verify can cross-reference signed_events for an immutable
   audit trail.
5. cli::boot::MAX_SUPPORTED_SCHEMA bumped.
6. Tests: insert, attempted-update fails, audit chain verifiable.

Branch: feat/v0.7-h-5-signed-events-table
Commit: feat(identity): signed_events append-only audit table

H6: Verification end-to-end test

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task H6.
Closes G12 audit finding.

Goal: end-to-end test proving signature verification works.

What to do:
1. tests/identity_e2e.rs:
   - Generate keypair for agent A
   - Store a memory_link as agent A
   - Read the link back; assert signature column non-NULL
   - Call memory_verify(link_id) → assert signature_verified=true
   - Negative test: tamper with link content; verify returns false
2. CRITICAL: this test must pass for v0.7.0 to ship per ROADMAP §7.3
   exit criteria.

Branch: feat/v0.7-h-6-verify-e2e-test
Commit: test(identity): end-to-end signature verification (G12)

Track I — Sidechain Transcripts

I1: memory_transcripts table (zstd-3 BLOB)

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task I1.

Goal: schema for compressed transcript storage.

What to do:
1. Migration v23 → v24 in src/storage/migrations/.
2. CREATE TABLE memory_transcripts (
     id TEXT PRIMARY KEY,
     namespace TEXT NOT NULL,
     created_at TEXT NOT NULL,
     expires_at TEXT,
     compressed_size INTEGER NOT NULL,
     original_size INTEGER NOT NULL,
     zstd_level INTEGER NOT NULL DEFAULT 3,
     content_blob BLOB NOT NULL
   );
3. Index on (namespace, created_at).
4. cli::boot::MAX_SUPPORTED_SCHEMA bumped.
5. src/storage/transcripts.rs:
   - fn store(namespace, content, ttl) -> Transcript
   - fn fetch(id) -> Result<String>
   - fn purge_expired() -> usize
6. Test: round-trip + 5-10× compression on chat-shaped text.

Branch: feat/v0.7-i-1-transcripts-schema
Commit: feat(transcripts): schema with zstd-3 BLOB storage
You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task I2.
Depends on I1.

Goal: join table for memory→transcript links with span metadata.

What to do:
1. CREATE TABLE memory_transcript_links (
     memory_id TEXT NOT NULL,
     transcript_id TEXT NOT NULL,
     span_start INTEGER NOT NULL,
     span_end INTEGER NOT NULL,
     PRIMARY KEY (memory_id, transcript_id),
     FOREIGN KEY (memory_id) REFERENCES memories(id),
     FOREIGN KEY (transcript_id) REFERENCES memory_transcripts(id)
   );
2. Indexes on memory_id + transcript_id.
3. src/storage/transcripts.rs::link_memory_to_transcript(...).
4. Tests: many-to-many cardinality verified (1 transcript → N memories;
   N memories → 1 transcript).

Branch: feat/v0.7-i-2-transcript-links
Commit: feat(transcripts): memory_transcript_links join table

I3: Per-namespace TTL with archive→prune

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task I3.
Depends on I1.

Goal: namespace-level transcript TTL with archive-then-prune lifecycle.

What to do:
1. config.toml table [transcripts.ttl_days_per_namespace]:
   "*"            = 30
   "team/audit"   = 365
   "ephemeral/*"  = 1
2. Background sweeper task every hour:
   - For each transcript: if expires_at < now AND memories all expired
     → mark as archived
   - After 7 days archived → DELETE
3. Archive moves blob to a parallel archived_transcripts table
   (mirrors archived_memories pattern).
4. Tests: sweeper purges expired; non-expired untouched; archive→prune
   transition.

Branch: feat/v0.7-i-3-transcript-lifecycle
Commit: feat(transcripts): per-namespace TTL + archive→prune

I4: memory_replay MCP tool

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task I4.
Depends on I1, I2.

Goal: new MCP tool memory_replay(memory_id) reconstructs the transcript
chain.

What to do:
1. src/mcp.rs: register memory_replay tool. Family: meta.
2. Handler:
   - SELECT transcript_id, span_start, span_end FROM
     memory_transcript_links WHERE memory_id = ? ORDER BY transcript_id
   - For each transcript_id: fetch + decompress
   - Return list of {transcript_id, span_text, span_start, span_end}
3. Response in TOON-compact + JSON formats.
4. Doc + integration test against memory_replay MCP wire path.

Branch: feat/v0.7-i-4-memory-replay
Commit: feat(mcp): memory_replay tool for transcript reconstruction

I5: R5 — Auto-extraction substrate hook

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task I5 (R5).
Depends on I1-I4, G3.

Goal: reference pre_store hook that extracts memories from a stored
transcript. Recovers commitment R5.

What to do:
1. tools/transcript-extractor/ (separate Rust binary).
2. Reads JSON HookEvent::PreTranscriptStore from stdin (daemon mode).
3. For each transcript:
   - Run LLM (Ollama) extraction prompt to identify candidate memories.
   - For each candidate → emit HookDecision::Modify with new MemoryDelta
     entries to be stored alongside.
   - Each extracted memory gets a memory_transcript_link to the transcript
     with span_start/span_end pointing at the source spans.
4. Default off; opt-in per namespace.
5. Bench: extraction adds <500ms p95 to transcript-store path (background-
   acceptable).
6. Test corpus: 10 sample transcripts with known extractable memories;
   assert extraction recall ≥80%.

Branch: feat/v0.7-i-5-pre-store-transcript-hook
Commit: feat(hooks): R5 transcript-to-memory auto-extraction reference

Track J — Apache AGE Acceleration

J1: AGE detection in Postgres SAL

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task J1.

Goal: detect AGE extension at Postgres SAL init.

What to do:
1. src/sal/postgres.rs initialization path:
   - SELECT extname FROM pg_extension WHERE extname='age';
   - If present: kg_backend = "age"
   - Else: kg_backend = "cte"
2. struct PostgresSAL gains kg_backend field.
3. Log the choice at startup: "Postgres KG backend: AGE | CTE".
4. ai-memory doctor reports kg_backend.
5. Test against both AGE-enabled and AGE-disabled test DBs.

Branch: feat/v0.7-j-1-age-detection
Commit: feat(sal): detect Apache AGE extension at Postgres init

J2: Cypher implementation of memory_kg_query

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task J2.
Depends on J1.

Goal: when kg_backend == "age", route memory_kg_query through Cypher.

What to do:
1. src/kg/query.rs: trait KgQueryBackend { fn query(...) -> Result<Vec<Path>>; }
2. CypherBackend impl: SELECT * FROM cypher('memory_graph',
   'MATCH (a)-[:RELATION*1..N]->(b) WHERE a.id = $start RETURN path');
   parameter binding via cypher's $vars syntax.
3. CteBackend (existing) impl: keep current recursive CTE.
4. AppState: select backend at SAL init; route memory_kg_query accordingly.
5. Tests against both backends asserting result equivalence (J5
   formalizes this).

Branch: feat/v0.7-j-2-kg-query-cypher
Commit: feat(kg): Cypher implementation of memory_kg_query for AGE

J3: Cypher implementation of memory_kg_timeline

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task J3.
Depends on J2.

Goal: Cypher implementation of memory_kg_timeline.

What to do:
1. Same backend trait pattern as J2.
2. Cypher query: MATCH (n)-[:VALID_AT*]->(...) WHERE n.id = $start
   AND $when BETWEEN valid_from AND valid_until.
3. Edge cases: open-ended valid_until, valid_from = NULL.
4. Tests covering the main timeline shapes.

Branch: feat/v0.7-j-3-kg-timeline-cypher
Commit: feat(kg): Cypher implementation of memory_kg_timeline

J4: Cypher implementation of memory_kg_invalidate

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task J4.
Depends on J2, J3.

Goal: Cypher implementation of memory_kg_invalidate + G14 audit absorption.

What to do:
1. Cypher: MATCH (n)-[r]->(m) WHERE n.id = $id SET r.invalidated_at = now,
   r.invalidated_by = $agent_id; create audit edge:
   MATCH (n) WHERE n.id = $id CREATE (n)-[:INVALIDATED_BY {at: now,
   by: $agent_id, reason: $reason}]->(audit_node).
2. G14 — emit on_invalidate hook event with the audit edge.
3. Tests covering invalidation chains + audit edge presence.

Branch: feat/v0.7-j-4-kg-invalidate-cypher
Commit: feat(kg): Cypher kg_invalidate + G14 audit edge

J5: Dual-path tests

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task J5.
Depends on J2, J3, J4.

Goal: AGE and CTE produce identical results.

What to do:
1. tests/kg_dual_path.rs.
2. Fixture: 200 memories + 800 links covering depth-1..5 + cycles.
3. For each KG op {kg_query, kg_timeline, kg_invalidate}:
   - Run against AGE-Postgres test DB
   - Run against CTE-SQLite test DB (same fixture)
   - Assert result sets equivalent under set-equivalence
4. Depths: 1, 2, 3, 5. Cyclic + non-cyclic.
5. Run only when AI_MEMORY_TEST_AGE_URL env var set; CI matrix
   configures.

Branch: feat/v0.7-j-5-dual-path-tests
Commit: test(kg): AGE vs CTE dual-path equivalence

J6: PERFORMANCE.md updated with separate budgets

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task J6.
Depends on J2-J5.

Goal: separate p95/p99 budgets for AGE-mode and CTE-mode.

What to do:
1. Run bench against fixture corpus across depths 1, 3, 5 for both
   backends.
2. Update PERFORMANCE.md:
   - kg_query (CTE-SQLite, depth=1): p95 < 50ms, p99 < 100ms
   - kg_query (CTE-SQLite, depth=5): p95 < 250ms, p99 < 500ms
   - kg_query (AGE-Postgres, depth=1): p95 < 30ms, p99 < 60ms
   - kg_query (AGE-Postgres, depth=5): p95 < 150ms, p99 < 300ms
   (numbers indicative; replace with actual measurements)
3. Note the target methodology: M2 16GB reference machine.

Branch: feat/v0.7-j-6-perf-budgets
Commit: perf(kg): separate AGE/CTE p95/p99 budgets in PERFORMANCE.md

J7: R2 — memory_find_paths

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task J7 (R2).
Depends on J1.

Goal: new MCP tool memory_find_paths. Recovers commitment R2.

What to do:
1. src/mcp.rs: register memory_find_paths in graph family.
   inputSchema: { source: id, target: id, max_depth: int default 5 }.
2. Backend dispatch:
   - AGE: MATCH p=(a)-[:RELATION*1..max_depth]->(b)
     WHERE a.id=$source AND b.id=$target RETURN p
   - CTE: recursive CTE walking memory_links
3. Response: list of paths, each as ordered [src,relation,dst] triples.
4. Tests + bench numbers added to J6 PERFORMANCE.md.

Branch: feat/v0.7-j-7-r2-find-paths
Commit: feat(mcp): R2 memory_find_paths MCP tool

J8: AGE bench gate (≥30% faster than CTE at depth=5)

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task J8.
Depends on J5, J6.

Goal: CI bench gate justifies AGE complexity. If AGE isn't ≥30% faster
than CTE at depth=5, drop AGE.

What to do:
1. .github/workflows/bench.yml: add age-vs-cte-bench job.
2. Job: run depth=5 bench against both backends; assert
   p95(AGE) ≤ 0.7 × p95(CTE).
3. If gate fails: red CI; v0.7.0 release blocked until either AGE is
   faster or AGE is removed.
4. Document the decision in docs/v0.7/age-justification.md.

Branch: feat/v0.7-j-8-age-bench-gate
Commit: ci(bench): AGE-mode ≥30% faster than CTE at depth=5 gate

Track K — A2A + Permissions + G1 Inheritance

K1 (G1 cutline): Namespace inheritance enforcement

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K1.
THIS IS THE CUTLINE FIX. Mandatory per ROADMAP §7.3 even if everything
else slips.

Goal: namespace inheritance enforcement. Today: parent "Approve" policy
doesn't block a child write because resolve_governance_policy only
consults the leaf. Fix: walk the chain.

What to do:
1. src/governance/policy.rs::resolve_governance_policy. Replace leaf-only
   with:
   for ancestor in build_namespace_chain(ns).iter().rev() {
       if let Some(policy) = lookup_policy(ancestor) {
           if policy.inherit || ancestor == ns {
               return Some(policy);
           }
       }
   }
   None
2. struct GovernancePolicy: add inherit: bool field, default true.
3. Migration: existing rows backfill inherit=true.
4. Cutline test in tests/governance_inheritance.rs:
   - Parent "team" has GovernancePolicy(approve_required=true)
   - Child "team/alice" has no policy
   - Write to "team/alice" → MUST require approval
   This test is FATAL: failure blocks the release.
5. docs/governance.html updated with worked inheritance example.
6. Audit log records which ancestor's policy fired.

Branch: feat/v0.7-k-1-g1-inheritance-fix
Commit: fix(governance): G1 namespace inheritance enforcement (cutline)

CRITICAL: This MUST ship in v0.7.0.

K2: pending_actions timeout sweeper

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K2.

Goal: pending_actions table timeout sweeper. Closes v0.6.3.1 honest
disclosure that default_timeout_seconds was advertised but unused.

What to do:
1. Background tokio task fires every 60 seconds.
2. SELECT id FROM pending_actions WHERE created_at + default_timeout_seconds
   < now AND status = 'pending';
3. UPDATE pending_actions SET status='expired', expired_at=now WHERE
   id IN (...).
4. Emit on_governance_decision event for each expiry.
5. Update src/governance/lifecycle.rs and integration test.

Branch: feat/v0.7-k-2-pending-actions-sweeper
Commit: feat(governance): pending_actions timeout sweeper

K3: permissions.mode consulted by gate

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K3.

Goal: gate consults permissions.mode. Closes v0.6.3.1 honest disclosure
that permissions were advisory-only.

What to do:
1. src/governance/gate.rs: read permissions.mode at decision time.
   - "enforce" → block on policy violation
   - "advisory" → log, allow
   - "off" → allow without checking
2. Default mode = "enforce" for new installs; "advisory" for upgrades
   from v0.6.x (preserves backward compat).
3. doctor reports active mode + decision-counts per mode.
4. Tests covering all three modes.

Branch: feat/v0.7-k-3-permissions-mode-honest
Commit: feat(governance): permissions.mode actually enforced

K4: Approval-event routing through subscription system

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K4.

Goal: pending approval events flow through existing subscription system.
Closes v0.6.3.1 honest disclosure that approval.subscribers was a placeholder.

What to do:
1. When pending_action requires approval, dispatch_event("approval.requested",
   payload).
2. Approval API HTTP+SSE handler in K10 consumes these events.
3. Backward compat: existing approval.subscribers list remains; events
   route to subscribers via existing subscription infrastructure.
4. Integration test: subscribe to approval.requested, store with policy
   that requires approval, assert webhook fired.

Branch: feat/v0.7-k-4-approval-event-routing
Commit: feat(governance): approval events through subscription system

K5: rule_summary populated

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K5.

Goal: capabilities v3 rule_summary returns real data.

What to do:
1. src/mcp.rs::build_capabilities_v3: populate rule_summary as ordered
   list of active governance rules with one-line summaries.
2. Format: [{rule_id, namespace, action, summary}, ...]
3. Closes v0.6.3.1 honest-Capabilities-v2 disclosure.
4. Doc + tests.

Branch: feat/v0.7-k-5-rule-summary
Commit: feat(mcp): populate rule_summary in capabilities v3

K6: A2A correlation IDs + ACK retry + replay protection

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K6.

Goal: A2A maturity. Add correlation IDs, ACK semantics, retry, replay
protection.

What to do:
1. src/federation/a2a.rs:
   - struct A2AMessage { correlation_id: Uuid, sender, recipients,
     payload, expires_at }
   - struct A2AAck { correlation_id, recipient, accepted: bool, reason }
2. Sender:
   - UUIDv4 per outbound message
   - Wait for ACK with TTL (default 30s)
   - Retry up to 3× on no-ACK with exp backoff
3. Receiver:
   - Bounded LRU of seen correlation IDs (size 10_000)
   - Duplicate → ACK accepted=false, reason="duplicate"
   - Expired → drop with warning
4. Tests: 100 messages all ACK'd; replay rejected; partition triggers
   retry; expired dropped.
5. PERFORMANCE.md gets A2A throughput numbers across 3/5/10-node mesh.

Branch: feat/v0.7-k-6-a2a-maturity
Commit: feat(federation): A2A correlation IDs + ACK retry + replay

K7: Subscription reliability

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K7.

Goal: subscription dispatcher gains DLQ, replay-from-cursor, mandatory HMAC.

What to do:
1. src/subscriptions.rs::dispatch_event:
   - 5xx response → exponential backoff retry (max 3)
   - Final failure → INSERT into subscription_dlq
2. New table subscription_dlq(id, subscription_id, event_payload,
   last_error, retry_count, queued_at).
3. CLI: ai-memory subscriptions replay --from-cursor <id> — re-fires
   events from a checkpoint.
4. HMAC signing on every dispatched event mandatory (already opt-in;
   flip default).
5. Tests covering retry, DLQ insert, replay-from-cursor.

Branch: feat/v0.7-k-7-subscription-reliability
Commit: feat(subscriptions): DLQ + replay-from-cursor + mandatory HMAC

K8: Per-agent rate limits + storage caps

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K8.

Goal: per-agent quotas enforced at MCP entry.

What to do:
1. config.toml [quotas.per_agent] table:
   "ai:claude-code@*"  = { rps_limit = 100, storage_mb_cap = 1024 }
   "*" = { rps_limit = 10, storage_mb_cap = 100 }
2. src/quota/rate_limit.rs: token-bucket algorithm per agent_id.
3. src/quota/storage.rs: cumulative storage tracker; enforce on insert.
4. RPS overage → 429 Too Many Requests.
5. Storage cap → 507 Insufficient Storage.
6. ai-memory doctor reports per-agent quota usage.
7. Tests covering both kinds of limits.

Branch: feat/v0.7-k-8-per-agent-quotas
Commit: feat(quota): per-agent rate limits + storage caps

K9: Permission system overhaul

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K9.

Goal: refactor governance into rules + modes + hooks → decision.
Deny-first; ask-by-default.

What to do:
1. src/permissions/mod.rs (new module):
   - struct Rule { id, namespace_pattern, action, decision_template }
   - enum Mode { Enforce, Advisory, Off }
   - fn decide(rule: &Rule, mode: Mode, context, hooks: &HookChain) -> Decision
2. Decision flow:
   - Match rule by namespace_pattern + action
   - Apply mode to determine enforcement
   - Run pre_governance_decision hooks (Track G); they can override
   - If no rule matches: deny-first default → AskUser by default
3. Schema migration: governance table maps to permissions table.
4. Tests: deny-first behavior; ask-by-default; hook override.

Branch: feat/v0.7-k-9-permission-system
Commit: feat(permissions): rules + modes + hooks decision system

K10: Approval API (HTTP + SSE + MCP)

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K10.
Depends on K4, K9.

Goal: three-surface approval API. HMAC mandatory. remember=forever
progressive trust.

What to do:
1. HTTP:
   - GET /api/v1/approvals/pending → list
   - POST /api/v1/approvals/:id/decide { decision, remember? } → record
2. SSE:
   - GET /api/v1/approvals/stream → server-sent events; live updates
3. MCP:
   - memory_approval_pending → list pending
   - memory_approval_decide → record decision
4. HMAC signing on POST/decide is mandatory (no opt-out).
5. remember=forever: subsequent identical requests auto-approve via a
   permissions rule auto-created from this decision.
6. Tests covering all three surfaces + HMAC mandatory.

Branch: feat/v0.7-k-10-approval-api
Commit: feat(permissions): three-surface approval API (HTTP + SSE + MCP)

K11: governance migrate-to-permissions CLI

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task K11.
Depends on K9.

Goal: idempotent one-shot migration tool from old governance schema
to new permissions schema.

What to do:
1. ai-memory governance migrate-to-permissions:
   - Default --dry-run: prints planned changes
   - --apply: commits
2. Maps each governance row to a permissions Rule.
3. Idempotent: running twice produces no change.
4. Audit log entry per migrated rule.
5. Test: migrate fixture DB, assert all rows mapped, re-run is no-op.

Branch: feat/v0.7-k-11-permissions-migration
Commit: feat(cli): governance migrate-to-permissions CLI

Track F — Docs + release

F1: docs/MIGRATION_v0.7.md

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task F1.

Goal: comprehensive migration guide for users coming from v0.6.4.

Cover:
1. Capabilities v3 schema additions (summary, to_describe_to_user,
   callable_now, agent_permitted_families).
2. New tools (memory_load_family, memory_smart_load, memory_find_paths,
   memory_replay, memory_verify, memory_approval_*).
3. Hook pipeline (opt-in; no default change).
4. Ed25519 attestation (opt-in via ai-memory identity generate).
5. Sidechain transcripts (opt-in per namespace).
6. Apache AGE (opt-in via Postgres extension).
7. G1 inheritance fix — BEHAVIOR CHANGE: child writes may now require
   approval where they didn't before. Document mitigation: set
   inherit=false on child policies if backward compat needed.
8. Permissions migration via ai-memory governance migrate-to-permissions.

Style: match docs/MIGRATION_v0.6.4.md exactly.

Branch: feat/v0.7-f-1-migration-guide
Commit: docs: v0.7.0 migration guide

F2: docs/whats-new-v07.html

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task F2.

Goal: produce docs/whats-new-v07.html (Pages-side).

Style + structure: copy docs/whats-new-v064.html exactly; rewrite for
v0.7.0.

Three-audience pattern:
- 👤 If you USE AI: cortex experience now reliably arrives across LLMs.
  Your memory is now cryptographically attested.
- 🛠️ If you BUILD with AI: hook pipeline + Ed25519 + sidechain transcripts
  + Apache AGE + new permission system + memory_load_family +
  memory_smart_load + ~3,500-token full profile.
- 🏢 If you DECIDE AI infrastructure: signed audit chains. T4-T5
  multi-agent positioning closer to ready.

Branch: feat/v0.7-f-2-whats-new-page
Commit: docs(pages): whats-new-v07

F3: Top-nav + landing-page badges + v0.6.4→v0.7.0 references

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task F3.

Goal: bump landing-page version references to v0.7.0.

What to do:
1. docs/index.html topnav: v0.6.4 link → v0.7.0 link.
2. Hero badges: test count + coverage updated.
3. "v0.6.4 cert" badge → "v0.7.0 cert".
4. Meta tags v0.6.4 → v0.7.0.
5. Footer: v0.6.4 → v0.7.0 with USPTO Serial No. preserved.

Branch: feat/v0.7-f-3-landing-version-bump
Commit: docs(pages): bump landing-page references to v0.7.0

F4: README + ADMIN_GUIDE updates

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task F4.

Goal: README + ADMIN_GUIDE reflect v0.7.0 scope.

What to do:
1. README.md:
   - Badge block: tests + coverage + cert + version
   - Body: new tool names; new --profile semantics; hook section
     reference; identity section reference
2. docs/ADMIN_GUIDE.md:
   - New sections: Hooks (track G), Identity & Attestation (track H),
     Sidechain Transcripts (track I), Apache AGE (track J), Permissions
     System (track K)
   - Per-section: config schema, operational concerns, troubleshooting
3. Cross-link to docs/MIGRATION_v0.7.md throughout.

Branch: feat/v0.7-f-4-readme-admin-guide
Commit: docs: README + ADMIN_GUIDE for v0.7.0

F5: CHANGELOG, version bumps, tag, CI release

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task F5.

Goal: ship the release.

What to do:
1. CHANGELOG.md: comprehensive v0.7.0 entry covering Tracks A-K.
2. Version bumps:
   - Cargo.toml: 0.6.4 → 0.7.0
   - sdk/python/pyproject.toml: 0.6.4 → 0.7.0
   - sdk/typescript/package.json: 0.6.4 → 0.7.0
3. git tag v0.7.0
4. gh release create v0.7.0 with comprehensive notes (model after v0.6.4).
5. publish-sdks.yml workflow auto-fires on tag push (npm + PyPI via OIDC).
6. SHA256SUMS asset uploaded.

Branch: feat/v0.7-f-5-release-prep
Commit: chore(release): v0.7.0 prep

F6: Design RFC docs/v0.7/rfc-attested-cortex.md

You are working on alphaonedev/ai-memory-mcp toward v0.7.0 — task F6.

Goal: design RFC documenting v0.7.0 architectural decisions.

Cover:
1. Why Ed25519 over X25519+ChaCha20 (X25519 is v0.8 e2e encryption;
   v0.7.0 attestation is signing-only).
2. Why subprocess-stdio + daemon-mode for hooks (vs. dynamic library
   plugins or eBPF or other).
3. Why AGE behind a feature flag (not hard dependency).
4. Why permission system replaces governance instead of augmenting
   (and the migration story).
5. Why sidechain transcripts are opt-in per namespace (storage cost vs
   audit value tradeoff).
6. Track-by-track threat model: what each track makes harder to attack;
   what stays out of scope.

Branch: feat/v0.7-f-6-design-rfc
Commit: docs(rfc): v0.7.0 design RFC — attested-cortex

End of prompts

All 69 tasks (Tracks A-K + F) have a paste-ready starter prompt above. Each is self-contained — an agent can take any one and begin work immediately.

Companion documents: