Canonical Home & cmx doctor
Status: Living design document. Published in the open on purpose — it records why cmx grows a canonical home for hand-authored artifacts and a system-survey command, so users and contributors can see the design goals and trade-offs rather than reverse-engineering them. Last substantive update: 2026-05.
Why this document exists
cmx already projects a curated set of artifacts outward into many tools'
native locations (see Multi-Tool Platform Support).
But it assumed the curated set already lived somewhere managed — a git-backed
marketplace or a tidy local source. Real systems don't start there. They start
as a pile of hand-authored skills and agents inside whatever tool you adopted
first (for many people, ~/.claude/), with no provenance, no checksums, and no
way to move them to the next tool without copy-paste.
Two forces made this urgent:
- Tool portability is now a survival requirement, not a nicety. A coding assistant's licensing or pricing can change such that you must move your automation to a different tool on a deadline. When that happens, your private skills must not be hostage to the tool you're leaving.
- The "curated set" has to have a home that outlives any one tool. If the
source of truth is
~/.claude/skills, then dropping Claude Code means losing the source of truth. The home must be tool-neutral.
This note records the model we chose, the classification cmx doctor reports,
the adoption mechanics, and the scope boundaries.
Design principles (how this extends the existing ones)
This work is a direct application of two commitments already stated in the platform-support design doc, plus one addition:
- One curated set, projected outward. (existing) The canonical home is that curated set, made explicit and tool-neutral. Today's gap was that the set had no first-class home; you projected outward from an accident of history.
- Provenance and integrity are first-class. (existing) Adoption is the act of
giving an un-provenanced artifact provenance: a checksum, a recorded version,
a home.
doctoris how you see provenance (or its absence) across the whole system. - Diagnose before you mutate. (new)
doctoris read-only by contract. You see the full truth of a disorganized system — what's tracked, drifted, orphaned, missing, duplicated — before any command changes a byte. Adoption is a separate, explicit step.
The model
Canonical home
A first-class local directory that holds your hand-authored private agents and skills. It is the authoritative source of truth for artifacts you wrote yourself (as opposed to artifacts you pulled from a remote marketplace).
- Location:
~/.config/context-mixer/homeby default — inside cmx's existing config root, alongsidesources.jsonand the lockfiles — overridable via thehomefield inconfig.json. (We deliberately reuse the established config directory rather than invent a new~/.cmx/tree; cmx already owns~/.config/context-mixer/.) - Structure: a plain artifact tree —
agents/*.mdandskills/<name>/SKILL.md— with nomarketplace.jsonrequired. cmx's existing fallback tree-walking scanner already reads un-manifested repositories, so the home works withlist,install,search, andbrowseunchanged. cmf does not author or lint canonical-home artifacts. - First-class status: the home is auto-registered as a visible local
source named
home(created, and written intosources.json, on first use — e.g. the firstadopt). It then behaves like any registered source:list,install --all,search, andbrowsesee it, and it shows incmx source list. We deliberately chose a visible, explicit source over an invisible injected one: cmx's whole model is that sources are explicit, and keeping the home insources.jsonavoids special-casing the shared config-load path that every command runs through. (Trade-off: like any source, it can besource removed — re-runningadoptorcmx home initre-registers it.) ~/.claude/skillsis demoted. It stops being special. It becomes one install target among the thirteen platforms, on equal footing with opencode, codex, and hermes. The source of truth now survives dropping any single tool — which is the entire point.
cmx doctor — read-only stocktake
doctor surveys every platform's install directories (global, and --local
for project scope) and cross-references each per-platform lock file plus the
canonical home. For each artifact it reports one classification:
| State | Meaning | Typical cause |
|---|---|---|
| tracked | in a lock file, installed checksum matches | a normal cmx install |
| drifted | tracked, but the installed copy was edited after install | hand-tweaked in place |
| untracked | on disk, no lock entry, but a registered source provides it | installed out-of-band → track via install |
| orphaned | on disk, no lock entry, no source provides it | hand-authored artifacts (the ~/.claude/skills pile) → adopt |
| missing | in a lock file, but gone from disk | deleted out-of-band → uninstall clears it |
| diverged | the same logical artifact's copies have different content across locations | edited in one tool but not another |
A skill installed for several tools is one logical artifact listing those
tools — not N duplicates. The survey keeps raw per-location rows internally (adopt
needs them), but the user-facing view groups them by (kind, name, scope).
diverged is the only multi-location situation worth flagging, and it is
decided on content, representation-aware: copies in the same representation
are compared by checksum, while a copy on a transforming platform (Codex's
generated agent TOML) is compared through the Codex projection of the portable
Markdown copy. Reformatting alone is never divergence; an edited or stale TOML
— one that is not byte-for-byte what cmx would generate from the Markdown
sibling today — is.
The untracked vs orphaned split is what makes adoption safe: adopt and
--adopt-all act only on orphaned artifacts. An untracked artifact has a
real upstream source, so adopting it as private would duplicate source content
and lose provenance — it's steered to install instead. (Discovered the hard
way: a real system had a vendor tool's entire bundled-skill collection sitting
untracked on disk; a blanket adopt-all would have swept those defaults into the
personal home.)
doctor mutates nothing. It exits non-zero when anything is off (so it is
usable in a hook or CI later) and prints a grouped, human-readable report plus a
short "what to do next" footer (e.g. "12 orphaned skills — run
cmx doctor --adopt-all to bring them under management").
Adoption — orphaned → tracked
Adoption is the bridge from "disorganized pile" to "managed set":
cmx skill adopt <path>(andcmx agent adopt <path>) copies one orphaned artifact into the canonical home, normalizes its frontmatter (fillsname, defaultsversion: 0.1.0if absent), records its checksum, and registers it.cmx doctor --adopt-allsweeps every orphandoctorfound into the home in one pass.- Adoption copies; it never moves the original. After adoption the original
on-disk copy reclassifies from orphaned to tracked (its checksum now
matches the home). The destructive choice — deleting originals, or rewriting
them as managed installs — is left to the user via existing
uninstall/installcommands. This keeps adoption safe to run on a messy system.
Projection — already built
Once artifacts are in the home, projecting them to whatever tool you are moving
to needs no new code: the home is an authoritative source, so
cmx skill install --all --platform opencode (or codex, hermes, …) installs
the whole set into that tool's native location with full lockfile tracking.
The cutover ritual becomes:
cmx doctor # see the mess
cmx doctor --adopt-all # canonicalize the orphaned private artifacts
cmx skill install --all --platform <target> # project to the new tool
Architecture notes
The one non-trivial structural point: doctor surveys all platforms, but the
existing path/lock machinery is bound to a single active platform (ConfigPaths
carries one Platform, and lock_path / install_dir resolve against it). To
survey every platform without coupling, doctor:
- iterates
Platform::ALL(a new exhaustive slice of variants), and - for each platform derives a per-platform view of
ConfigPaths(thehome_dirandconfig_dirare platform-independent; onlyplatformchanges), then reuses the already-testedinstalled_names,lock_path, and lock-loading functions.
No platform-specific knowledge is duplicated into doctor; it composes the
existing pure functions per platform. This keeps the survey honest as new
platforms are added — a platform that appears in Platform::ALL is automatically
surveyed.
Scope decisions
| Decision | Choice | Rationale |
|---|---|---|
doctor mutation | Read-only by contract | Diagnose before you mutate. The only writing path is the explicit adopt / --adopt-all flag. |
| Adoption of originals | Copy, never move | Safe to run on a messy system; reclassifies originals to tracked without risking data loss. Deleting/migrating originals stays an explicit, separate user action. |
| Home structure | Plain tree, no manifest | The fallback scanner already reads un-manifested repos; requiring marketplace.json for purely-local private artifacts would be friction with no benefit. |
| Home location | ~/.config/context-mixer/home, config-overridable | Reuses cmx's existing config root (next to sources.json and the lockfiles) rather than inventing a new ~/.cmx/ tree. Still tool-neutral — it lives under cmx's own directory, not any single assistant's — so it outlives tool changes. |
| Home as a source | Visible, auto-registered local source | Consistent with cmx's explicit-source model; the home lives in sources.json and shows in source list, rather than being injected into the shared config-load path. Auto-registered on first adopt (or cmx home init); re-registers if removed. |
~/.claude/skills | Demoted to an install target | Decouples the source of truth from the tool being abandoned — the motivating requirement. |
Phasing
- Phase 1 —
doctor(read-only) — done. The cross-platform survey and classification, shipped first so the system state is visible before any model decision about the home was finalized. - Phase 2 — canonical home +
adopt— done. Theconfig.jsonhomefield, the auto-registered visiblehomesource,cmx home init/cmx home path,cmx {skill,agent} adopt <name>, andcmx doctor --adopt-all. Adoption copies verbatim into the home and recordshomeprovenance so orphans reclassify as tracked. Projection reuses the existinginstall --all --platform <tool>(the home is just a registered source) — no new code. - Phase 3 — reconciliation ergonomics — done (with a different shape than first
sketched). Rather than a projection convenience that fans the home out (still
just
install --all --platform X), the gap that proved worth closing was reconciling copies that have diverged across tools. Two commands cover it:cmx {skill,agent} promote <name>pushes in-place edits back into the home (the mirror ofupdate), andcmx skill sync <name>reconciles a skill between install locations by copying one copy over the others.doctor's divergence hint routes to whichever fits the artifact's provenance. See Promoting Local Edits and Reconciling Across Platforms.
Note on frontmatter normalization. An earlier sketch had
adoptdefault a missingversionto0.1.0. We deliberately don't: normalizing the home copy would make it differ from the verbatim original, so the original would show as drifted rather than tracked. Adoption copies byte-for-byte; version curation is a separate, explicit step.
Source code map
cmx/src/platform.rs—Platform::ALL(exhaustive variant slice) so the survey is automatically complete.cmx/src/paths.rs—ConfigPaths::with_platform(per-platform view) anddefault_artifact_home()(config_dir.join("home")— not the existinghome_dirfield, which is the OS home).cmx/src/types.rs— thehomefield onCmxConfig.cmx/src/config.rs—resolve_artifact_home(config, paths)(override or default).cmx/src/doctor.rs— the read-only cross-platform survey + classification (ArtifactState,DoctorRow,survey).cmx/src/adopt.rs— copy-into-home + per-platform lock registration +ensure_home_source(registers thehomesource so projection viainstall --allworks with no further setup). Backsadopt,doctor --adopt-all, andhome init/home path.book/src/reference/commands.md— documentsdoctor,adopt, and the home.