> ## Documentation Index
> Fetch the complete documentation index at: https://prism.ntecdev.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Plan #5: Cross-machine clone + DFW migration + project symmetry + ambient memory

> Cross-machine clone + DFW migration + project symmetry + ambient memory

<Info>**Status:** `active` · **Version** `1` · Filed 2026-04-18</Info>

# Plan #5 — Cross-machine clone + DFW migration + project symmetry + ambient memory

## Status: PENDING — awaiting Frank's GO

Filed 2026-04-18 from the "cloning gap" discovery session (Lola / Claude.ai project).
Author: Donna (external reviewer voice). Gated on Plan #4 Wave A activation so we don't interleave migrations with the server1 cutover.

## Origin story

Frank attempted to clone an existing project (MemRGR) from his Windows machine to a new machine and found there was no Prism-native path for it. The discovery surfaced three adjacent gaps and one orthogonal one:

1. **No `prism_clone` primitive** — cloning a git-hosted project onto a machine, placing it under `$PROJECT_ROOT`, registering it with Prism, and wiring it into the local filesystem-MCP allowlist is a 4-step manual dance with no single verb.
2. **No DFW→Prism migration path** — projects like MemRGR carry legacy history in Obsidian markdown under the deprecated DFW process. That history is not currently accessible via `semantic_recall` and is not migrated on clone.
3. **Claude Desktop project ↔ Prism project ↔ directory symmetry is not guaranteed** — creating a Claude Desktop project does not automatically create its Prism PID or its `$PROJECT_ROOT/<name>/` directory. The three identities can drift from birth.
4. **No ambient-memory capture when not in a project** — noodling outside any PID scope has no clean landing pad; content evaporates or gets mis-attached to whatever project happens to be open.

Plan #5 addresses all four under one spine because they share a critical invariant: **every path to "project exists" must terminate at the same state — filesystem dir + (optionally) git repo + Prism PID + MCP allowlist entry + BIOS synced.** Fixing them in isolation would allow the invariant to be half-implemented by each surface.

## Core invariant (architectural through-line)

For every project Prism manages, these five facts must be simultaneously true:

```
dir_exists($PROJECT_ROOT/<name>)      AND
(optional) git_repo($PROJECT_ROOT/<name>)  AND
prism_pid_exists(<name>, project_dir) AND
mcp_allowlist_contains($PROJECT_ROOT/<name>) AND
bios_synced(<pid>)
```

Any verb, hook, or agent action that creates, clones, or binds a project must end in this terminal state. Any verb that destroys a project must unwind it symmetrically. Waves A, C, and (to a lesser degree) B all re-use this invariant.

## Frank's answers from the shaping session (2026-04-18)

* **Q1 (Wave A):** `prism_clone` supports BOTH modes — auto-detect. If `$PROJECT_ROOT/<name>` exists, register-mode (bootstrap\_project strategy=register/join). If not, clone-mode (agent runs git clone first). Same terminal state either way.
* **Q2 (Wave B):** DFW detection automatic when a DFW vault is found in or near the repo. Migration pass triggered as part of the same clone call (still worth dry-run-preview before destructive writes — see Wave B body).
* **Q3 (Wave C):** Claude Desktop is the preferred driver for project creation but the mechanism is convention-based, not hook-based: first-session-on-missing-PID triggers `bootstrap_project` automatically. Any Prism-enabled agent (Claude Code, Cursor, Codex) implements the same convention so Desktop-first and agent-first entry points converge on identical state.

## Waves

### Wave A — `prism_clone` primitive (keystone)

Goal: one MCP verb that takes a project name and optional github\_repo, auto-detects clone-vs-register, and terminates at the core invariant.

A1. SPEC-017 — `prism_clone` verb contract (see spec filing alongside this plan).
A2. Backend implementation — route, service, input validation.

