# Contract: Apply Adapter (v1, frozen) — the fedlearn integration seam The ONLY interface through which anything installs a Solution bundle onto a machine. Freezing this seam is what lets the wedge ship before the fedlearn rails land (research.md R5) and swap them in later without touching CLI or Store. ## Protocol (Python, `cli/src/m2_market/apply/base.py`) ```python class ApplyAdapter(Protocol): name: str # "local" | "m2core-sync" def plan(self, bundle: Path, state: InstallState) -> Changeset: ... def apply(self, changeset: Changeset) -> ApplyResult: ... # idempotent def verify(self, bundle: Path, state: InstallState) -> bool: ... def rollback(self, changeset: Changeset) -> None: ... # reversible (constitution VI) ``` - `Changeset`: declarative list of file placements / config merges / post-install checks derived from the bundle's `recipe.yaml`, plus a `changeset_hash` recorded in InstallState. - `apply` MUST be idempotent (re-apply of an applied changeset = no-op success) and MUST NOT require elevated fleet-wide operations (canary principle). - `rollback` restores pre-apply state from the changeset's recorded backups. ## v1 implementations ### `local` (ships in this wedge) Applies `payload/` into the operator's agent-home per `recipe.yaml` targets (skills, prompts, configs, scripts), runs recipe post-install checks, records InstallState. Works identically inside primus and agent-latest desktops (targets are volume paths, not image paths) — satisfying "runtime-sync is the only universal path" in spirit until the real one exists. ### `m2core-sync` (stub in this wedge) Raises `RailsNotLanded` (CLI exit 6) with pointer to this contract. When fedlearn's `m2-core-sync`/`m2-core pull --apply` lands, this adapter shells to it and merges its state.json with InstallState. Acceptance for the swap (row 10 of plan sequencing): all quickstart.md scenarios pass with `adapter = m2core-sync` unchanged. ## Selection `config.toml: apply_adapter = "local"` (default in v1). Per-install override `--adapter` for testing. The Store never selects an adapter — it inherits the CLI's.