Skip to content

Assembly Lock, SkillBOM, and Replay

Turning an assembly into files on disk is a three-step, digest-gated pipeline: resolve (pure), preview (builds a plan, zero writes), and apply (writes files and durable provenance). This guide covers that pipeline, the two provenance records it produces (skillmeat.lock and a SkillBOM sidecar), and how to recover from a failed or stale apply.

The three steps

# 1. resolve — pure computation, no plan, zero writes
skillmeat assembly resolve managed:assembly/my-assembly --format json

# 2. preview — resolve + build a MaterializationPlan (still zero writes)
skillmeat assembly preview managed:assembly/my-assembly \
  --project <project-id> --adapter-ref claude_skill --format json
# returns plan_digest + idempotency_key alongside the resolve output

# 3. apply — digest-gated: materializes the SAME plan just previewed
skillmeat assembly apply managed:assembly/my-assembly \
  --project <project-id> --adapter-ref claude_skill -y

If you omit --plan-digest/--idempotency-key on apply, the CLI runs preview internally first and applies exactly that result — you never need a separate round trip just to get the digest. Pass both flags explicitly only when you're scripting a two-step flow (e.g. showing a user a preview, then applying only after they confirm) and want the guarantee that what gets applied is the exact plan they saw, not a silent re-resolution.

apply never re-resolves silently: if the assembly (or anything it depends on) changed since your preview call, the digest no longer matches and the call is rejected rather than materializing something you didn't see.

skillmeat.lock

A successful apply writes (or updates) one entry in <project-root>/skillmeat.lock, keyed by the assembly's qualified ref. This is an exact lock of the last applied state, not a history log — applying the same assembly again overwrites its own entry. History and audit trail live in the MaterializationRecord and SkillBOM instead.

skillmeat.lock is a separate authority from collection.lock (artifact source pinning) and .skillmeat-deployed.toml (legacy deployment tracking) — neither of those files is touched by assembly apply, and assembly apply doesn't touch them.

skillmeat assembly lock managed:assembly/my-assembly --project <project-id> --format json

is the read-only accessor — there is no corresponding write endpoint; apply is the sole writer.

SkillBOM provenance

Every successful apply also emits a SkillBOM v0 sidecar recording the resolution/materialization/graph digests for that apply. The skillmeat.lock entry and the SkillBOM sidecar are built from the same provenance section, so the two always agree on those digests for a given apply — you never have to reconcile two different records of what happened.

Deterministic replay

Resolution and plan-building are pure functions of their inputs: the same assembly, presets, rule facts, and module content always resolve to the same plan digest. That means:

  • Re-running preview with identical inputs always returns the identical plan_digest/idempotency_key.
  • Re-running apply with the same idempotency_key against an already-applied record is a no-op — it detects the existing record and returns without writing anything a second time.
  • Retrying an interrupted apply (see Recovery below) recomputes identical content and file writes are idempotent no-ops for any path already correct.

Recovery states

apply never returns a false success. Every failure path is one of these, distinguishable by response shape:

Situation Response What to do
Definition changed since your preview 409 materialization_stale_plan Re-run preview, review the new plan, apply again.
Resolution itself failed (conflicts, unresolved bindings) 422 assembly_resolution_failed with diagnostics Fix the assembly/preset/rule issue the diagnostics describe; nothing was written.
A pre-write validation failed (e.g. an output path would escape the project directory) 422 materialization_error with errors Fix the offending module's output_claims; nothing was written.
A write succeeded but durable provenance (lock/BOM/record) could not be committed 200 with applied: false and a populated recovery object (stage, manifest_path, compensation, retryable: true) Retry the identical apply call (same plan_digest/idempotency_key/inputs) — it converges rather than duplicating writes.

The third row is the one to understand operationally: past the point where files are actually written, a failure is never rolled back (the write is already correct) and never silently reported as success (provenance isn't done yet). The response tells you exactly which stage failed and gives you everything needed to retry the same call safely.

Before that point — anything that fails during file writes themselves — is fully reversible: SkillMeat restores every already-written output from its backup and raises a plain error; no partial state is left on disk.

  • Authoring — building the assembly you're resolving/previewing/applying.
  • Presets & Rules — how binding provenance and rule decisions end up in the resolve/preview/apply response, and in skillmeat.lock.
  • Editions — where MaterializationRecord and lock storage differ between local and enterprise.