* Signature: `prism_clone(name, github_repo=None, project_dir=None, ptype="application", branch=None)`
* If `project_dir` omitted, default to `$PROJECT_ROOT/<name>` (already the registry convention).
* Check `isdir(project_dir)`:
  * exists + is git repo: register-mode → bootstrap\_project(strategy="register" if has-Prism-structure else "bind")
  * exists + not git: error (ambiguous — refuse to auto-init)
  * not exists + github\_repo given: clone-mode → `git clone {github_repo} {project_dir}` then bootstrap\_project(strategy="join")
  * not exists + no github\_repo: error
    A3. Call `register_project_paths(project_dir)` unconditionally at end of successful clone/bind.
    A4. Call `prism_sync_bios(pid, files="all")` at end to ensure CLAUDE/AGENTS/PRISM replicas are current against SOR.
    A5. Return structured payload: `{pid, project_dir, mode: "clone"|"register", git_result, sync_result, migration_candidate: bool}`. The `migration_candidate` flag is Wave B's entry point — true when DFW vault detected.
    A6. MCP server exposure — wire `prism_clone` as a tool in mcp/server.py with the standard error-shaping pattern.
    A7. Smoke tests — 4 cases: fresh clone, register existing, git-but-no-Prism-structure (bind), failure mode (dir exists non-git).
    A8. Exit: Verb callable end-to-end from Claude Desktop and Claude Code MCP surfaces. Terminal-state invariant verified by post-call `prism_start` cold-read.

### Wave B — DFW vault detection + migration

Goal: when `prism_clone` succeeds and DFW signatures are detected, offer migration; execute in dry-run-first, write-on-confirm mode.

B1. DFW signature detector — probes for: `AGENTS.md` with legacy DFW constitution header, `context/_ACTIVE_CONTEXT.md`, `plans/_TODO.md`, `plans/_DELTAS.md`, co-located Obsidian vault with `projects/<name>/_index.md`. Returns structured detection payload listing what was found and where.
B2. DFW artifact mappers — per artifact type, define the mapping:

* `plans/_TODO.md` → Prism `todos` (status-aware; done items migrated as done)
* `plans/_DELTAS.md` entries → Prism `session_deltas`
* `context/_DECISIONS_LOG.md` → Prism `decisions` (ADRs, status=accepted where clear, proposed otherwise)
* Obsidian retro notes → Prism `retrospectives`
* Obsidian plan notes → Prism `plans`
* Misc notes → Prism `notes` with source-path tag
  B3. Provenance discipline — every migrated row gets:
* `migration_source = "dfw_vault"` tag
* `source_file` = original path
* `source_created_at` = original file mtime or frontmatter date (if parseable)
* `created_at` = migration timestamp (honest about when the row landed in Prism)
* Legacy PID preserved in a `legacy_pid` field or tag — never silently renamed
  B4. `prism_migrate_dfw(pid, vault_path, dry_run=True)` verb — default dry-run, prints the full migration plan (N plans, M retros, K decisions, L todos, P deltas) with per-item diff summary. No writes until `dry_run=False`.
  B5. Migration integrates with Wave A — when `prism_clone` returns `migration_candidate=True`, agent offers: "DFW vault detected at `<path>`. Migrate now? (dry-run first)" — never automatic write.
  B6. Backend schema — ensure `legacy_pid`, `source_file`, `source_created_at`, `migration_source` are either first-class columns or queryable tags across the affected tables (todos, session\_deltas, decisions, retrospectives, plans, notes). May need a small Alembic migration.
  B7. Smoke test — use a copy of MemRGR's DFW vault as the test fixture. Migrate, then `semantic_recall("MemRGR ingestion architecture")` against the new PID and confirm historical context surfaces with clear provenance.
  B8. Exit: migrated project's history is readable via `semantic_recall` with source\_file tags intact; no fabricated timestamps; `legacy_pid` preserved and queryable.

### Wave C — Claude Desktop project ↔ Prism symmetry (convention-based)

Goal: creating a project in Claude Desktop (or starting work in one that lacks a PID) automatically produces the three-way binding. No platform hooks required — agent convention.

