# skills-lock-valid skills-lock.json files must be valid and portable project lockfiles | | | |---|---| | **Severity** | error (auto) | | **Autofix** | - | | **Since** | v0.20.0 | | **Repo Types** | skills-lock | | **Category** | [Vercel](vercel.md) | ## Why The [Vercel skills CLI](https://github.com/vercel-labs/skills) records each project's installed skills in `skills-lock.json`. The lockfile tells later `skills update` and restore operations where a skill came from, which source dialect to use, which `SKILL.md` was selected, and what content hash was installed. A lockfile can remain valid JSON while a wrong field type or an unusable path quietly makes those operations unreliable. This rule follows the project-lock structure implemented by the CLI's [`src/local-lock.ts`](https://github.com/vercel-labs/skills/blob/main/src/local-lock.ts): - the root is an object with numeric `version` and an object-valued `skills`; - each skill has non-empty `source` and `sourceType` strings; - `computedHash`, when present, is a lowercase, 64-character SHA-256 hex digest. The CLI reads it only to detect drift, so an entry without one is a warning rather than an error; - optional `sourceUrl`, `ref`, `skillPath`, and `wellKnownDigest` fields have the string shape their consumers expect; - optional `subagents` is an array of strings. An empty string is valid here: the CLI uses it for a source's root agent; - `skillPath` cannot be absolute or traverse above the downloaded source, and ends in `SKILL.md`; - a `wellKnownDigest`, when present, uses `sha256:` followed by 64 lowercase hex characters. Known `sourceType` values are `github`, `gitlab`, `git`, `local`, `well-known`, `node_modules`, and `download`. A newer CLI may add another one, so an unknown value is information rather than an error and can be accepted immediately with `extra-source-types`. Validation is offline and structural: `computedHash` is checked for format (a lowercase 64-character SHA-256 hex digest) rather than recalculated over the network. Because lockfiles are generated by package managers, defects are reported for human review without autofix. ## Detection Every exact `skills-lock.json` filename is discovered recursively. This supports monorepos where the root and individual packages each run the skills CLI. Vendored directories and paths excluded through skillsaw configuration are not attached. A global `.skill-lock.json` or singular `skill-lock.json` is a different file and is not claimed by this rule. The rule auto-enables whenever at least one non-excluded project lockfile is present. A lockfile is structured JSON rather than agent prose, so content rules never read it. skillsaw also uses valid lock entries as provenance for installed skill directories. A remote, package-managed, unknown, or repository-external `local` source marks the matching installed skill as externally sourced. Those payloads are linted by default, but `skillsaw fix` never rewrites them. Set the top-level `lint-external-content: false` configuration key to omit them from rule discovery while continuing to validate `skills-lock.json` itself. A `local` source that resolves inside the lint root remains repository-owned. ## Severity Errors identify data the CLI cannot reliably interpret: invalid or non-strict JSON (including bare `NaN` or `Infinity`), the wrong top-level shape, missing or wrong-typed required fields, malformed digests, malformed optional fields, or a `skillPath` that escapes its source or does not point to `SKILL.md`. Warnings identify a lockfile that remains readable but is not portable or may not restore correctly: - a schema version newer than this skillsaw release; - an entry without `computedHash`, which `npx skills check` cannot verify; - a bare `git` or `gitlab` shorthand without the `sourceUrl` the CLI's update path needs; - an absolute local source path; - backslashes in `skillPath`, which are not portable path separators. An unknown `sourceType` is informational because it may come from a newer or custom skills CLI. ## Examples **Good:** ```json { "version": 1, "skills": { "release-notes": { "source": "vercel-labs/skills", "sourceType": "github", "computedHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "sourceUrl": "https://github.com/vercel-labs/skills.git", "ref": "main", "skillPath": "skills/release-notes/SKILL.md" } } } ``` **Bad:** ```json { "version": 1, "skills": { "release-notes": { "source": "/Users/alice/src/skills/release-notes", "sourceType": "local", "computedHash": "not-a-sha256", "skillPath": "../README.md", "subagents": "reviewer" } } } ``` The absolute source only works on one machine, the digest has the wrong shape, `skillPath` escapes the downloaded source and does not end in `SKILL.md`, and `subagents` must be an array. ## How to fix Regenerate a damaged lockfile with the same skills CLI release used by the project, or correct the reported structural fields and rerun the CLI command that consumes it. Prefer project-relative local sources and `/` separators. If a legitimate source type was added after this skillsaw release, accept it without disabling the rest of the rule: ```yaml rules: skills-lock-valid: extra-source-types: - registry ``` ## Configuration ```yaml rules: skills-lock-valid: enabled: auto # true | false | auto severity: error ``` | Parameter | Description | Default | |-----------|-------------|---------| | `extra-source-types` | Additional sourceType values to accept when a newer or custom skills CLI writes sources this skillsaw release does not know | `[]` | *Run `skillsaw explain skills-lock-valid` to see this documentation and the rule's effective configuration in your terminal.*