m2-market/specs/001-market-first-wedge/contracts/apply-adapter.md
m2 (AI Agent) 8757c6b620 factory-loop(plan): 001-market-first-wedge — plan, research, data model, 5 contracts, quickstart
Rail-independence is the plan's spine: fedlearn rails verified NOT landed
(no m2/m2-core, no capture/curator CLIs), so the wedge builds against frozen
interfaces — m2/market-registry as registry of record, ApplyAdapter seam with
local adapter now / m2core-sync stub, schemas frozen here with a fedlearn
coordination note. Constitution check passed (1 justified deviation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 02:15:24 +02:00

2.1 KiB

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)

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.