C1. Authoring update to `templates/prism-base.md` and AGENTS.md — document the "first-session-on-missing-PID" convention. When `prism_start(pid="")` returns no resolution AND the current dir looks like a project root (heuristic: has `.git/` or an AGENTS.md or a CLAUDE.md), the agent MUST prompt the user: "No Prism PID for this directory. Bootstrap now? (name=`<dir basename>`)"
C2. `prism_start` payload enhancement — when pid resolution fails, include `suggested_bootstrap: {name, project_dir, reason}` so the agent has a structured prompt target.
C3. Agent-side enforcement — AGENTS.md rule addition: "Never write to Prism without a resolved PID. If none resolves, bootstrap or ask."
C4. New verb or strategy extension — `bootstrap_project` already covers this; the gap is the agent-convention layer above it. Verify that calling `bootstrap_project` from within a Claude Desktop project session produces the same terminal state as calling it from Claude Code or Cursor.
C5. Document the convention in AGENTS.md §X "Project entry protocol" with explicit examples for each editor surface.
C6. Smoke test — create a fresh Claude Desktop project named `TestSymmetry`, open a session, verify the agent detects no-PID and offers bootstrap, accept, verify terminal state (dir exists at `$PROJECT_ROOT/TestSymmetry`, PID resolves, MCP allowlist contains the path, BIOS synced).
C7. Repeat C6 from Claude Code and Cursor entry points — same terminal state, same convention, different surface.
C8. Exit: three different agent surfaces produce identical terminal state when entering an unbootstrapped project.

### Wave D — Ambient memory capture (noodling)

Goal: first-class write target for un-scoped content plus a triage verb for promoting inbox → PID or global.

D1. `prism_remember(scope="personal", text, tags=[])` — extend the existing remember verb to accept a `scope` parameter. `scope="personal"` writes to namespace `global:personal` with no PID. Semantic\_recall already supports the `namespace` filter per ADR #4.
D2. `prism_capture(text, session_context={}, tags=[])` — new verb. Writes to namespace `inbox:personal` with session context (timestamp, originating agent, originating chat/project context if any). No PID required. This is the "I don't know where this goes yet but I don't want to lose it" bucket.
D3. `prism_triage(action, inbox_id, target_pid=None, target_scope=None)` — new verb. action ∈ {promote_to_pid, promote_to_personal, discard}. Promotes an inbox item to a PID namespace or `global:personal`, or discards it. Never automatic. Matches the global\_fact\_candidates pattern from ADR-019.
D4. Backend schema — `inbox_items` table with id, tenant\_id, text, session\_context jsonb, tags, status (open/triaged/discarded), created\_at, triaged\_at, triaged\_to\_pid, triaged\_to\_scope. Alembic migration.
D5. `prism_inbox(action="list", status_filter="open")` — list verb for reviewing untriaged items. Mirrors the prism\_todo list pattern.
D6. Recall integration — `semantic_recall` already supports namespace filtering; verify `inbox:personal` and `global:personal` are queryable with no PID required.
D7. AGENTS.md rule addition — when user says "remember this" outside a PID scope, agent should offer: "Capture to personal notes, or to inbox for later triage?" Never silently pick.
D8. Smoke test — capture 3 items ambiently, list them, triage 1 to a PID, 1 to personal, discard 1. Verify all three flows end at correct terminal state and are recallable via semantic\_recall with appropriate namespace.
D9. Exit: ambient capture + triage loop works end-to-end; no content lost when working outside project scope.

### Wave E — Dogfood: clone MemRGR end-to-end

Goal: the falsification test. The whole plan earns green only if Wave E produces a working Prism-native MemRGR with its DFW history migrated and recallable.

