Lockfiles
What skillporter.lock records, what its pin states mean and what reproducible covers.
skillporter.lock records exactly what is installed. With it, a teammate or a CI job can reproduce the same skill content and detect any change to it.
Create and check
skillporter lock
skillporter lock --check
skillporter sync also writes the lockfile. lock --check writes nothing. It exits 1 when the lockfile is stale or the files on disk no longer match.
$ skillporter lock --check
skillporter.lock check FAILED (1 issue(s)):
- lockfile drift on 'pdf' (.claude/skills/pdf): locked 6b86f0c798b5 != disk e08dcf6fa1c6
Commit skillporter.lock next to skillporter.yaml.
Format
This entry was produced by skillporter add anthropics/skills --skill pdf --harness all followed by skillporter lock. The file list and harness list are shortened here.
{
"generated_at": "2026-11-14T11:40:33Z",
"manifest_schema_version": 2,
"reproducible": true,
"schema_version": 2,
"skills": {
"pdf": {
"adapt": false,
"adapter_version": "3.0.0",
"dependencies": {},
"direct": true,
"file_hashes": {
"SKILL.md": "d1c7caf5f35eb7d37877dd7fc33a095037be2b34fb5d2f7f4d43bde3165bb4ee",
"forms.md": "9530b3f57034792e0242a3abfe8b3c296466329551916f2ff6c5a70db78f7e44",
"scripts/fill_fillable_fields.py": "d140872c2e92e41b1e05370ae8d270c5b7f5ebdf55d9905f03bc974a9c708ff5"
},
"installed_targets": ["claude-code", "codex", "cursor"],
"locked_at": "2026-11-14T11:40:33Z",
"manifest_schema_version": 2,
"name": "pdf",
"pin_state": "PINNED_COMMIT",
"resolved_commit": "2c5958ecac7cee2b583967a2c4981d0f4a75c579",
"risk_score": 0,
"scope": "project",
"source": "https://github.com/anthropics/skills.git",
"strategy": "copy",
"tree_hash": "6b86f0c798b5a47987bdd48a97964c0d349ea8ecbc4b886f10dcc38ab0580c32",
"version": "1.0.0"
}
}
}
Skill lockfiles explained goes through what these fields pin and what they leave open.
Fields
| Field | Purpose |
|---|---|
resolved_commit |
The full Git commit the source resolved to. sync installs this commit even if the branch has moved since. |
tree_hash |
One SHA-256 over the whole skill directory. Any change to any file changes it. |
file_hashes |
A SHA-256 per file, so a mismatch can be traced to the file that changed. |
pin_state |
Whether the entry is immutable. See below. |
dependencies |
Other skills this one depends on, with resolved versions. |
adapter_version |
The Skillporter version whose adapters produced the install. |
installed_targets |
The harnesses the skill was installed for. |
source, strategy, scope, adapt |
How and where it was installed. |
risk_score |
The static risk score at lock time. |
reproducible |
Top level. true only when every entry is pinned. |
Pin states
pin_state |
Meaning |
|---|---|
PINNED_COMMIT |
The entry has a content hash and a resolved Git commit. |
PINNED_CONTENT |
The entry has a content hash but no commit, for example a local directory. The content is pinned. Its origin is not. |
UNPINNED_MUTABLE |
A remote source without a resolved commit, or an entry without a content hash. |
reproducible is false as soon as one entry is UNPINNED_MUTABLE.
What reproducible means here
Given the same skillporter.lock, skillporter sync installs the same file contents, verified by hash, for the same harnesses.
It does not promise more than that:
- A local source is only as stable as the directory it points to.
- If the pinned commit can no longer be fetched from the source, the install cannot be reproduced from that lockfile.
- A different Skillporter version may place files differently.
adapter_versionrecords which version wrote the lockfile. - The lockfile pins skill content. It does not pin what a skill's scripts download when an agent runs them.
How those copies are placed across harnesses, and what happens when one of them drifts, is covered in Syncing agent skills across harnesses.
Changed sources are refused, not absorbed
- If the content fetched for a pinned commit does not match
tree_hash, the sync stops. - If the files on disk differ from the lockfile,
skillporter syncrefuses to re-lock without--force. - If
skillporter.yamlnow points a skill at a different source than the lockfile recorded,lock --checkreports it.
Validation
A lockfile that is not valid JSON, or that declares an unsupported schema_version, raises an error. It is never silently treated as empty. Schema version 1 lockfiles are migrated to version 2 automatically.
Dependencies
Skills can declare dependencies on other skills with semantic version constraints in skillporter.yaml. sync resolves the graph, detects cycles and conflicts, and installs in topological order.
skillporter graph
skillporter graph --json
In CI
- run: uvx skillporter lock --check