Skip to main content
This page defines what an install MUST guarantee, whatever implementation performs it. Adding, removing, updating, and reproducing are all installs; they differ only in what the project is asked to change. Command behavior, prompts, and output are documented at facet install.

Ordering

An install MUST resolve and verify the complete desired set, and MUST compose one plan over all of it, before writing anything. Composing globally is what makes collision detection possible: two facets claiming one name cannot be detected once the first is already on disk. See Materialization. Project state MUST NOT be written unless every check passes. A failure before the first write leaves the project byte-identical. From the first write onward, an implementation MUST record enough state to restore every file it changes. A failure MUST then restore text assets, native configuration documents, and project state to their exact prior bytes, except where a path was changed by something else after this operation wrote it, or cannot be read or written back. Such a path MUST be left as it stands rather than overwritten, and every path an implementation could not return to its prior bytes MUST be reported.

Resolve

Whether the lockfile is trusted for version resolution depends on how an entry was requested, not on a flag.
  • Implicit request. A non-exact specifier in an addition, such as a bare name, 1.*, or latest, MUST resolve to the newest matching version even when the lockfile already satisfies it. The user asked for that range now.
  • Explicit request. An exact specifier, and any reproduction of an already-declared facet, MUST reuse a satisfying lockfile entry. Only an absent or stale entry triggers resolution.
  • Reviewed update. A version chosen during an update’s read-only discovery MUST be installed as reviewed. Resolution MUST NOT be repeated at write time, so a release published in between cannot silently change what is installed.
Skipping the question never skips verification.

Verification

Verification obligations are defined in the Integrity Model. An install MUST apply them:
  • Cached content MUST be re-verified on every use, and content that fails MUST NOT be materialized.
  • When the lockfile pins a version, the verified integrity MUST equal the locked integrity. A mismatch MUST fail.
  • When a lockfile entry is created or replaced, the integrity MUST be confirmed against the registry’s published fingerprint. An unreachable registry MUST fail rather than record an unconfirmed entry.
  • A git source MUST verify its built content against the locked integrity, which is what detects a moved tag.
  • Failures MUST be reported under distinct checks, so a frozen local-source failure is never reported as a git failure.
On reproduction, an implementation MUST reconcile each file against its recorded per-file integrity before writing, and MUST report the exact drifting path.

Composition

The desired set is computed by the deterministic planner defined in Materialization. Text assets and MCP servers are planned in separate identity spaces. A collision MUST NOT be resolved by choosing a winner or inventing a name. An implementation MUST either collect the user’s decision, recorded as durable intent in facets.json, or report every claimant and stop.

MCP configuration

A new or changed MCP server declaration MUST be approved before it is materialized.
  • Approval MUST be keyed by the effective server name and the declaration’s canonical content, so an unchanged declaration does not ask again and any change to what would launch or connect does.
  • Interactive consumers MUST be shown the exact command, arguments, and environment assignments, or the exact URL, before anything is written. Non-interactive consumers MUST require an explicit opt-in.
  • Approval MUST be recorded only by a fully successful operation. A declined, failed, or rolled back run MUST bank nothing.
  • Approval MUST be machine-local. It MUST NOT be stored in version-controlled project state, because a committed file must not be able to claim that another machine consented to running something.
Two identical declarations at one effective name are not a conflict. They compose into one configuration retaining both claimants. Configuration is applied after text-asset writes and immediately before project state is written, which is the narrowest window in which a tool watching its own configuration could observe an entry a later failure would undo.

Ownership

Two rules govern every kind of state.
  • Desired state authorizes writes. Every desired identity is reconciled. When what is already present matches byte for byte, the write MAY be skipped, and the identity is still recorded as owned.
  • Recorded ownership authorizes deletion. Only an identity this machine recorded materializing may be deleted. A lockfile entry alone is not evidence, because a lockfile is shared state that can arrive from a teammate.