E1. On new machine: user says "Claude, clone MemRGR from `<git URL>`" in a Claude Desktop session (not yet bound to any Prism PID).
E2. Agent calls `prism_clone(name="MemRGR", github_repo="{url}")`.
E3. Clone-mode triggers, git clone completes, `bootstrap_project(strategy="join")` runs, PID-MRGxx is created, paths registered, BIOS synced.
E4. Return payload flags `migration_candidate=True` because DFW vault detected.
E5. Agent offers DFW migration, user approves, dry-run runs, preview shown, user confirms, write executes.
E6. Post-migration: `prism_start PID-MRGxx` returns structured context showing migrated TODOs, retros, decisions, plans with provenance tags.
E7. `semantic_recall("Learn Loop Phase 2 rationale", pid="PID-MRGxx")` returns migrated content with source\_file references.
E8. Retro on Plan #5 captures what felt natural vs forced. Candidate G-scope lessons — especially around provenance, dry-run-first discipline, and the agent-convention symmetry layer.
E9. Exit: MemRGR lives in Prism as a first-class project with history intact and honestly provenanced. If this fails, the plan hasn't shipped — no matter what Waves A–D did in isolation.

## Dependencies

* **Plan #4 Wave A must be GREEN before Plan #5 starts.** Server1 cutover and this migration work should not interleave. Frank has not yet signed off on Plan #4 Wave A activation — this plan sits behind that gate.
* **TODO #48 (ORG.md override semantics) is not a blocker** but its resolution shapes how per-user "personal scope" content interacts with org-wide overrides. Worth surfacing again when Wave D lands.
* **TODO #61 (retrieval-quality deep-dive)** is parallel but independent — Plan #5 writes more data, retrieval-quality work improves how the data is queried. No ordering constraint.

## Frank-decisions embedded (do not let agents silently resolve)

* **FD-1:** confirm auto-migration trigger in Wave B is "detect then PROMPT for dry-run," not "detect then automatically run dry-run + execute." The word "automatic" in Frank's answer to Q2 covers detection; the write path should still be confirmed. Surface on Wave B kickoff.
* **FD-2:** Wave D's `global:personal` namespace — single-user (Frank) or multi-user (each driver gets their own)? Today Prism is single-tenant single-user; this plan inherits that. Flag for revisit if ever multi-user.
* **FD-3:** legacy PID handling — store as tag (`legacy-pid-MRG001`) or first-class column (`legacy_pid`) on migrated rows? Tag is zero-schema-change; column is queryable without recall. Surface on Wave B kickoff.

## Out of scope for this plan

* Multi-user ambient memory (Wave D is single-user).
* Auto-detecting when a Claude Desktop project is *renamed* and keeping the Prism side in sync (one-way trigger only; rename is out of scope).
* Migrating from sources other than DFW Obsidian vaults (Notion, plain-file projects, etc.) — explicit extension work if ever needed.
* `prism_destroy` — the symmetric unwind verb. Probably wanted eventually but not this plan.
* Connecting MemRGR's *graph RAG capability* to Prism as a retrieval leg (that's TODO #61 territory — separate thread).

## Exit criteria for Plan #5 as a whole

* Wave A: `prism_clone` callable from 3 agent surfaces, both modes work, terminal-state invariant holds.
* Wave B: MemRGR-shaped DFW vault migrates into a Prism project with provenance tags intact and no fabricated timestamps; dry-run-first discipline enforced.
* Wave C: creating a project from Claude Desktop OR Claude Code OR Cursor produces identical terminal state via agent convention.
* Wave D: ambient capture + triage loop closes the "noodling" gap; nothing is lost when working outside PID scope.
* Wave E: clone MemRGR end-to-end on a real machine, migrate history, retro captures lessons. This is the falsification test — plan ships only when Wave E is green.

## Artifacts to produce

* SPEC-017 — `prism_clone` verb contract (filed alongside this plan)
* Alembic migration(s) for inbox\_items, provenance columns on migrated tables
* AGENTS.md §X "Project entry protocol" — the agent-convention layer from Wave C
* Updates to `templates/prism-base.md` reflecting the entry-protocol rules
* Retro document after Wave E

— Filed 2026-04-18 from the cloning-gap shaping session.
