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

43 lines
2.1 KiB
Markdown

# 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.