facets.json, facets.lock, and the install receipt together. A removal that local state already answers for skips resolution entirely.
facet add, facet remove, and facet install all run this same pipeline — they differ only in the delta they produce.
Before the pipeline runs, every installed adapter’s declared adapter API MUST be inspected against the CLI’s supported set. An incompatible or broken installed adapter MUST fail the operation before planning begins — before any adapter method is invoked and before any project write — with per-adapter diagnostics and the best available reinstall command for each entry. The commit phase then re-checks the adapters selected for the operation as defense-in-depth: the same incompatibility MUST fail at Load state and gate, before any facet is resolved and therefore before any Git or local facet build can invoke adapter metadata methods.
The adapter API, the archive facetVersion, the project manifest version, and the lockfile version are four independent version axes, each classified separately by exact match. They happen to share release trains; they are not one version in four places. The adapter-compatibility preflight runs before archive-version dispatch, so a positional 0.0 adapter fails on the adapter axis — with reinstall guidance — before a facet’s facetVersion is even examined.
Project state MUST NOT be written unless every check passes. Commit’s first two phases are read-only, so a failure there — including an unresolved name collision or a cancelled resolution — leaves the project byte-identical with nothing to undo. From the first write onward, a failure MUST roll back all materialization via the journal and leave the project exactly as it was.
What install materializes — and what it does not — is a first-class part of the contract:
- Materialization boundary. Skill companion files materialize atomically with their skill; every other supplementary file (top-level
README.md,LICENSE, extras beside agents or commands) ships in the archive but is never written to disk. Detail in Commit — Materialize. - Project-chosen names. A project MAY materialize an asset under a different name, or not at all. The asset is still resolved, verified, and recorded either way — only the file on disk changes. See Materialization.
- Global collision detection. Two facets cannot claim one name. The complete desired asset set is checked before the first write, so a collision stops the install with nothing changed. See Commit — Compose.
- Per-file integrity and drift. On reproduction, install reconciles each file against its lockfile integrity before writing and reports the exact drifting path; a drifted companion is repaired by replacing its skill’s bundle atomically.
- Unsupported archive versions. An archive whose
facetVersionthe CLI does not support fails with a structured error the CLI renders as upgrade guidance — a known newer format names the minimum supporting release, an unknown format says update to the latest.
Planning
Phase 1 — build the delta. No version resolution, no lockfile reads, no project mutation.
Commit
Phase 2 — the transaction. Resolve every facet (or refine a removal from local state), compose one global plan, then delete drift and materialize; write atomically.
The project manifest MUST NOT be written ahead of the install. A failed operation MUST leave the project exactly as it was.