Ownership is recorded per project, not per adapter. Connecting an adapter delegates management of those identities inside it, so removing a facet removes it from every connected tool. An unusable ownership record is treated as an empty one: nothing is deleted, and the run records only what it reconciles. Records that fail validation MUST be reported, and the files they covered MUST be left in place. A name that no remaining contribution claims is vacated, and is deleted before any alias is written. An authored name that was never recorded is left alone.

Untracked destinations

A desired identity already occupied by state this machine does not own MUST be disclosed before it is adopted or replaced. Equivalent state MUST be adopted without rewriting it. Divergent state MAY be replaced once the user continues. State at an identity the desired set does not name MUST NOT be inspected or modified.

Manifest write policy

The value written to facets.json depends on the specifier shape, and is applied only as part of the final write. An update decides its manifest value from the choice the user reviewed. Taking a facet’s range target never rewrites the specifier, and the authored string is preserved rather than re-rendered. Taking the latest release widens the specifier by the smallest edit that admits it: Materialization overrides are carried across a version change untouched. An override naming something the new version no longer contains is pruned and reported. Comments in facets.json MUST survive a write.

Transactional tri-write

On success, a non-frozen install MUST write the project manifest, the lockfile, and the machine-local ownership record together. A failure anywhere MUST leave all three exactly as they were, and MUST restore every text asset and every native configuration document the operation touched, under the restoration rule in Ordering.

Frozen lockfile

Frozen mode treats the lockfile as authoritative for what to reproduce. It is not authority to delete: that stays with the machine-local record. Only a reproduction can run frozen. An addition, removal, or update has no frozen form. Frozen mode MUST NOT prompt, for a collision or for approval, though it MAY use approval supplied in advance. These gates MUST run before any network access, so a frozen install can refuse without fetching anything:
1

Coverage

Every manifest facet MUST have a satisfying lockfile entry, the lockfile MUST NOT pin anything the manifest dropped, and git and local sources MUST match their locked provenance.
2

Format capability

A lockfile version with no field for a text-asset disposition MUST fail when the manifest declares one, rather than ignore the intent. A server disposition never triggers this, because no lockfile version records server intent.
3

Plan over the locked set

The planner runs against the lockfile’s recorded text assets, so a collision or invalid alias in recorded state is reported before anything is downloaded.
4

Stale intent

An override naming a text asset the locked version does not contain, or a server the pinned facet does not declare, is blocking drift. Frozen mode reports it without removing it.
5

Intent against recorded disposition

For every locked text asset, what the manifest asks for MUST equal what the lockfile records. Editing an alias without a normal install is drift, exactly like editing a version.
MCP adds its own pre-mutation gates: an unresolved server collision, an adapter that cannot configure servers, an unparseable native document, or an unapproved declaration MUST fail before any write. These checks MAY need to fetch locked content, because declarations live in the facet rather than the lockfile. Once every check passes, frozen mode MAY reconcile native configuration and the machine-local record, including removing an owned entry the project no longer desires. It MUST NOT write the project manifest or lockfile. An otherwise successful frozen install MAY still succeed if its machine-local ownership record cannot be persisted. The implementation MUST report that failure and any incomplete restoration of the record, and MUST NOT treat unpersisted ownership or approval as recorded.

Version axes

The archive facetVersion, the project manifest version, and the lockfile version are independent axes. Each is classified by exact match against a supported set, never by numeric ordering, so an unrecognized version is refused rather than guessed at. An implementation that also supports adapters carries its own adapter SDK API axis on top of these. An archive whose facetVersion is unsupported MUST fail with the observed and supported versions, so a consumer can be told what to upgrade.

Boundaries

  • Text is never fetched or assembled at install time. Everything installed comes from the verified archive.
  • Skill companion files materialize with their skill. Every other supplementary file ships in the archive and MUST NOT be written to disk.
  • Materializing a declaration MUST NOT install, launch, probe, or authenticate to an MCP server, and MUST NOT store credentials.
  • An installer MUST NOT modify user-wide or system-wide tool configuration.