Skip to main content
Status: accepted · Version 1.0 · Filed 2026-04-18

SPEC-017 — prism_clone

One MCP verb that takes a project name and optional github_repo, auto-detects clone-vs-register-vs-bind, and terminates at the Plan #5 core invariant:

Runs host-side (not in the backend)

The backend runs in a Docker container in personal mode and cannot write to the host filesystem for cloning a NEW project outside the mounted repo. Therefore:
  • git clone executes on the host (same process as the MCP server).
  • Legacy-project detection probes the host filesystem.
  • Prism-side registration (bootstrap_project, register_project_paths, prism_sync_bios) is delegated to existing verbs.
This mirrors the _execute_manifest pattern from bootstrap_project — backend computes, host applies.

Signature

Mode detection

Evaluate project_dir on the host: “Prism-structured” = has PRISM.md OR context/_ACTIVE_CONTEXT.md OR plans/_TODO.md at repo root.

Terminal steps (all modes, unconditional on success)

  1. register_project_paths(project_dir) — idempotent allowlist injection.
  2. prism_sync_bios(pid, files="all", dry_run=False, force=False) — best-effort; if PRISM.md pre-flight blocks, surface in response but don’t fail the whole call.
  3. Legacy-project detection — returns migration_candidate: bool + legacy_signatures: list[str].

Return payload

Legacy-project signature probes (Wave A stub — Wave B does full mapper)

Return migration_candidate=True if ANY of these are found at project_dir:
  • context/_ACTIVE_CONTEXT.md
  • plans/_TODO.md
  • plans/_DELTAS.md
  • AGENTS.md containing the regex r"^# AGENTS\.md\s*$" as a legacy AGENTS.md header signature
  • A sibling Obsidian vault at <project_dir>/../<name>Vault/ or <project_dir>/.obsidian/
Each probed path that hit is returned in legacy_signatures. Wave A does NOT read the content or produce a migration plan.

Error shaping

  • DirExistsNotGit: dir present, no .git. Agent should escalate to the user.
  • NothingToClone: dir absent and github_repo absent.
  • GitCloneFailed: propagate git’s stderr in the error message.
  • BackendError: wrap any HTTPError from the backend with the call that failed.

Smoke battery (Wave A exit gate)

  1. fresh_clone — target dir absent, github_repo given, small repo. Expect mode=clone, PID created, paths registered, bios synced, migration_candidate=False.
  2. register_existing — target dir is a Prism project already on disk. Expect mode=register, PID resolved (or created via bootstrap register), allowlist updated, bios re-synced.
  3. bind_non_prism — target dir is a git repo with no Prism structure. Expect mode=bind, PID minted, scaffold manifest written, bios synced.
  4. error_exists_non_git — target dir exists, no .git. Expect DirExistsNotGit. No side effects.

Exit gate for Wave A

All 4 smoke cases green; verb callable end-to-end from Claude Desktop and Claude Code MCP surfaces; terminal-state invariant verified by post-call prism_start(pid) cold read.

Frank-decisions NOT silently resolved

  • FD-1 (Wave B): write path stays behind explicit user prompt, not auto-executed. (Affects Wave B migration integration — not Wave A.)
  • FD-2/FD-3: n/a for Wave A.

Dependencies

  • Existing: bootstrap_project, register_project_paths, prism_sync_bios (all shipped).
  • New: host-side git subprocess, legacy-project detector stub, mode-detection branching.
  • Not new: no backend schema change, no new route, no alembic migration for Wave A.
Last modified on April 22, 2026