diff --git a/.changeset/calm-rivers-bloom.md b/.changeset/calm-rivers-bloom.md
new file mode 100644
index 000000000..43edc9117
--- /dev/null
+++ b/.changeset/calm-rivers-bloom.md
@@ -0,0 +1,5 @@
+---
+type: Added
+pr: 1201
+---
+**MemPalace memory capability (opt-in)** — adds cross-session/cross-project recall and verbatim+temporal-KG capture at GSD loop boundaries via the MemPalace MCP server and CLI; disabled by default, skip-on-error. (#1201)
diff --git a/CONTEXT.md b/CONTEXT.md
index 6b4bdf2da..bad5d354e 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -181,6 +181,24 @@ A Capability whose integration shape brings its own external process, service, o
`RULESET.CAPABILITY.step-additive-gate-blocks=a `step` hook is purely additive (invoke skill + produce artifacts, NEVER halts the host); host-blocking preconditions are `gate`s (blocking:true, onError:halt); runtime/mode context (auto/chain vs manual) self-gates IN THE SKILL, not via `when` (config-only). §5.6 = plan:pre step (ui-phase; skill self-gates on frontend+pipeline, auto-fires only in pipelines) + a NEW plan:pre gate (frontend-and-no-UI-SPEC → halt, when:workflow.ui_safety_gate); the loop.render-hooks dispatch template handles steps AND gates. Resolves #1022.`
+### Wing
+A MemPalace organizational unit corresponding to one project or repository. GSD derives the wing name from `project_code` or the project directory when `mempalace.wing` is unset. A wing contains Rooms. Cross-project Tunnels connect rooms across wings. MemPalace vocabulary — see Connected Capability, MemPalace memory capability (issue #956).
+
+### Room
+A named bucket inside a Wing that groups drawers by semantic kind. GSD maps its phase artifacts to five fixed rooms: `decisions` (CONTEXT.md), `planning` (PLAN.md), `milestones` (SUMMARY.md/UAT.md excerpts), `problems` (confirmed bug→fix pairs), and `learnings` (extract-learnings output). MemPalace vocabulary — see Wing.
+
+### Drawer
+A verbatim-content unit stored inside a Room. GSD files phase artifacts as drawers using `mempalace_add_drawer`; duplicate-check via `mempalace_check_duplicate` makes capture idempotent. GSD stores verbatim text (not AAAK summaries) to preserve recall fidelity. MemPalace vocabulary — see Room.
+
+### Tunnel
+A cross-Wing knowledge connection created by `mempalace_create_tunnel`. GSD proposes tunnels at `ship:post` when `mempalace.cross_project_tunnels: true`, linking rooms in the current wing to semantically related rooms in other project wings. MemPalace vocabulary — see Wing.
+
+### Diary
+A per-agent narrative entry written by `mempalace_diary_write`. GSD's `gsd-mempalace-curator` writes a diary entry at `ship:post` when `mempalace.diary_journal: true`, recording a session summary scoped to the project and agent role. MemPalace vocabulary — see Connected Capability.
+
+### memory_mode
+The `mempalace.memory_mode` config key controlling how tightly MemPalace couples to GSD's native memory. Three declared values: `augment` (default — **implemented**; palace is an additional write-mostly recall layer; lowest coupling), `kg_backend` (**declared; routing seam not yet implemented** — intended to route graphify KG queries through MemPalace's temporal graph; selecting today behaves as `augment`), `replace` (**declared; not yet functional** — intended to make the palace the durable store; selecting today behaves as `augment`). Only `augment` has effect in the current release; `kg_backend` and `replace` are forward-declared for a future release. Read at hook-render time; switching is a config change, not a reinstall. See MemPalace Settings in `docs/CONFIGURATION.md`.
+
### Runtime Hooks Surface Module
Standalone hook-surface writer module extracted from `bin/install.js` as ADR-857 phase 5f-1 (behavior-preserving relocation, no logic change). Owns: Cline rules-body/agents-md/pre-tool-use hook generation (`buildClineRulesBody`, `buildClineAgentsMdBody`, `buildClinePreToolUseHook`, `mergeGsdAgentsMd`, `writeClineArtifacts`); Cursor `hooks.json` lifecycle (`buildCursorHookEntry`, `isManagedCursorHookEntry`, `reconcileCursorHooksJson`, `writeCursorHooksJson`, `removeCursorHooksJson`); Copilot session-hook config (`buildCopilotHookConfig`, `writeCopilotHookConfig`); Codex hook-block and event management (`buildCodexHookBlock`, `rewriteLegacyCodexHookBlock`, `reconcileCodexHooksJsonEvent`, `reconcileCodexHooksJsonSessionStart`, `ensureCodexHooksJsonSessionStart`, `ensureCodexHooksJsonEvent`, `removeCodexHooksJsonEvent`, `removeCodexHooksJsonSessionStart`, `buildCodexHookWindowsShimIR`); and shared hook command helpers (`buildHookCommand`, `rewriteLegacyManagedNodeHookCommands`, `normalizeNodePath`, `resolveNodeRunner`). `bin/install.js` delegates to this module via thin wrappers and re-exports its functions unchanged so existing tests require no modification. Source: `src/runtime-hooks-surface.cts`. Built output: `gsd-core/bin/lib/runtime-hooks-surface.cjs`.
diff --git a/agents/gsd-mempalace-curator.md b/agents/gsd-mempalace-curator.md
new file mode 100644
index 000000000..3bd7c1fba
--- /dev/null
+++ b/agents/gsd-mempalace-curator.md
@@ -0,0 +1,47 @@
+---
+name: gsd-mempalace-curator
+description: Ship-time MemPalace curation — writes the session diary, proposes/creates cross-project tunnels, mirrors extract-learnings into the temporal KG, and runs wing-scoped drawer pruning. Spawned at ship:post by the mempalace capability.
+tools: Read, Bash, Grep, Glob
+model: sonnet
+color: cyan
+---
+
+
+You are the MemPalace curator. You run once per phase at `ship:post`, after verification has passed, to consolidate the phase's memory into the palace. Everything you do is best-effort and wing-scoped: a MemPalace failure must never fail the ship step (`onError: skip`), and you must never touch drawers outside this project's wing.
+
+
+
+- `.planning/config.json` — read `mempalace.enabled`, `mempalace.memory_mode`, `mempalace.wing`, `mempalace.diary_journal`, `mempalace.cross_project_tunnels`, `mempalace.mirror_kg`, `project_code`.
+- The completed phase artifacts: `UAT.md`, `SUMMARY.md`, and any `extract-learnings` output.
+
+
+## Gate
+
+If `mempalace.enabled !== true`, do nothing and report `MemPalace disabled — curation skipped`. This is the hard gate; respect it before any other work.
+
+## Wing / mode / transport
+
+- **Wing:** `mempalace.wing` if non-empty, else `project_code`, else the repo directory name. Every call you make is scoped to this one wing.
+- **Mode:** only `augment` is currently wired — KG writes are an additive mirror of `.planning/graphs/`. `kg_backend`/`replace` are forward-declared and behave as `augment` today.
+- **Transport:** prefer the `mempalace_*` MCP tools interactively; fall back to the `mempalace` CLI in headless/cron runs. If neither is reachable, report unavailability and stop — do not error.
+
+## Tasks (each independently best-effort)
+
+1. **Diary entry** (when `mempalace.diary_journal` is true). Write one concise per-agent diary entry summarising the phase outcome: `mempalace_diary_write(agent_name=/, entry=, topic="phase-ship", wing=)` (CLI: `mempalace hook run` / the diary CLI). Namespace `agent_name` by repo+role so diaries don't collide across projects. **Idempotency:** before writing, `mempalace_diary_read` (or list) for an existing entry keyed by `(wing, agent_name, topic, phase-id)`; if one exists for this phase, update it in place rather than appending a second.
+
+2. **extract-learnings → KG mirror** (when `mempalace.mirror_kg` is true). For each decision/lesson/pattern/surprise from the phase's learnings, add a typed KG triple with provenance (`source_file`, `source_drawer_id`) and `valid_from` = the phase date. **Idempotency:** the triple `(subject, predicate, object)` is the natural key — `mempalace_kg_query` for it first and skip `mempalace_kg_add` if it already exists with the same `valid_from`, so reruns don't fork duplicate facts. When a prior decision was superseded this phase, call `mempalace_kg_invalidate` to set its `valid_to` rather than deleting it.
+
+3. **Cross-project tunnels** (when `mempalace.cross_project_tunnels` is true). Use `mempalace_find_tunnels` to surface related wings, then `mempalace_create_tunnel(label=…)` only for connections you (or the user) can justify. **Idempotency:** check the `find_tunnels` result first and skip creation if a tunnel with that `(source-wing, target-wing, label)` already exists. Do not mass-create tunnels.
+
+4. **Wing-scoped prune** (optional). Run `mempalace sync --wing --apply` to prune drawers whose source artifacts were archived/deleted. **Never** run a global sync/prune; always pass `--wing`.
+
+## Hard rules
+
+- Best-effort only: catch and report every MemPalace failure; never propagate an error that would fail `ship:post`.
+- Wing-scoped only: never read, write, or prune outside this project's wing.
+- Verbatim preservation: invalidate superseded facts (set `valid_to`); do not destroy history.
+- Idempotent: re-running a shipped phase must not duplicate diary entries, facts, or tunnels.
+
+## Report
+
+Emit a short summary of what was curated: diary (yes/no), KG facts mirrored (count), tunnels proposed/created (count), drawers pruned (count) — or `MemPalace unavailable — curation skipped`.
diff --git a/capabilities/mempalace/capability.json b/capabilities/mempalace/capability.json
new file mode 100644
index 000000000..534eeecec
--- /dev/null
+++ b/capabilities/mempalace/capability.json
@@ -0,0 +1,128 @@
+{
+ "id": "mempalace",
+ "role": "feature",
+ "title": "MemPalace memory",
+ "description": "Cross-session, cross-project memory: deliberate recall before discuss/plan and verbatim capture + temporal-KG sync at phase boundaries, via the MemPalace MCP server and CLI.",
+ "tier": "full",
+ "requires": [],
+ "runtimeCompat": { "supported": ["*"], "unsupported": [] },
+ "skills": ["mempalace-recall", "mempalace-capture"],
+ "agents": ["gsd-mempalace-curator"],
+ "hooks": [],
+ "config": {
+ "mempalace.enabled": {
+ "type": "boolean",
+ "default": false,
+ "description": "Master toggle for the MemPalace memory capability."
+ },
+ "mempalace.memory_mode": {
+ "type": "enum",
+ "values": ["augment", "kg_backend", "replace"],
+ "default": "augment",
+ "description": "How MemPalace relates to GSD native memory. Only 'augment' (additive) is implemented today; 'kg_backend' and 'replace' are forward-declared (routing seam not yet built) and currently behave as 'augment'."
+ },
+ "mempalace.wing": {
+ "type": "string",
+ "default": "",
+ "description": "Palace wing name; empty derives from project_code / project dir."
+ },
+ "mempalace.recall_on_discuss": {
+ "type": "boolean",
+ "default": true,
+ "description": "Inject wake-up + search recall at discuss:pre."
+ },
+ "mempalace.recall_on_plan": {
+ "type": "boolean",
+ "default": true,
+ "description": "Produce MEMORY-RECALL.md at plan:pre."
+ },
+ "mempalace.capture_artifacts": {
+ "type": "boolean",
+ "default": true,
+ "description": "File CONTEXT/PLAN/SUMMARY and learnings into the palace at phase boundaries."
+ },
+ "mempalace.mirror_kg": {
+ "type": "boolean",
+ "default": true,
+ "description": "Mirror decisions/learnings into MemPalace's temporal knowledge graph."
+ },
+ "mempalace.cross_project_tunnels": {
+ "type": "boolean",
+ "default": false,
+ "description": "Propose/create cross-wing tunnels at ship:post."
+ },
+ "mempalace.diary_journal": {
+ "type": "boolean",
+ "default": true,
+ "description": "Write a per-agent diary entry at ship:post."
+ },
+ "mempalace.auto_capture_hooks": {
+ "type": "boolean",
+ "default": false,
+ "description": "Reserved / not yet implemented: will install MemPalace's native stop/precompact Claude Code hooks for passive mid-session capture (the capability's hooks array is currently empty)."
+ }
+ },
+ "steps": [
+ {
+ "point": "discuss:post",
+ "ref": { "skill": "mempalace-capture" },
+ "produces": [],
+ "consumes": ["CONTEXT.md"],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
+ {
+ "point": "plan:pre",
+ "ref": { "skill": "mempalace-recall" },
+ "produces": ["MEMORY-RECALL.md"],
+ "consumes": ["CONTEXT.md"],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
+ {
+ "point": "plan:post",
+ "ref": { "skill": "mempalace-capture" },
+ "produces": [],
+ "consumes": ["PLAN.md"],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
+ {
+ "point": "verify:post",
+ "ref": { "skill": "mempalace-capture" },
+ "produces": [],
+ "consumes": ["SUMMARY.md"],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
+ {
+ "point": "ship:post",
+ "ref": { "agent": "gsd-mempalace-curator" },
+ "produces": [],
+ "consumes": ["UAT.md"],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ }
+ ],
+ "contributions": [
+ {
+ "point": "discuss:pre",
+ "into": "orchestrator",
+ "fragment": { "path": "fragments/recall-discuss.md" },
+ "produces": [],
+ "consumes": [],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
+ {
+ "point": "execute:wave:post",
+ "into": "verifier",
+ "fragment": { "path": "fragments/capture-problems.md" },
+ "produces": [],
+ "consumes": [],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ }
+ ],
+ "gates": []
+}
diff --git a/capabilities/mempalace/fragments/capture-problems.md b/capabilities/mempalace/fragments/capture-problems.md
new file mode 100644
index 000000000..9b273c23a
--- /dev/null
+++ b/capabilities/mempalace/fragments/capture-problems.md
@@ -0,0 +1,20 @@
+
+### Capture problems → fixes (MemPalace)
+
+**Gate first.** Read `.planning/config.json`. If `mempalace.enabled` is not `true`, or `mempalace.capture_artifacts` is `false`, **skip this entire section** and let the wave complete unchanged. (This contribution is only injected when the capability is enabled; the `capture_artifacts` check lets you turn capture off without disabling the rest of the capability.)
+
+Otherwise — after verifying this wave, persist any *confirmed* problem→fix pairs into the palace so they are recalled in future phases. This is best-effort; if MemPalace is unreachable, skip silently — capture never fails a wave.
+
+For each confirmed bug/issue resolved in this wave:
+
+1. **Resolve the wing** (`mempalace.wing`, else `project_code`, else project dir) and target `room: problems`.
+2. **Dedupe first.** Call `mempalace_check_duplicate` (interactive) before filing so re-runs don't create duplicate drawers.
+3. **File the drawer verbatim.** Store the problem statement and its fix as a drawer in `room: problems` — interactive: `mempalace_add_drawer`; headless: `mempalace mine` / `mempalace hook run`. Include provenance (`source_file`, phase id).
+4. **Mirror the KG fact** when `mempalace.mirror_kg` is on: add `(, fixed_by, )` with `valid_from` = the phase date via `mempalace_kg_add`.
+5. **Mode awareness.** Only `augment` is currently wired: the fact is an *additive* mirror alongside `.planning/graphs/` (never a replacement). `kg_backend`/`replace` are forward-declared and behave as `augment` today.
+
+Captures are idempotent: deterministic drawer IDs + `check_duplicate` mean re-running the wave re-files the same content without duplication. On any error, skip and let the wave complete normally.
diff --git a/capabilities/mempalace/fragments/recall-discuss.md b/capabilities/mempalace/fragments/recall-discuss.md
new file mode 100644
index 000000000..774287cfe
--- /dev/null
+++ b/capabilities/mempalace/fragments/recall-discuss.md
@@ -0,0 +1,22 @@
+
+### Memory recall (MemPalace)
+
+**Gate first.** Read `.planning/config.json`. If `mempalace.enabled` is not `true`, or `mempalace.recall_on_discuss` is `false`, **skip this entire section** and continue the discussion unchanged. (This contribution is only injected when the capability is enabled; the `recall_on_discuss` check lets you turn discuss-time recall off without disabling the rest of the capability.)
+
+Otherwise — before gathering new context, surface what you already know. This is read-only and side-effect-free; if MemPalace is unreachable, note "memory unavailable" and continue — recall never blocks discussion.
+
+1. **Resolve the wing.** Use `mempalace.wing` if set; otherwise derive it from `project_code` (fall back to the project directory name).
+2. **Wake up (cheap, ~600–900 tokens).**
+ - Interactive run → call `mempalace_search` after a wake-up read of the wing.
+ - Headless/cron run (no MCP server) → run `mempalace wake-up --wing ` via the CLI.
+3. **Targeted recall.** Search the palace for prior work on this phase's topic:
+ - Interactive → `mempalace_search(query=, wing=)` and, when `mempalace.mirror_kg` is on, `mempalace_kg_query` / `mempalace_kg_timeline` for decision facts and their validity windows.
+ - Headless → `mempalace search "" --wing `.
+4. **Mode awareness.** Only `augment` is currently wired: always treat the palace as an *additional* recall layer on top of GSD's native memory — never skip `.planning/graphs/` or STATE. `kg_backend`/`replace` are forward-declared and behave as `augment` today.
+5. **Surface, don't dump.** Fold the top relevant drawers, decisions, patterns, and *surprises* into the discussion as prior context — cite drawer/fact provenance. Do not paste raw search output.
+
+If any MemPalace call errors or times out, skip the rest of recall and proceed with discussion as normal.
diff --git a/commands/gsd/mempalace-capture.md b/commands/gsd/mempalace-capture.md
new file mode 100644
index 000000000..271920886
--- /dev/null
+++ b/commands/gsd/mempalace-capture.md
@@ -0,0 +1,71 @@
+---
+name: gsd:mempalace-capture
+description: "File a phase artifact into MemPalace; mirror decision facts into its temporal KG"
+argument-hint: "[CONTEXT.md|PLAN.md|SUMMARY.md]"
+allowed-tools:
+ - Read
+ - Bash
+requires: [config]
+---
+
+**STOP -- DO NOT READ THIS FILE. You are already reading it. This prompt was injected into your context by the command system. Using the Read tool on this file wastes tokens. Begin executing Step 0 immediately.**
+
+## Step 0 -- Banner
+
+**Before ANY tool calls**, display this banner:
+
+```
+GSD > MEMPALACE CAPTURE
+```
+
+Then proceed to Step 1.
+
+## Step 1 -- Config Gate
+
+Check whether the MemPalace capability is enabled by reading `.planning/config.json` directly with the Read tool.
+
+1. Read `.planning/config.json` with the Read tool.
+2. If the file does not exist, or `config.mempalace` is absent, or `config.mempalace.enabled !== true`, or `config.mempalace.capture_artifacts !== true`: display the disabled message and **STOP**.
+3. Otherwise proceed to Step 2.
+
+**Disabled message:**
+
+```
+GSD > MEMPALACE CAPTURE
+
+MemPalace capture is disabled (mempalace.enabled / mempalace.capture_artifacts).
+Nothing was filed; the loop proceeds normally.
+```
+
+This step is `onError: skip` at `discuss:post` / `plan:post` / `verify:post` -- capture never fails a phase.
+
+## Step 2 -- Resolve target
+
+1. **Artifact.** Take the artifact from `$ARGUMENTS`. If absent, infer from the loop point: `discuss:post` → `CONTEXT.md`, `plan:post` → `PLAN.md`, `verify:post` → `SUMMARY.md`.
+2. **Room.** Map artifact → room:
+ - `CONTEXT.md` → `decisions`
+ - `PLAN.md` → `planning`
+ - `SUMMARY.md` → `milestones`
+ (Confirmed problem→fix pairs go to `problems` — see the `capture-problems` fragment used at `execute:wave:post`.)
+3. **Wing.** `config.mempalace.wing` if non-empty, else `config.project_code`, else the repo directory name.
+4. **Mode / transport.** Read `config.mempalace.memory_mode`. Prefer MCP (`mempalace_*`) when your MemPalace MCP server is registered and your runtime permits those tools; otherwise use the `mempalace` CLI (covered by this skill's `Bash` allow-tool), as in `mempalace-recall`.
+
+## Step 3 -- File verbatim (idempotent)
+
+On any error or timeout, stop and let the phase continue -- capture is best-effort.
+
+1. **Dedup first.** Interactive: `mempalace_check_duplicate` on the artifact's deterministic drawer id. Headless: rely on `mempalace mine`'s content-hash idempotency.
+2. **Add the drawer (verbatim).** File the exact artifact text into `room: ` of `wing: ` with provenance (`source_file`, phase id). Interactive: `mempalace_add_drawer`. Headless: `mempalace mine --wing --room `.
+3. **Mirror KG facts** when `config.mempalace.mirror_kg` is true: extract decision/delivery facts and `mempalace_kg_add` them with `valid_from` = the phase date (e.g. `(, decided, )` from CONTEXT; `(, delivered, )` from SUMMARY). Only `augment` is currently wired, so these are an *additive* mirror of `.planning/graphs/`. (`kg_backend`/`replace` are forward-declared and behave as `augment` today.)
+4. Re-running a phase MUST NOT create duplicate drawers (deterministic ids + `check_duplicate`).
+
+## Step 4 -- Report
+
+Print a one-line summary: `Filed → / ( KG facts)` or `MemPalace unavailable — capture skipped`.
+
+## Anti-Patterns
+
+1. DO NOT let any MemPalace error fail the step -- capture is `onError: skip`.
+2. DO NOT write lossy summaries -- store the verbatim artifact text (AAAK compression is a separate, optional index).
+3. DO NOT prune or delete drawers here -- pruning (`sync --apply`) is the curator agent's job at `ship:post`, wing-scoped only.
+4. DO NOT skip the config gate or the dedup check.
diff --git a/commands/gsd/mempalace-recall.md b/commands/gsd/mempalace-recall.md
new file mode 100644
index 000000000..14bd9df72
--- /dev/null
+++ b/commands/gsd/mempalace-recall.md
@@ -0,0 +1,102 @@
+---
+name: gsd:mempalace-recall
+description: "Recall decisions, patterns, and surprises from MemPalace before planning"
+argument-hint: "[phase-slug]"
+allowed-tools:
+ - Read
+ - Write
+ - Bash
+requires: [config]
+---
+
+**STOP -- DO NOT READ THIS FILE. You are already reading it. This prompt was injected into your context by the command system. Using the Read tool on this file wastes tokens. Begin executing Step 0 immediately.**
+
+## Step 0 -- Banner
+
+**Before ANY tool calls**, display this banner:
+
+```
+GSD > MEMPALACE RECALL
+```
+
+Then proceed to Step 1.
+
+## Step 1 -- Config Gate
+
+Check whether the MemPalace capability is enabled by reading `.planning/config.json` directly with the Read tool.
+
+**DO NOT use `gsd-tools config get-value`** -- it hard-exits on missing keys.
+
+1. Read `.planning/config.json` with the Read tool.
+2. If the file does not exist: write the "unavailable" stub (Step 4) and **STOP**.
+3. Parse the JSON. Proceed to Step 2 only if `config.mempalace && config.mempalace.enabled === true` **and** `config.mempalace.recall_on_plan !== false`. Otherwise display the disabled message and **STOP** (`recall_on_plan: false` turns plan-time recall off while leaving the rest of the capability enabled).
+
+**Disabled message:**
+
+```
+GSD > MEMPALACE RECALL
+
+MemPalace memory is disabled. To activate:
+
+ node /gsd-core/bin/gsd-tools.cjs config-set mempalace.enabled true
+
+Recall is opt-in; the loop proceeds normally without it.
+```
+
+This step is `onError: skip` at `plan:pre` -- recall never blocks planning.
+
+## Step 2 -- Resolve wing, mode, and transport
+
+1. **Wing.** Use `config.mempalace.wing` if non-empty; otherwise derive from `config.project_code`; otherwise fall back to the repository directory name.
+2. **Mode.** Read `config.mempalace.memory_mode` (`augment` | `kg_backend` | `replace`, default `augment`). Only `augment` is wired today, so recall always treats the palace as additive; `kg_backend`/`replace` are forward-declared and behave as `augment`.
+3. **Transport.** Prefer the **MCP tools** (`mempalace_*`) in interactive runs *when your MemPalace MCP server is registered and your runtime permits those tools*. Otherwise — headless/cron/autonomous runs, or runtimes that don't grant the MemPalace MCP tools — use the **CLI** (`mempalace wake-up`, `mempalace search`), which this skill's `Bash` allow-tool always covers. If neither is reachable, go to Step 4.
+4. **Topic.** Read the phase `CONTEXT.md` (the consumed artifact). Derive a short search query from its title, goal, and key decisions.
+
+## Step 3 -- Retrieve (read-only)
+
+All calls in this step are side-effect-free. On any error or timeout, stop retrieving and write whatever was gathered (or the stub) -- never raise.
+
+1. **Wake up** (cheap, ~600--900 tokens):
+ - Interactive: read the wing identity/summary, then `mempalace_search`.
+ - Headless: `mempalace wake-up --wing `.
+2. **Targeted search:**
+ - Interactive: `mempalace_search(query=, wing=)`.
+ - Headless: `mempalace search "" --wing `.
+3. **Knowledge-graph facts** (when `config.mempalace.mirror_kg` is true): `mempalace_kg_query` / `mempalace_kg_timeline` for decisions relevant to the topic and their validity windows. Only `augment` is currently wired, so the palace KG *supplements* GSD's native `.planning/graphs/` — do not treat it as the sole source. (`kg_backend`/`replace` are forward-declared and behave as `augment` today.)
+4. **Dedup** the returned drawers/facts; keep the top results.
+
+## Step 4 -- Write MEMORY-RECALL.md
+
+Write `MEMORY-RECALL.md` in the current phase directory. The planner consumes it.
+
+When recall succeeded, structure it as:
+
+```markdown
+# Memory Recall (MemPalace)
+
+_Wing: · Mode: · Transport: _
+
+## Prior decisions
+- —
+
+## Patterns
+- —
+
+## Surprises / gotchas
+- —
+```
+
+When MemPalace is unreachable, write the stub and continue:
+
+```markdown
+# Memory Recall (MemPalace)
+
+_MemPalace unavailable at recall time — proceeding without recalled memory._
+```
+
+## Anti-Patterns
+
+1. DO NOT let any MemPalace error fail the step -- recall is `onError: skip`.
+2. DO NOT write to the palace from this skill -- recall is read-only; capture is a separate skill.
+3. DO NOT paste raw search output into the file -- distil to decisions/patterns/surprises with provenance.
+4. DO NOT skip the config gate.
diff --git a/commands/gsd/ns-context.md b/commands/gsd/ns-context.md
index feb952a22..51342371e 100644
--- a/commands/gsd/ns-context.md
+++ b/commands/gsd/ns-context.md
@@ -1,11 +1,11 @@
---
name: gsd-context
-description: "codebase intelligence | map graphify docs learnings"
+description: "codebase intel | map graphify docs learnings mempalace"
argument-hint: ""
allowed-tools:
- Read
- Skill
-requires: [map-codebase, graphify, docs-update, extract-learnings]
+requires: [map-codebase, graphify, docs-update, extract-learnings, mempalace-recall, mempalace-capture]
---
Route to the appropriate codebase-intelligence skill based on the user's intent.
@@ -19,5 +19,7 @@ Route to the appropriate codebase-intelligence skill based on the user's intent.
| Generate a knowledge graph | gsd-graphify |
| Update project documentation | gsd-docs-update |
| Extract learnings from a completed phase | gsd-extract-learnings |
+| Recall prior decisions and patterns before planning | gsd-mempalace-recall |
+| File a phase artifact into MemPalace | gsd-mempalace-capture |
Invoke the matched skill directly using the Skill tool.
diff --git a/docs/AGENTS.md b/docs/AGENTS.md
index d82c2ec73..4d66c8a15 100644
--- a/docs/AGENTS.md
+++ b/docs/AGENTS.md
@@ -10,7 +10,7 @@ GSD uses a multi-agent architecture where thin orchestrators (workflow files) sp
### Agent Categories
-> The table below covers the **21 primary agents** detailed in this section. Twelve additional shipped agents (pattern-mapper, debug-session-manager, code-reviewer, code-fixer, ai-researcher, domain-researcher, eval-planner, eval-auditor, framework-selector, intel-updater, doc-classifier, doc-synthesizer) have concise stubs in the [Advanced and Specialized Agents](#advanced-and-specialized-agents) section below. For the authoritative 33-agent roster, see [`docs/INVENTORY.md`](INVENTORY.md) and the `agents/` directory.
+> The table below covers the **21 primary agents** detailed in this section. Thirteen additional shipped agents (pattern-mapper, debug-session-manager, code-reviewer, code-fixer, ai-researcher, domain-researcher, eval-planner, eval-auditor, framework-selector, intel-updater, doc-classifier, doc-synthesizer, mempalace-curator) have concise stubs in the [Advanced and Specialized Agents](#advanced-and-specialized-agents) section below. For the authoritative 34-agent roster, see [`docs/INVENTORY.md`](INVENTORY.md) and the `agents/` directory.
| Category | Count | Agents |
|----------|-------|--------|
@@ -732,9 +732,30 @@ Twelve additional agents ship under `agents/gsd-*.md` and are used by specialty
---
+### gsd-mempalace-curator
+
+**Role:** Ship-time memory curation — writes per-agent diary entries, proposes and creates cross-project tunnels, runs wing-scoped sync pruning, and mirrors `extract-learnings` output into MemPalace's temporal knowledge graph with provenance.
+
+| Property | Value |
+|----------|-------|
+| **Spawned by** | MemPalace capability at `ship:post` (when `mempalace.enabled = true`); diary/tunnels/KG-mirror are then refined by their own toggles |
+| **Parallelism** | Single instance |
+| **Tools** | Read, Bash, Grep, Glob |
+| **Model (balanced)** | Sonnet |
+| **Produces** | Diary entry in MemPalace, wing tunnel proposals, KG provenance records |
+
+**Key behaviors:**
+- Best-effort only — every operation is `onError: skip`; a MemPalace failure never halts the loop
+- Wing-scoped sync pruning (`mempalace sync --wing --apply`) — never runs a global prune
+- Cross-project tunnel proposals when `mempalace.cross_project_tunnels = true`
+- Mirrors `extract-learnings` decisions, lessons, patterns, and surprises into the KG with `source_drawer_id` provenance
+- Requires MemPalace MCP server or CLI to be reachable; writes a skip-notice stub when unavailable
+
+---
+
## Agent Tool Permissions Summary
-> **Scope:** this table covers the 21 primary agents only. The 12 advanced/specialized agents listed above carry their own tool surfaces in their `agents/gsd-*.md` frontmatter (summarized in the per-agent stubs above and in [`docs/INVENTORY.md`](INVENTORY.md)).
+> **Scope:** this table covers the 21 primary agents only. The 13 advanced/specialized agents listed above carry their own tool surfaces in their `agents/gsd-*.md` frontmatter (summarized in the per-agent stubs above and in [`docs/INVENTORY.md`](INVENTORY.md)).
| Agent | Read | Write | Edit | Bash | Grep | Glob | WebSearch | WebFetch | MCP |
|-------|------|-------|------|------|------|------|-----------|----------|-----|
diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md
index 3ba6e9dac..eec06b69b 100644
--- a/docs/COMMANDS.md
+++ b/docs/COMMANDS.md
@@ -1157,6 +1157,41 @@ Build, query, and inspect the project knowledge graph stored in `.planning/graph
**Programmatic access:** `node gsd-tools.cjs graphify ` — see [CLI Tools Reference](CLI-TOOLS.md).
+### `/gsd-mempalace-recall`
+
+Recall prior decisions, patterns, and surprises from MemPalace into `MEMORY-RECALL.md` before planning. Reads `CONTEXT.md` to derive a search query, runs `mempalace wake-up` + `mempalace_search` + `mempalace_kg_query`/timeline, and writes a deduped recall document. When MemPalace is unavailable the skill writes a stub and continues. Opt-in via `mempalace.enabled: true` and `mempalace.recall_on_plan: true` (see [Configuration Reference](CONFIGURATION.md#mempalace-settings)).
+
+| Argument | Required | Description |
+|----------|----------|-------------|
+| `phase-slug` | No | Phase slug used to scope the search query (defaults to the active phase from CONTEXT.md) |
+
+**Produces:** `MEMORY-RECALL.md` in the active phase directory (or an "unavailable" stub when MemPalace is unreachable)
+
+```bash
+/gsd-mempalace-recall # Recall for the current phase
+/gsd-mempalace-recall 03-auth # Recall scoped to a specific phase slug
+```
+
+---
+
+### `/gsd-mempalace-capture`
+
+File a phase artifact (`CONTEXT.md`, `PLAN.md`, or `SUMMARY.md`) verbatim into MemPalace and mirror decision facts into its temporal knowledge graph. Uses `mempalace_check_duplicate` before filing, so re-running the same phase is idempotent. Opt-in via `mempalace.enabled: true` and `mempalace.capture_artifacts: true` (see [Configuration Reference](CONFIGURATION.md#mempalace-settings)).
+
+| Argument | Required | Description |
+|----------|----------|-------------|
+| `CONTEXT.md\|PLAN.md\|SUMMARY.md` | No | Artifact to capture (defaults to `CONTEXT.md` when called at `discuss:post`) |
+
+**Produces:** A drawer in the appropriate MemPalace room (`decisions`, `planning`, or `milestones`) plus KG facts when `mempalace.mirror_kg: true`
+
+```bash
+/gsd-mempalace-capture CONTEXT.md # File CONTEXT.md → decisions room
+/gsd-mempalace-capture PLAN.md # File PLAN.md → planning room
+/gsd-mempalace-capture SUMMARY.md # File SUMMARY.md → milestones room
+```
+
+---
+
### `gsd-tools intel api-surface`
Render the `.planning/intel/api-map.json` index (built by `/gsd-map-codebase`) into a human-readable `API-SURFACE.md` in `.planning/intel/`. Gated on `intel.enabled: true` in `config.json`; when Intel is disabled the command prints an activation hint and exits. The output path is always `.planning/intel/API-SURFACE.md` — there is no `--out` or `--format` flag. When `api-map.json` is absent or empty the command still writes the file with an explicit "incomplete" banner so consumers never mistake silence for "nothing exists".
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
index 543d5d46f..e98a22494 100644
--- a/docs/CONFIGURATION.md
+++ b/docs/CONFIGURATION.md
@@ -531,6 +531,49 @@ The `plan_review.*` namespace controls the plan drift guard, which verifies that
| `plan_review.source_grounding` | boolean | `true` | Enable the plan drift guard. When `true` (the default), plan review resolves every symbol reference cited in a PLAN.md against the live source tree. Plans that cite a non-existent function, class, decorator, or CLI flag produce a `needs-acknowledgement` notice before the plan is approved. Disable with `false` to skip symbol verification entirely. Toggle during setup (`/gsd:new-project`) or at any time via `/gsd:settings`. |
| `plan_review.source_grounding_authority` | enum | `grep` | Selects the resolver adapter used to verify symbol existence. Allowed values: `grep` (default — ripgrep/grep search of source files, works in any project without additional tooling), `intel` (query the `.planning/intel/api-map.json` index built by `/gsd:map-codebase`; requires `intel.enabled: true`), `treesitter` (reserved for future tree-sitter adapter), `lsp` (reserved for future LSP adapter), `scip` (reserved for future SCIP/LSIF adapter). Use `intel` when you have run `/gsd:map-codebase` and want the faster, pre-indexed lookup. All other values beyond `grep` and `intel` are reserved and have no effect in the current release. |
+
+### MemPalace Settings
+
+MemPalace is an opt-in, default-resilient memory capability. Every hook is `onError: skip` — a missing or unreachable MemPalace installation never halts or fails the loop. Enable with `mempalace.enabled: true` after installing MemPalace (`pip install mempalace`).
+
+`mempalace.enabled` is the **master gate**: all five loop hooks (discuss, plan, execute-wave, verify, ship) and both curator contributions are gated on this key. When it is `false` (the default), nothing fires and the GSD loop is byte-for-byte unchanged. The remaining keys only refine behavior when `mempalace.enabled` is `true`; they are honored at runtime by the skills, curator, and fragments — they do not add independent hook gating.
+
+| Setting | Type | Default | Description |
+|---------|------|---------|-------------|
+| `mempalace.enabled` | boolean | `false` | Master gate for the MemPalace memory capability. When `false` (the default) every recall/capture hook is inactive and the loop is unchanged. All other `mempalace.*` keys are inert while this is `false`. |
+| `mempalace.memory_mode` | enum: `augment`, `kg_backend`, `replace` | `augment` | How MemPalace relates to GSD native memory. `augment` (**implemented** — MemPalace is an additional write-mostly recall layer alongside GSD's native graphs/learnings; lowest coupling). `kg_backend` (**declared; routing seam not yet implemented** — intended to route graphify KG queries through MemPalace's temporal graph instead of `.planning/graphs/`; selecting this today behaves the same as `augment`). `replace` (**declared; not yet functional** — intended to make the palace the durable store for GSD memory reads; selecting this today behaves the same as `augment`). |
+| `mempalace.wing` | string | `""` | Palace wing name for this project. Empty (the default) derives the wing from `project_code` or the project directory name. |
+| `mempalace.recall_on_discuss` | boolean | `true` | When `mempalace.enabled` is `true`: inject a wake-up + semantic-search recall fragment into the orchestrator at `discuss:pre`. Surfaces prior decisions, patterns, and surprises before the discussion starts. |
+| `mempalace.recall_on_plan` | boolean | `true` | When `mempalace.enabled` is `true`: run the `mempalace-recall` skill at `plan:pre` to produce `MEMORY-RECALL.md` from prior decisions, patterns, and surprises relevant to the plan. |
+| `mempalace.capture_artifacts` | boolean | `true` | When `mempalace.enabled` is `true`: file phase artifacts (`CONTEXT.md`, `PLAN.md`, `SUMMARY.md`) verbatim into MemPalace at their respective phase boundaries (`discuss:post`, `plan:post`, `verify:post`). Also captures confirmed bug→fix pairs at `execute:wave:post`. |
+| `mempalace.mirror_kg` | boolean | `true` | When `mempalace.enabled` is `true`: mirror decisions and learnings into MemPalace's temporal knowledge graph (`mempalace_kg_add` with `valid_from` = phase date) alongside drawer capture. |
+| `mempalace.cross_project_tunnels` | boolean | `false` | When `mempalace.enabled` is `true`: at `ship:post`, propose and create tunnels between this wing's rooms and semantically related wings in other projects (`mempalace_find_tunnels`, `mempalace_create_tunnel`). |
+| `mempalace.diary_journal` | boolean | `true` | When `mempalace.enabled` is `true`: at `ship:post`, write a per-agent diary entry (`mempalace_diary_write`) summarising the session. |
+| `mempalace.auto_capture_hooks` | boolean | `false` | **Reserved — not yet implemented.** Intended to install MemPalace's native Claude Code hooks (`session-start`, `stop`, `precompact`) for passive mid-session capture between loop points. The capability's `hooks` array is currently empty; no native hooks are installed by setting this key. This key is forward-declared for the future "Connected Capability" phase. |
+
+#### Memory modes in detail
+
+| Mode | `.planning/graphs` KG | Recall source | Coupling | Status |
+|------|-----------------------|---------------|---------|--------|
+| `augment` (default) | stays native | GSD native + palace search | lowest | **Implemented** |
+| `kg_backend` | intended: routed to MemPalace temporal graph | intended: KG queries hit MemPalace | medium | **Declared — routing seam not yet implemented; behaves as `augment`** |
+| `replace` | intended: backed by palace | intended: palace is the durable store | highest | **Declared — not yet functional; behaves as `augment`** |
+
+Mode is read at hook-render time; switching modes is a config change, not a reinstall. Only `augment` has effect today — `kg_backend` and `replace` are forward-declared for a future release.
+
+#### Example
+
+```bash
+# Enable MemPalace (augment mode — the only implemented mode today)
+gsd-tools query config-set mempalace.enabled true
+
+# Forward-declared: kg_backend/replace are not yet functional (declared for future release)
+# gsd-tools query config-set mempalace.memory_mode kg_backend
+
+# Enable cross-project tunnel proposals at ship:post
+gsd-tools query config-set mempalace.cross_project_tunnels true
+```
+
### Graphify Settings
diff --git a/docs/FEATURES.md b/docs/FEATURES.md
index a66f02627..c7cab7fc1 100644
--- a/docs/FEATURES.md
+++ b/docs/FEATURES.md
@@ -166,6 +166,8 @@
- [Structured JSON Error Mode](#142-structured-json-error-mode)
- [UAT-Passed Predicate](#143-uat-passed-predicate)
- [Spec-Phase Edge-Completeness Probe](#144-spec-phase-edge-completeness-probe)
+- [v1.43.0 Features](#v1430-features)
+ - [MemPalace Memory Capability](#145-mempalace-memory-capability)
---
@@ -3119,3 +3121,27 @@ The load-bearing wire is the `plan-phase` lift: `covered` and `backstop` edges b
- REQ-EDGE-06: `plan-phase` MUST lift `covered` criteria and `backstop` notes into `must_haves.truths`.
**Reference:** [Edge Probe](../gsd-core/references/edge-probe.md)
+
+---
+
+## v1.43.0 Features
+
+### 145. MemPalace Memory Capability
+
+**Purpose:** Opt-in cross-session and cross-project memory via the [MemPalace](https://github.com/MemPalace/mempalace) external service (local-first, MCP + CLI). Wires deliberate recall before discuss/plan and verbatim capture + temporal-KG sync at phase boundaries through the ADR-857 capability mechanism. Default-resilient: disabled by default, every hook is `onError: skip`, and an absent MemPalace installation leaves the loop unchanged.
+
+**Commands:** `/gsd-mempalace-recall`, `/gsd-mempalace-capture`
+
+**Requirements:**
+- REQ-MP-01: Opt-in via `mempalace.enabled: true`. Default `false` — the loop is unchanged when unset.
+- REQ-MP-02: At `plan:pre`, skill `mempalace-recall` produces `MEMORY-RECALL.md` from prior decisions, patterns, and surprises retrieved via wake-up + semantic search + KG timeline. When MemPalace is unreachable, writes an "unavailable" stub and continues.
+- REQ-MP-03: At `discuss:post`, `plan:post`, and `verify:post`, skill `mempalace-capture` files the phase artifact verbatim into the appropriate MemPalace room (`decisions`, `planning`, `milestones`). Capture is idempotent via `mempalace_check_duplicate`.
+- REQ-MP-04: At `ship:post`, agent `gsd-mempalace-curator` writes a diary entry, proposes cross-project tunnels (when `mempalace.cross_project_tunnels: true`), and runs wing-scoped sync pruning.
+- REQ-MP-05: `mempalace.memory_mode` declares three values: `augment` (default, **implemented** — palace is an additional recall layer alongside GSD native memory), `kg_backend` (**forward-declared; routing seam not yet implemented** — selecting this today behaves identically to `augment`), `replace` (**forward-declared; not yet functional** — selecting this today behaves identically to `augment`). Only `augment` has effect in the current release.
+- REQ-MP-06: Every hook is `onError: skip`. No hook carries `blocking: true`. Memory never halts or fails a phase.
+- REQ-MP-07: Interactive runs prefer MCP tools; headless/cron runs prefer the MemPalace CLI (`mempalace wake-up`, `mempalace search`, `mempalace mine`, `mempalace sync`).
+- REQ-MP-08: `mempalace.auto_capture_hooks` is **forward-declared and not yet functional**. No native Claude Code hooks (`stop`, `precompact`, `session-start`) are installed by this key; the capability's hooks array is empty. This key is reserved for the future "Connected Capability" phase. Default `false`.
+
+**Configuration:** `mempalace.enabled`, `mempalace.memory_mode`, `mempalace.wing`, `mempalace.recall_on_discuss`, `mempalace.recall_on_plan`, `mempalace.capture_artifacts`, `mempalace.mirror_kg`, `mempalace.cross_project_tunnels`, `mempalace.diary_journal`, `mempalace.auto_capture_hooks`
+
+See [Configuration Reference](CONFIGURATION.md#mempalace-settings) for full schema and [How to enable cross-session memory with MemPalace](how-to/enable-cross-session-memory-with-mempalace.md) for a setup walkthrough.
diff --git a/docs/INVENTORY-MANIFEST.json b/docs/INVENTORY-MANIFEST.json
index b6eb15ae9..54fd39a8e 100644
--- a/docs/INVENTORY-MANIFEST.json
+++ b/docs/INVENTORY-MANIFEST.json
@@ -20,6 +20,7 @@
"gsd-framework-selector",
"gsd-integration-checker",
"gsd-intel-updater",
+ "gsd-mempalace-curator",
"gsd-nyquist-auditor",
"gsd-pattern-mapper",
"gsd-phase-researcher",
@@ -64,6 +65,8 @@
"/gsd-ingest-docs",
"/gsd-manager",
"/gsd-map-codebase",
+ "/gsd-mempalace-capture",
+ "/gsd-mempalace-recall",
"/gsd-milestone-summary",
"/gsd-mvp-phase",
"/gsd-new-milestone",
diff --git a/docs/INVENTORY.md b/docs/INVENTORY.md
index 0e0e64227..51bf10b99 100644
--- a/docs/INVENTORY.md
+++ b/docs/INVENTORY.md
@@ -51,6 +51,7 @@ Full roster at `agents/gsd-*.md`. The "Primary doc" column flags whether [`docs/
| gsd-intel-updater | Writes structured intel files (`.planning/intel/*.json`) used as a queryable codebase knowledge base. | `/gsd-map-codebase --query` | advanced stub |
| gsd-doc-classifier | Classifies a single planning document as ADR, PRD, SPEC, DOC, or UNKNOWN; spawned in parallel to process the doc corpus. | `/gsd-ingest-docs` | advanced stub |
| gsd-doc-synthesizer | Synthesizes classified planning docs into a single consolidated context with precedence rules, cycle detection, and three-bucket conflicts report. | `/gsd-ingest-docs` | advanced stub |
+| gsd-mempalace-curator | Ship-time MemPalace curation — diary entry, cross-project tunnel proposals, wing-scoped sync pruning, and extract-learnings → KG mirroring with provenance. | MemPalace capability at `ship:post` | advanced stub |
**Coverage note.** `docs/AGENTS.md` gives full role cards for the primary agents plus concise stubs for the advanced agents. The Agent Tool Permissions Summary in that file covers only the primary agents; the advanced agents' tool lists are captured in their per-agent frontmatter in `agents/gsd-*.md`.
@@ -138,6 +139,8 @@ These six routers are descriptor-only entries that the model picks first; the bo
| `/gsd-map-codebase` | Analyze codebase with parallel mapper agents; use `--fast` for lightweight scan or `--query` for intel queries. | [commands/gsd/map-codebase.md](../commands/gsd/map-codebase.md) |
| `/gsd-graphify` | Build, query, and inspect the project knowledge graph in `.planning/graphs/`. | [commands/gsd/graphify.md](../commands/gsd/graphify.md) |
| `/gsd-extract-learnings` | Extract decisions, lessons, patterns, and surprises from completed phase artifacts. | [commands/gsd/extract-learnings.md](../commands/gsd/extract-learnings.md) |
+| `/gsd-mempalace-recall` | Recall prior decisions, patterns, and surprises from MemPalace into MEMORY-RECALL.md before planning. | [commands/gsd/mempalace-recall.md](../commands/gsd/mempalace-recall.md) |
+| `/gsd-mempalace-capture` | File a phase artifact (CONTEXT/PLAN/SUMMARY) verbatim into MemPalace and mirror decision facts into its temporal KG. | [commands/gsd/mempalace-capture.md](../commands/gsd/mempalace-capture.md) |
### Review, Debug & Recovery
diff --git a/docs/how-to/enable-cross-session-memory-with-mempalace.md b/docs/how-to/enable-cross-session-memory-with-mempalace.md
new file mode 100644
index 000000000..fc01a33a5
--- /dev/null
+++ b/docs/how-to/enable-cross-session-memory-with-mempalace.md
@@ -0,0 +1,138 @@
+# How to enable cross-session memory with MemPalace
+
+Give GSD durable memory across sessions and projects. When MemPalace is connected, GSD recalls prior decisions and patterns before you plan, and captures phase artifacts verbatim at each phase boundary.
+
+**What you need:** [MemPalace](https://github.com/MemPalace/mempalace) installed locally. GSD never installs MemPalace for you — `pip install mempalace` is your action. A working MemPalace install is not required to complete GSD work; the capability is `onError: skip` at every hook, so an absent or unreachable MemPalace leaves the loop unchanged.
+
+---
+
+## Step 1 — Install MemPalace
+
+Follow the [MemPalace installation guide](https://github.com/MemPalace/mempalace#installation). The quick path:
+
+```bash
+pip install mempalace
+mempalace init # creates the local palace (ChromaDB + SQLite)
+mempalace status # verify the server is reachable
+```
+
+For interactive use (Claude Code), MemPalace's MCP server must be running so `mempalace_*` tool calls resolve. For headless or cron runs, the CLI path (`mempalace wake-up`, `mempalace search`, etc.) is used automatically — no MCP server needed.
+
+---
+
+## Step 2 — Enable the capability
+
+Inside your GSD project:
+
+```bash
+gsd-tools query config-set mempalace.enabled true
+```
+
+That is the only required step — `mempalace.enabled` is the master switch that gates all loop hooks. All other `mempalace.*` keys are optional refinements of the enabled behavior; they are honored at runtime by the skills, curator, and fragments.
+
+---
+
+## Step 3 — Choose a memory mode
+
+The `mempalace.memory_mode` key controls how tightly MemPalace couples to GSD's native memory. **Only `augment` is implemented today.** The other modes are declared for future use — selecting them today has no additional effect beyond `augment`.
+
+| Mode | What it does | When to use it | Status |
+|------|-------------|----------------|--------|
+| `augment` (default) | MemPalace is an additional recall layer alongside `.planning/graphs/` and learnings. Lowest coupling — palace is write-mostly and never required. | Most users. Safe to enable immediately. | **Implemented** |
+| `kg_backend` | Intended to route knowledge-graph queries through MemPalace's temporal graph instead of `.planning/graphs/`. | Future use — not yet functional today. | **Declared; routing seam not yet implemented** |
+| `replace` | Intended to make the palace the durable store; GSD memory reads would resolve through it. | Future use — not yet functional today. | **Declared; not yet functional** |
+
+Until `kg_backend` and `replace` are implemented, changing `memory_mode` away from `augment` has no effect. Use the default and revisit when these modes ship.
+
+```bash
+# memory_mode defaults to augment (the only functional mode today)
+# no change needed for most users
+```
+
+---
+
+## Step 4 — Run a phase and observe recall and capture
+
+With `mempalace.enabled: true`, here is what you will see at each loop point:
+
+**Discuss phase (`/gsd-discuss-phase`):**
+1. At `discuss:pre` — MemPalace recall fires: prior decisions, patterns, and surprises are surfaced as context before the discussion begins.
+2. At `discuss:post` — `CONTEXT.md` is filed into the `decisions` room in MemPalace.
+
+**Plan phase (`/gsd-plan-phase`):**
+3. At `plan:pre` — a `MEMORY-RECALL.md` file appears in the phase directory containing prior decisions, patterns, and surprises retrieved from the palace.
+4. At `plan:post` — `PLAN.md` is filed into the `planning` room.
+
+**Verify phase (`/gsd-verify-work`):**
+5. At `verify:post` — `SUMMARY.md` is filed into `milestones` in MemPalace.
+
+**Ship phase (`/gsd-ship`):**
+6. At `ship:post` — the `gsd-mempalace-curator` agent writes a diary entry and (if enabled) proposes cross-project tunnels.
+
+**Execute phase (`/gsd-execute-phase`, each wave):**
+7. At `execute:wave:post` — confirmed problem→fix pairs are captured into the `problems` room via the `capture-problems` fragment.
+
+If MemPalace is unreachable at any step, a skip-notice is written and the loop continues normally.
+
+---
+
+## Optional configuration
+
+### Turn off recall or capture independently
+
+```bash
+# Disable recall injection at discuss:pre (keeps capture)
+gsd-tools query config-set mempalace.recall_on_discuss false
+
+# Disable the MEMORY-RECALL.md step at plan:pre
+gsd-tools query config-set mempalace.recall_on_plan false
+
+# Disable artifact capture at phase boundaries
+gsd-tools query config-set mempalace.capture_artifacts false
+```
+
+### Cross-project tunnels
+
+Enable tunnel proposals at `ship:post` to connect related rooms across projects:
+
+```bash
+gsd-tools query config-set mempalace.cross_project_tunnels true
+```
+
+The curator agent will call `mempalace_find_tunnels` and propose connections to the wings it finds semantically related to this project's wing.
+
+### Passive mid-session capture (reserved — not yet implemented)
+
+`mempalace.auto_capture_hooks` is a forward-declared key reserved for a future "Connected Capability" phase. Setting it to `true` currently has no effect — no native Claude Code hooks (`stop`, `precompact`, `session-start`) are installed by this key yet. The capability's hooks array is empty; the deliberate loop hooks are the only active integration today.
+
+```bash
+# Not yet functional — reserved for a future release
+# gsd-tools query config-set mempalace.auto_capture_hooks true
+```
+
+### Override the wing name
+
+By default, the wing name derives from `project_code` or the project directory. Override it:
+
+```bash
+gsd-tools query config-set mempalace.wing my-project-name
+```
+
+---
+
+## What to expect when MemPalace is absent
+
+If MemPalace is not installed, not running, or unreachable:
+
+- Every hook logs a skip notice and continues.
+- `MEMORY-RECALL.md` is written with an "unavailable" stub (the planner can still proceed without it).
+- No phase step fails or blocks.
+- Loop behaviour is identical to having `mempalace.enabled: false`.
+
+You can safely leave `mempalace.enabled: true` in a config that will be used on machines without MemPalace — it is safe to do so.
+
+---
+
+## Full configuration reference
+
+See [MemPalace Settings](../CONFIGURATION.md#mempalace-settings) in the Configuration Reference for the complete key list with types and defaults.
diff --git a/gsd-core/bin/lib/capability-registry.cjs b/gsd-core/bin/lib/capability-registry.cjs
index d07f477dc..6ec540646 100644
--- a/gsd-core/bin/lib/capability-registry.cjs
+++ b/gsd-core/bin/lib/capability-registry.cjs
@@ -1000,6 +1000,176 @@ const capabilities = {
"extendedHookEvents": []
}
},
+ "mempalace": {
+ "id": "mempalace",
+ "role": "feature",
+ "title": "MemPalace memory",
+ "description": "Cross-session, cross-project memory: deliberate recall before discuss/plan and verbatim capture + temporal-KG sync at phase boundaries, via the MemPalace MCP server and CLI.",
+ "tier": "full",
+ "requires": [],
+ "runtimeCompat": {
+ "supported": [
+ "*"
+ ],
+ "unsupported": []
+ },
+ "skills": [
+ "mempalace-recall",
+ "mempalace-capture"
+ ],
+ "agents": [
+ "gsd-mempalace-curator"
+ ],
+ "hooks": [],
+ "config": {
+ "mempalace.enabled": {
+ "type": "boolean",
+ "default": false,
+ "description": "Master toggle for the MemPalace memory capability."
+ },
+ "mempalace.memory_mode": {
+ "type": "enum",
+ "values": [
+ "augment",
+ "kg_backend",
+ "replace"
+ ],
+ "default": "augment",
+ "description": "How MemPalace relates to GSD native memory. Only 'augment' (additive) is implemented today; 'kg_backend' and 'replace' are forward-declared (routing seam not yet built) and currently behave as 'augment'."
+ },
+ "mempalace.wing": {
+ "type": "string",
+ "default": "",
+ "description": "Palace wing name; empty derives from project_code / project dir."
+ },
+ "mempalace.recall_on_discuss": {
+ "type": "boolean",
+ "default": true,
+ "description": "Inject wake-up + search recall at discuss:pre."
+ },
+ "mempalace.recall_on_plan": {
+ "type": "boolean",
+ "default": true,
+ "description": "Produce MEMORY-RECALL.md at plan:pre."
+ },
+ "mempalace.capture_artifacts": {
+ "type": "boolean",
+ "default": true,
+ "description": "File CONTEXT/PLAN/SUMMARY and learnings into the palace at phase boundaries."
+ },
+ "mempalace.mirror_kg": {
+ "type": "boolean",
+ "default": true,
+ "description": "Mirror decisions/learnings into MemPalace's temporal knowledge graph."
+ },
+ "mempalace.cross_project_tunnels": {
+ "type": "boolean",
+ "default": false,
+ "description": "Propose/create cross-wing tunnels at ship:post."
+ },
+ "mempalace.diary_journal": {
+ "type": "boolean",
+ "default": true,
+ "description": "Write a per-agent diary entry at ship:post."
+ },
+ "mempalace.auto_capture_hooks": {
+ "type": "boolean",
+ "default": false,
+ "description": "Reserved / not yet implemented: will install MemPalace's native stop/precompact Claude Code hooks for passive mid-session capture (the capability's hooks array is currently empty)."
+ }
+ },
+ "steps": [
+ {
+ "point": "discuss:post",
+ "ref": {
+ "skill": "mempalace-capture"
+ },
+ "produces": [],
+ "consumes": [
+ "CONTEXT.md"
+ ],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
+ {
+ "point": "plan:pre",
+ "ref": {
+ "skill": "mempalace-recall"
+ },
+ "produces": [
+ "MEMORY-RECALL.md"
+ ],
+ "consumes": [
+ "CONTEXT.md"
+ ],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
+ {
+ "point": "plan:post",
+ "ref": {
+ "skill": "mempalace-capture"
+ },
+ "produces": [],
+ "consumes": [
+ "PLAN.md"
+ ],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
+ {
+ "point": "verify:post",
+ "ref": {
+ "skill": "mempalace-capture"
+ },
+ "produces": [],
+ "consumes": [
+ "SUMMARY.md"
+ ],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
+ {
+ "point": "ship:post",
+ "ref": {
+ "agent": "gsd-mempalace-curator"
+ },
+ "produces": [],
+ "consumes": [
+ "UAT.md"
+ ],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ }
+ ],
+ "contributions": [
+ {
+ "point": "discuss:pre",
+ "into": "orchestrator",
+ "fragment": {
+ "path": "fragments/recall-discuss.md",
+ "inline": "\n### Memory recall (MemPalace)\n\n**Gate first.** Read `.planning/config.json`. If `mempalace.enabled` is not `true`, or `mempalace.recall_on_discuss` is `false`, **skip this entire section** and continue the discussion unchanged. (This contribution is only injected when the capability is enabled; the `recall_on_discuss` check lets you turn discuss-time recall off without disabling the rest of the capability.)\n\nOtherwise — before gathering new context, surface what you already know. This is read-only and side-effect-free; if MemPalace is unreachable, note \"memory unavailable\" and continue — recall never blocks discussion.\n\n1. **Resolve the wing.** Use `mempalace.wing` if set; otherwise derive it from `project_code` (fall back to the project directory name).\n2. **Wake up (cheap, ~600–900 tokens).**\n - Interactive run → call `mempalace_search` after a wake-up read of the wing.\n - Headless/cron run (no MCP server) → run `mempalace wake-up --wing ` via the CLI.\n3. **Targeted recall.** Search the palace for prior work on this phase's topic:\n - Interactive → `mempalace_search(query=, wing=)` and, when `mempalace.mirror_kg` is on, `mempalace_kg_query` / `mempalace_kg_timeline` for decision facts and their validity windows.\n - Headless → `mempalace search \"\" --wing `.\n4. **Mode awareness.** Only `augment` is currently wired: always treat the palace as an *additional* recall layer on top of GSD's native memory — never skip `.planning/graphs/` or STATE. `kg_backend`/`replace` are forward-declared and behave as `augment` today.\n5. **Surface, don't dump.** Fold the top relevant drawers, decisions, patterns, and *surprises* into the discussion as prior context — cite drawer/fact provenance. Do not paste raw search output.\n\nIf any MemPalace call errors or times out, skip the rest of recall and proceed with discussion as normal.\n"
+ },
+ "produces": [],
+ "consumes": [],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
+ {
+ "point": "execute:wave:post",
+ "into": "verifier",
+ "fragment": {
+ "path": "fragments/capture-problems.md",
+ "inline": "\n### Capture problems → fixes (MemPalace)\n\n**Gate first.** Read `.planning/config.json`. If `mempalace.enabled` is not `true`, or `mempalace.capture_artifacts` is `false`, **skip this entire section** and let the wave complete unchanged. (This contribution is only injected when the capability is enabled; the `capture_artifacts` check lets you turn capture off without disabling the rest of the capability.)\n\nOtherwise — after verifying this wave, persist any *confirmed* problem→fix pairs into the palace so they are recalled in future phases. This is best-effort; if MemPalace is unreachable, skip silently — capture never fails a wave.\n\nFor each confirmed bug/issue resolved in this wave:\n\n1. **Resolve the wing** (`mempalace.wing`, else `project_code`, else project dir) and target `room: problems`.\n2. **Dedupe first.** Call `mempalace_check_duplicate` (interactive) before filing so re-runs don't create duplicate drawers.\n3. **File the drawer verbatim.** Store the problem statement and its fix as a drawer in `room: problems` — interactive: `mempalace_add_drawer`; headless: `mempalace mine` / `mempalace hook run`. Include provenance (`source_file`, phase id).\n4. **Mirror the KG fact** when `mempalace.mirror_kg` is on: add `(, fixed_by, )` with `valid_from` = the phase date via `mempalace_kg_add`.\n5. **Mode awareness.** Only `augment` is currently wired: the fact is an *additive* mirror alongside `.planning/graphs/` (never a replacement). `kg_backend`/`replace` are forward-declared and behave as `augment` today.\n\nCaptures are idempotent: deterministic drawer IDs + `check_duplicate` mean re-running the wave re-files the same content without duplication. On any error, skip and let the wave complete normally.\n"
+ },
+ "produces": [],
+ "consumes": [],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ }
+ ],
+ "gates": []
+ },
"nyquist": {
"id": "nyquist",
"role": "feature",
@@ -1716,6 +1886,8 @@ const bySkill = {
"ai-integration-phase": "ai-integration",
"code-review": "code-review",
"graphify": "graphify",
+ "mempalace-recall": "mempalace",
+ "mempalace-capture": "mempalace",
"validate-phase": "nyquist",
"profile-user": "profile-pipeline",
"secure-phase": "security",
@@ -1730,6 +1902,7 @@ const byAgent = {
"gsd-eval-planner": "ai-integration",
"gsd-code-reviewer": "code-review",
"gsd-code-fixer": "code-review",
+ "gsd-mempalace-curator": "mempalace",
"gsd-nyquist-auditor": "nyquist",
"gsd-pattern-mapper": "pattern-mapper",
"gsd-user-profiler": "profile-pipeline",
@@ -1742,11 +1915,39 @@ const byAgent = {
const byLoopPoint = {
"discuss:pre": {
"steps": [],
- "contributions": [],
+ "contributions": [
+ {
+ "capId": "mempalace",
+ "point": "discuss:pre",
+ "into": "orchestrator",
+ "fragment": {
+ "path": "fragments/recall-discuss.md",
+ "inline": "\n### Memory recall (MemPalace)\n\n**Gate first.** Read `.planning/config.json`. If `mempalace.enabled` is not `true`, or `mempalace.recall_on_discuss` is `false`, **skip this entire section** and continue the discussion unchanged. (This contribution is only injected when the capability is enabled; the `recall_on_discuss` check lets you turn discuss-time recall off without disabling the rest of the capability.)\n\nOtherwise — before gathering new context, surface what you already know. This is read-only and side-effect-free; if MemPalace is unreachable, note \"memory unavailable\" and continue — recall never blocks discussion.\n\n1. **Resolve the wing.** Use `mempalace.wing` if set; otherwise derive it from `project_code` (fall back to the project directory name).\n2. **Wake up (cheap, ~600–900 tokens).**\n - Interactive run → call `mempalace_search` after a wake-up read of the wing.\n - Headless/cron run (no MCP server) → run `mempalace wake-up --wing ` via the CLI.\n3. **Targeted recall.** Search the palace for prior work on this phase's topic:\n - Interactive → `mempalace_search(query=, wing=)` and, when `mempalace.mirror_kg` is on, `mempalace_kg_query` / `mempalace_kg_timeline` for decision facts and their validity windows.\n - Headless → `mempalace search \"\" --wing `.\n4. **Mode awareness.** Only `augment` is currently wired: always treat the palace as an *additional* recall layer on top of GSD's native memory — never skip `.planning/graphs/` or STATE. `kg_backend`/`replace` are forward-declared and behave as `augment` today.\n5. **Surface, don't dump.** Fold the top relevant drawers, decisions, patterns, and *surprises* into the discussion as prior context — cite drawer/fact provenance. Do not paste raw search output.\n\nIf any MemPalace call errors or times out, skip the rest of recall and proceed with discussion as normal.\n"
+ },
+ "produces": [],
+ "consumes": [],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ }
+ ],
"gates": []
},
"discuss:post": {
- "steps": [],
+ "steps": [
+ {
+ "capId": "mempalace",
+ "point": "discuss:post",
+ "ref": {
+ "skill": "mempalace-capture"
+ },
+ "produces": [],
+ "consumes": [
+ "CONTEXT.md"
+ ],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ }
+ ],
"contributions": [],
"gates": []
},
@@ -1780,6 +1981,21 @@ const byLoopPoint = {
"when": "intel.enabled",
"onError": "skip"
},
+ {
+ "capId": "mempalace",
+ "point": "plan:pre",
+ "ref": {
+ "skill": "mempalace-recall"
+ },
+ "produces": [
+ "MEMORY-RECALL.md"
+ ],
+ "consumes": [
+ "CONTEXT.md"
+ ],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
{
"capId": "research",
"point": "plan:pre",
@@ -1894,7 +2110,21 @@ const byLoopPoint = {
]
},
"plan:post": {
- "steps": [],
+ "steps": [
+ {
+ "capId": "mempalace",
+ "point": "plan:post",
+ "ref": {
+ "skill": "mempalace-capture"
+ },
+ "produces": [],
+ "consumes": [
+ "PLAN.md"
+ ],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ }
+ ],
"contributions": [],
"gates": [
{
@@ -1921,7 +2151,21 @@ const byLoopPoint = {
},
"execute:wave:post": {
"steps": [],
- "contributions": [],
+ "contributions": [
+ {
+ "capId": "mempalace",
+ "point": "execute:wave:post",
+ "into": "verifier",
+ "fragment": {
+ "path": "fragments/capture-problems.md",
+ "inline": "\n### Capture problems → fixes (MemPalace)\n\n**Gate first.** Read `.planning/config.json`. If `mempalace.enabled` is not `true`, or `mempalace.capture_artifacts` is `false`, **skip this entire section** and let the wave complete unchanged. (This contribution is only injected when the capability is enabled; the `capture_artifacts` check lets you turn capture off without disabling the rest of the capability.)\n\nOtherwise — after verifying this wave, persist any *confirmed* problem→fix pairs into the palace so they are recalled in future phases. This is best-effort; if MemPalace is unreachable, skip silently — capture never fails a wave.\n\nFor each confirmed bug/issue resolved in this wave:\n\n1. **Resolve the wing** (`mempalace.wing`, else `project_code`, else project dir) and target `room: problems`.\n2. **Dedupe first.** Call `mempalace_check_duplicate` (interactive) before filing so re-runs don't create duplicate drawers.\n3. **File the drawer verbatim.** Store the problem statement and its fix as a drawer in `room: problems` — interactive: `mempalace_add_drawer`; headless: `mempalace mine` / `mempalace hook run`. Include provenance (`source_file`, phase id).\n4. **Mirror the KG fact** when `mempalace.mirror_kg` is on: add `(, fixed_by, )` with `valid_from` = the phase date via `mempalace_kg_add`.\n5. **Mode awareness.** Only `augment` is currently wired: the fact is an *additive* mirror alongside `.planning/graphs/` (never a replacement). `kg_backend`/`replace` are forward-declared and behave as `augment` today.\n\nCaptures are idempotent: deterministic drawer IDs + `check_duplicate` mean re-running the wave re-files the same content without duplication. On any error, skip and let the wave complete normally.\n"
+ },
+ "produces": [],
+ "consumes": [],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ }
+ ],
"gates": [
{
"capId": "drift",
@@ -1994,6 +2238,19 @@ const byLoopPoint = {
},
"verify:post": {
"steps": [
+ {
+ "capId": "mempalace",
+ "point": "verify:post",
+ "ref": {
+ "skill": "mempalace-capture"
+ },
+ "produces": [],
+ "consumes": [
+ "SUMMARY.md"
+ ],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ },
{
"capId": "nyquist",
"point": "verify:post",
@@ -2065,7 +2322,21 @@ const byLoopPoint = {
]
},
"ship:post": {
- "steps": [],
+ "steps": [
+ {
+ "capId": "mempalace",
+ "point": "ship:post",
+ "ref": {
+ "agent": "gsd-mempalace-curator"
+ },
+ "produces": [],
+ "consumes": [
+ "UAT.md"
+ ],
+ "when": "mempalace.enabled",
+ "onError": "skip"
+ }
+ ],
"contributions": [],
"gates": []
}
@@ -2081,6 +2352,16 @@ const configKeys = {
"workflow.post_planning_gaps": "gap-analysis",
"graphify.enabled": "graphify",
"intel.enabled": "intel",
+ "mempalace.enabled": "mempalace",
+ "mempalace.memory_mode": "mempalace",
+ "mempalace.wing": "mempalace",
+ "mempalace.recall_on_discuss": "mempalace",
+ "mempalace.recall_on_plan": "mempalace",
+ "mempalace.capture_artifacts": "mempalace",
+ "mempalace.mirror_kg": "mempalace",
+ "mempalace.cross_project_tunnels": "mempalace",
+ "mempalace.diary_journal": "mempalace",
+ "mempalace.auto_capture_hooks": "mempalace",
"workflow.nyquist_validation": "nyquist",
"workflow.pattern_mapper": "pattern-mapper",
"profile-pipeline.enabled": "profile-pipeline",
@@ -2159,6 +2440,71 @@ const configSchema = {
"default": false,
"description": "Enable the intel code-intelligence command."
},
+ "mempalace.enabled": {
+ "owner": "mempalace",
+ "type": "boolean",
+ "default": false,
+ "description": "Master toggle for the MemPalace memory capability."
+ },
+ "mempalace.memory_mode": {
+ "owner": "mempalace",
+ "type": "enum",
+ "default": "augment",
+ "description": "How MemPalace relates to GSD native memory. Only 'augment' (additive) is implemented today; 'kg_backend' and 'replace' are forward-declared (routing seam not yet built) and currently behave as 'augment'.",
+ "values": [
+ "augment",
+ "kg_backend",
+ "replace"
+ ]
+ },
+ "mempalace.wing": {
+ "owner": "mempalace",
+ "type": "string",
+ "default": "",
+ "description": "Palace wing name; empty derives from project_code / project dir."
+ },
+ "mempalace.recall_on_discuss": {
+ "owner": "mempalace",
+ "type": "boolean",
+ "default": true,
+ "description": "Inject wake-up + search recall at discuss:pre."
+ },
+ "mempalace.recall_on_plan": {
+ "owner": "mempalace",
+ "type": "boolean",
+ "default": true,
+ "description": "Produce MEMORY-RECALL.md at plan:pre."
+ },
+ "mempalace.capture_artifacts": {
+ "owner": "mempalace",
+ "type": "boolean",
+ "default": true,
+ "description": "File CONTEXT/PLAN/SUMMARY and learnings into the palace at phase boundaries."
+ },
+ "mempalace.mirror_kg": {
+ "owner": "mempalace",
+ "type": "boolean",
+ "default": true,
+ "description": "Mirror decisions/learnings into MemPalace's temporal knowledge graph."
+ },
+ "mempalace.cross_project_tunnels": {
+ "owner": "mempalace",
+ "type": "boolean",
+ "default": false,
+ "description": "Propose/create cross-wing tunnels at ship:post."
+ },
+ "mempalace.diary_journal": {
+ "owner": "mempalace",
+ "type": "boolean",
+ "default": true,
+ "description": "Write a per-agent diary entry at ship:post."
+ },
+ "mempalace.auto_capture_hooks": {
+ "owner": "mempalace",
+ "type": "boolean",
+ "default": false,
+ "description": "Reserved / not yet implemented: will install MemPalace's native stop/precompact Claude Code hooks for passive mid-session capture (the capability's hooks array is currently empty)."
+ },
"workflow.nyquist_validation": {
"owner": "nyquist",
"type": "boolean",
@@ -3206,6 +3552,10 @@ const capabilityClusters = {
"graphify": [
"graphify"
],
+ "mempalace": [
+ "mempalace-capture",
+ "mempalace-recall"
+ ],
"nyquist": [
"validate-phase"
],
@@ -3240,6 +3590,12 @@ const profileMembership = {
"full"
]
},
+ "mempalace": {
+ "tier": "full",
+ "profiles": [
+ "full"
+ ]
+ },
"nyquist": {
"tier": "full",
"profiles": [
@@ -3286,6 +3642,7 @@ const _requiresGraph = {
"intel": [],
"kilo": [],
"kimi": [],
+ "mempalace": [],
"nyquist": [],
"opencode": [],
"pattern-mapper": [
diff --git a/gsd-core/bin/shared/model-catalog.json b/gsd-core/bin/shared/model-catalog.json
index 8fad7e216..308f1bcc8 100644
--- a/gsd-core/bin/shared/model-catalog.json
+++ b/gsd-core/bin/shared/model-catalog.json
@@ -153,6 +153,7 @@
"gsd-eval-planner": { "golden": "opus", "balanced": "opus", "budget": "sonnet", "phaseType": "planning", "routingTier": "heavy" },
"gsd-framework-selector": { "golden": "opus", "balanced": "sonnet", "budget": "sonnet", "phaseType": "planning", "routingTier": "heavy" },
"gsd-intel-updater": { "golden": "opus", "balanced": "sonnet", "budget": "haiku", "phaseType": "research", "routingTier": "light" },
+ "gsd-mempalace-curator": { "golden": "sonnet", "balanced": "sonnet", "budget": "haiku", "phaseType": "research", "routingTier": "light" },
"gsd-security-auditor": { "golden": "opus", "balanced": "sonnet", "budget": "sonnet", "phaseType": "verification", "routingTier": "heavy" },
"gsd-user-profiler": { "golden": "opus", "balanced": "sonnet", "budget": "sonnet", "phaseType": "research", "routingTier": "heavy" }
}
diff --git a/gsd-core/workflows/help/modes/full.md b/gsd-core/workflows/help/modes/full.md
index 052626f54..59270a8ae 100644
--- a/gsd-core/workflows/help/modes/full.md
+++ b/gsd-core/workflows/help/modes/full.md
@@ -606,6 +606,8 @@ The commands above cover the most common day-to-day flows. Every command listed
### Knowledge & Context
- **`/gsd:graphify [build|query |status|diff]`** — Build, query, and inspect the project knowledge graph in `.planning/graphs/`.
+- **`/gsd:mempalace-recall`** — Recall prior decisions, patterns, and surprises from MemPalace before planning.
+- **`/gsd:mempalace-capture [artifact-type]`** — File a phase artifact into MemPalace and mirror decision facts into its temporal KG.
- **`/gsd:thread [list [--open|--resolved] | close | status | name | description]`** — Manage persistent context threads for cross-session work.
- **`/gsd:profile-user [--questionnaire] [--refresh]`** — Generate developer behavioral profile and create Claude-discoverable artifacts.
- **`/gsd:stats`** — Display project statistics: phases, plans, requirements, git metrics, and timeline.
@@ -626,7 +628,7 @@ The commands above cover the most common day-to-day flows. Every command listed
These six skills exist primarily for the model to perform two-stage hierarchical routing across 60+ skills. You can invoke them directly when you want to browse a category interactively.
-- **`/gsd-context`** — Codebase intelligence routing (map, graphify, docs, learnings).
+- **`/gsd-context`** — Codebase intelligence routing (map, graphify, docs, learnings, mempalace).
- **`/gsd-ideate`** — Exploration / capture routing (explore, sketch, spike, spec, capture).
- **`/gsd-manage`** — Configuration and workspace routing (workstreams, thread, update, ship, inbox).
- **`/gsd-project`** — Project-lifecycle routing (milestones, audits, summary).
diff --git a/gsd-core/workflows/ship.md b/gsd-core/workflows/ship.md
index 317abd8bd..bbb9c9d8e 100644
--- a/gsd-core/workflows/ship.md
+++ b/gsd-core/workflows/ship.md
@@ -13,6 +13,11 @@ Create a pull request from completed phase/milestone work, generate a rich PR bo
Read all files referenced by the invoking prompt's execution_context before starting.
+
+Valid GSD subagent types (use exact names — do not fall back to 'general-purpose'):
+- gsd-mempalace-curator — Ship-time MemPalace curation (diary, KG mirror, cross-project tunnels, wing-scoped prune); dispatched at ship:post when the mempalace capability is enabled.
+
+
@@ -397,6 +402,32 @@ gsd_run query commit "docs(${padded_phase}): ship phase ${PHASE_NUMBER} — PR #
```
+
+
+> Capability-driven dispatch. Resolves active `ship:post` hooks via the capability registry; each hook's `when` is evaluated by the registry — no inline `config-get`. All `ship:post` hooks are post-ship and additive (`onError: skip`); a failure here never affects the already-created PR.
+
+```bash
+SHIP_POST_HOOKS_JSON=$(gsd_run loop render-hooks ship:post --raw)
+```
+
+Read the `activeHooks` array directly from `SHIP_POST_HOOKS_JSON` in-context (do NOT pipe it through a shell parser).
+
+**Branch 1 — no active `ship:post` step hooks (`activeHooks` has no entry with `kind == "step"`):** Skip silently to the report.
+
+**Generic step hook dispatch contract:** For each active entry where `kind == "step"`:
+- Honor `consumes`: if it lists `UAT.md`, resolve `ls "${PHASE_DIR}"/*-UAT.md 2>/dev/null | head -1` and pass it to the dispatch; if a consumed artifact is absent, skip that hook.
+- If `ref.agent` is set, first show the spawn banner, then dispatch the agent named by `ref.agent` (use the exact `ref.agent` value as the subagent type — e.g. `gsd-mempalace-curator` — never `general-purpose`):
+
+ ```
+ ◆ Spawning ship:post capability agent... (runs in a subagent — no output until it returns, ~1–2 min; expected, not a freeze)
+ ```
+
+ `Agent(subagent_type=ref.agent, prompt="Ship-time capability hook for phase ${PHASE_NUMBER}. Phase dir: ${PHASE_DIR}. Consume: ${consumed_files}. Follow your agent instructions.", model="{balanced_model}")`
+- If `ref.skill` is set, dispatch with `Skill(skill="gsd-${ref.skill}", args="${PHASE_NUMBER} --auto ${GSD_WS}")` (prepend `gsd-` to `ref.skill`).
+
+Each dispatch is best-effort: if it errors, record a warning and continue — never re-raise (`onError: skip`).
+
+
```
───────────────────────────────────────────────────────────────
diff --git a/src/clusters.cts b/src/clusters.cts
index 0e55d44fd..b0e606739 100644
--- a/src/clusters.cts
+++ b/src/clusters.cts
@@ -132,6 +132,8 @@ export const CLUSTERS: ClusterMap = Object.freeze({
'review-backlog',
'debug',
'extract-learnings',
+ 'mempalace-recall',
+ 'mempalace-capture',
'surface',
]),
});
diff --git a/tests/agent-size-baseline.json b/tests/agent-size-baseline.json
index 30b0b83a2..55ab48258 100644
--- a/tests/agent-size-baseline.json
+++ b/tests/agent-size-baseline.json
@@ -18,6 +18,7 @@
"gsd-framework-selector.md": 6778,
"gsd-integration-checker.md": 15141,
"gsd-intel-updater.md": 18122,
+ "gsd-mempalace-curator.md": 4160,
"gsd-nyquist-auditor.md": 7245,
"gsd-pattern-mapper.md": 12487,
"gsd-phase-researcher.md": 40611,
diff --git a/tests/check-gap-analysis-plan-post-e2e.test.cjs b/tests/check-gap-analysis-plan-post-e2e.test.cjs
index 768a80bd9..f08955cf5 100644
--- a/tests/check-gap-analysis-plan-post-e2e.test.cjs
+++ b/tests/check-gap-analysis-plan-post-e2e.test.cjs
@@ -493,13 +493,14 @@ describe('resolveLoopHooks plan:post — pure function against real registry', (
assert.strictEqual(result.activeHooks[0].capId, 'gap-analysis');
});
- test('[happy] real registry byLoopPoint plan:post has exactly one gate and no steps or contributions', () => {
+ test('[happy] real registry byLoopPoint plan:post has 1 step (mempalace), 0 contributions, and 1 gate (gap-analysis)', () => {
const entry = realRegistry.byLoopPoint['plan:post'];
assert.ok(entry, 'plan:post must exist in byLoopPoint');
assert.ok(Array.isArray(entry.steps), 'steps must be an array');
assert.ok(Array.isArray(entry.contributions), 'contributions must be an array');
assert.ok(Array.isArray(entry.gates), 'gates must be an array');
- assert.strictEqual(entry.steps.length, 0, 'plan:post must have zero steps');
+ assert.strictEqual(entry.steps.length, 1, 'plan:post must have 1 step (mempalace capture)');
+ assert.strictEqual(entry.steps[0].capId, 'mempalace', 'plan:post step must be from mempalace');
assert.strictEqual(entry.contributions.length, 0, 'plan:post must have zero contributions');
assert.strictEqual(entry.gates.length, 1, 'plan:post must have exactly one gate');
assert.strictEqual(entry.gates[0].capId, 'gap-analysis');
diff --git a/tests/copilot-install.test.cjs b/tests/copilot-install.test.cjs
index 295bacb00..5b9cd5687 100644
--- a/tests/copilot-install.test.cjs
+++ b/tests/copilot-install.test.cjs
@@ -1438,6 +1438,7 @@ describe('E2E: Copilot full install verification', () => {
'gsd-framework-selector.agent.md',
'gsd-integration-checker.agent.md',
'gsd-intel-updater.agent.md',
+ 'gsd-mempalace-curator.agent.md',
'gsd-nyquist-auditor.agent.md',
'gsd-pattern-mapper.agent.md',
'gsd-phase-researcher.agent.md',
diff --git a/tests/enh-2790-skill-consolidation.test.cjs b/tests/enh-2790-skill-consolidation.test.cjs
index 206984b3e..9cb2f7486 100644
--- a/tests/enh-2790-skill-consolidation.test.cjs
+++ b/tests/enh-2790-skill-consolidation.test.cjs
@@ -45,6 +45,8 @@ const KNOWN_SKILLS = new Set([
'ingest-docs.md',
'manager.md',
'map-codebase.md',
+ 'mempalace-capture.md',
+ 'mempalace-recall.md',
'milestone-summary.md',
'mvp-phase.md',
'new-milestone.md',
diff --git a/tests/execute-wave-post-gate-pipeline-e2e.test.cjs b/tests/execute-wave-post-gate-pipeline-e2e.test.cjs
index 242facc83..dec5b4117 100644
--- a/tests/execute-wave-post-gate-pipeline-e2e.test.cjs
+++ b/tests/execute-wave-post-gate-pipeline-e2e.test.cjs
@@ -629,12 +629,14 @@ describe('F. Real registry execute:wave:post shape — guard against accidental
`ui.safety-gate onError must be 'halt'; got ${uiGate.onError}`);
});
- test('[happy] real registry: execute:wave:post has no steps and no contributions — pure gate point', () => {
+ test('[happy] real registry: execute:wave:post has no steps and 1 contribution (mempalace capture-problems) — gate point with mempalace contribution', () => {
const point = realRegistry.byLoopPoint['execute:wave:post'];
assert.strictEqual(point.steps.length, 0,
`execute:wave:post steps must be empty; got ${point.steps.length}`);
- assert.strictEqual(point.contributions.length, 0,
- `execute:wave:post contributions must be empty; got ${point.contributions.length}`);
+ assert.strictEqual(point.contributions.length, 1,
+ `execute:wave:post must have 1 contribution (mempalace); got ${point.contributions.length}`);
+ assert.strictEqual(point.contributions[0].capId, 'mempalace',
+ `execute:wave:post contribution must be from mempalace; got ${point.contributions[0].capId}`);
});
});
diff --git a/tests/loop-hooks-empty-points-e2e.test.cjs b/tests/loop-hooks-empty-points-e2e.test.cjs
index f78721b50..78530a676 100644
--- a/tests/loop-hooks-empty-points-e2e.test.cjs
+++ b/tests/loop-hooks-empty-points-e2e.test.cjs
@@ -585,7 +585,8 @@ describe('ship:post — real registry empty-resolution + resilience to registry
}
});
- it('[happy] resolveLoopHooks with real capability-registry at ship:post returns empty activeHooks and well-formed byLoopPoint entry', () => {
+ it('[happy] resolveLoopHooks with real capability-registry at ship:post returns empty activeHooks (mempalace step inactive by default) and well-formed byLoopPoint entry with 1 step', () => {
+ // mempalace.enabled defaults to false → step not activated with empty config
const resolved = resolveLoopHooks({ point: 'ship:post', registry: realRegistry, config: {} });
assert.strictEqual(resolved.point, 'ship:post');
assert.ok(Array.isArray(resolved.activeHooks));
@@ -593,7 +594,8 @@ describe('ship:post — real registry empty-resolution + resilience to registry
const entry = realRegistry.byLoopPoint['ship:post'];
assert.ok(entry, 'ship:post must be present in real registry byLoopPoint');
- assert.strictEqual(entry.steps.length, 0, 'ship:post must have 0 steps');
+ assert.strictEqual(entry.steps.length, 1, 'ship:post must have 1 step (mempalace curator)');
+ assert.strictEqual(entry.steps[0].capId, 'mempalace', 'ship:post step must be from mempalace');
assert.strictEqual(entry.contributions.length, 0, 'ship:post must have 0 contributions');
assert.strictEqual(entry.gates.length, 0, 'ship:post must have 0 gates');
});
@@ -665,12 +667,13 @@ describe('ship:post — real registry empty-resolution + resilience to registry
}
});
- it('[empty-resolution] ship:post byLoopPoint entry exists in the real registry with all three arrays empty — no capability has silently self-registered here', () => {
+ it('[happy] ship:post byLoopPoint entry exists in the real registry with 1 step (mempalace), 0 contributions, 0 gates', () => {
const entry = realRegistry.byLoopPoint['ship:post'];
assert.ok(entry, 'ship:post must be present in real registry byLoopPoint');
- assert.strictEqual(entry.steps.length, 0, 'ship:post must have 0 steps — accidental registration guard');
- assert.strictEqual(entry.contributions.length, 0, 'ship:post must have 0 contributions — accidental registration guard');
- assert.strictEqual(entry.gates.length, 0, 'ship:post must have 0 gates — accidental registration guard');
+ assert.strictEqual(entry.steps.length, 1, 'ship:post must have 1 step (mempalace curator step)');
+ assert.strictEqual(entry.steps[0].capId, 'mempalace', 'ship:post step must be from mempalace capability');
+ assert.strictEqual(entry.contributions.length, 0, 'ship:post must have 0 contributions');
+ assert.strictEqual(entry.gates.length, 0, 'ship:post must have 0 gates');
});
});
@@ -715,14 +718,13 @@ describe('CLI contract — missing/invalid point argument (shared across all 6 e
// SECTION 8: Parametric empty-point sweep across all 6 points (E2E regression guard)
// ─────────────────────────────────────────────────────────────────────────────
-describe('Parametric E2E sweep — all 6 empty points return correct envelope shape via real registry', () => {
+describe('Parametric E2E sweep — 5 empty points return correct envelope shape via real registry (ship:post excluded — mempalace registers 1 step there)', () => {
const EMPTY_POINTS = [
'discuss:pre',
'discuss:post',
'execute:pre',
'execute:wave:pre',
'verify:pre',
- 'ship:post',
];
for (const point of EMPTY_POINTS) {
diff --git a/tests/loop-hooks-verify-post-e2e.test.cjs b/tests/loop-hooks-verify-post-e2e.test.cjs
index eac5d5c9a..4f01d0e58 100644
--- a/tests/loop-hooks-verify-post-e2e.test.cjs
+++ b/tests/loop-hooks-verify-post-e2e.test.cjs
@@ -522,22 +522,22 @@ describe('verify:post — envelope shape pins Hyrum\'s Law contract', () => {
// ─── 10. Real registry byLoopPoint shape check (no drift guard) ───────────────
-describe('verify:post — real registry has exactly 3 steps and 0 contributions+gates', () => {
- test('[happy] realRegistry.byLoopPoint[verify:post] has 3 steps, 0 contributions, 0 gates', () => {
+describe('verify:post — real registry has exactly 4 steps and 0 contributions+gates', () => {
+ test('[happy] realRegistry.byLoopPoint[verify:post] has 4 steps, 0 contributions, 0 gates', () => {
const entry = realRegistry.byLoopPoint['verify:post'];
assert.ok(entry, 'verify:post must exist in registry');
- assert.strictEqual(entry.steps.length, 3,
- `Expected 3 steps at verify:post, got ${entry.steps.length}`);
+ assert.strictEqual(entry.steps.length, 4,
+ `Expected 4 steps at verify:post, got ${entry.steps.length}`);
assert.strictEqual(entry.contributions.length, 0,
`Expected 0 contributions at verify:post, got ${entry.contributions.length}`);
assert.strictEqual(entry.gates.length, 0,
`Expected 0 gates at verify:post, got ${entry.gates.length}`);
});
- test('[happy] registry steps at verify:post have correct capIds in order', () => {
+ test('[happy] registry steps at verify:post have correct capIds in order (mempalace→nyquist→security→ui)', () => {
const entry = realRegistry.byLoopPoint['verify:post'];
const capIds = entry.steps.map(s => s.capId);
- assert.deepEqual(capIds, ['nyquist', 'security', 'ui'],
- `Registry must have steps in nyquist→security→ui order, got ${JSON.stringify(capIds)}`);
+ assert.deepEqual(capIds, ['mempalace', 'nyquist', 'security', 'ui'],
+ `Registry must have steps in mempalace→nyquist→security→ui order, got ${JSON.stringify(capIds)}`);
});
});
diff --git a/tests/workflow-size-baseline.json b/tests/workflow-size-baseline.json
index 888cfc270..8c70aa6dd 100644
--- a/tests/workflow-size-baseline.json
+++ b/tests/workflow-size-baseline.json
@@ -69,7 +69,7 @@
"settings-advanced.md": 39621,
"settings-integrations.md": 15801,
"settings.md": 32133,
- "ship.md": 22534,
+ "ship.md": 24629,
"sketch-wrap-up.md": 14223,
"sketch.md": 19960,
"spec-phase.md": 23094,