Add project-level skills lock file (POC)#5715
Conversation
There was a problem hiding this comment.
Large PR Detected
This PR exceeds 1000 lines of changes and requires justification before it can be reviewed.
How to unblock this PR:
Add a section to your PR description with the following format:
## Large PR Justification
[Explain why this PR must be large, such as:]
- Generated code that cannot be split
- Large refactoring that must be atomic
- Multiple related changes that would break if separated
- Migration or data transformationAlternative:
Consider splitting this PR into smaller, focused changes (< 1000 lines each) for easier review and reduced risk.
See our Contributing Guidelines for more details.
This review will be automatically dismissed once you add the justification section.
|
Love it! Let's talk on Monday about this. Shall we have an RFC too? Just for docs |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5715 +/- ##
==========================================
- Coverage 70.74% 69.75% -1.00%
==========================================
Files 683 704 +21
Lines 69194 70884 +1690
==========================================
+ Hits 48950 49443 +493
- Misses 16663 17741 +1078
- Partials 3581 3700 +119 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Yeah 🚀 , I opened this RFC: stacklok/toolhive-rfcs#80. I just refined it a bit. |
Large PR justification has been provided. Thank you!
|
✅ Large PR justification has been provided. The size review has been dismissed and this PR can now proceed with normal review. |
dafb744 to
ae2f41a
Compare
Introduce toolhive.lock.yaml, committed at the project root, that pins the
name, version, source, resolved reference, and digest of every project-scoped
skill install. This gives teams reproducible skill installs (restorable via
"thv skill sync") and a path to refresh pinned content when the catalog moves
("thv skill upgrade"), mirroring the role package-lock.json plays for npm.
- New pkg/skills/lockfile package: schema, load/save, and file-locked
upsert/remove using pkg/fileutils.WithFileLock; entries sorted by name for
stable diffs.
- skillsvc install/uninstall hooks: project-scope installs upsert a lock
entry (recording the original source string for later re-resolution);
uninstalls remove it. User-scope installs never touch the lock.
- Sync: installs each entry strictly at its pinned resolvedReference@digest,
reports unmanaged project-scope skills, and prunes them with --prune.
- Upgrade: re-resolves each entry's original source, compares digests, and
installs + rewrites the entry on change; immutable sources (OCI digest,
git commit) reported as not-upgradable; --dry-run supported.
- API: POST /skills/sync and POST /skills/upgrade plus skills client methods.
- CLI: thv skill sync and thv skill upgrade with git-root auto-detection and
--project-root override; JSON/text output.
- Docs: regenerated CLI/swagger docs; updated docs/arch/12-skills-system.md
with a Project Lock File section, diagram, and endpoint/CLI tables.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Splitting this into the stacked PR chain proposed above, matching the merged RFC THV-0080. Closing this POC in favor of: Stack 1 — lock file (open now):
Stack 2 — Sigstore signing/verification: to follow once Stack 1 is fully merged, per the RFC's trust model. A prerequisite PR to The whole feature (both stacks) stays gated behind Thanks for validating the approach here — the stack is a from-scratch rewrite against the RFC (not a mechanical re-split of these commits), so it also folds in a few hardening fixes this review process and testing surfaced (a real drift-detection bug in the install fast path, CodeQL path-injection findings, typed failure-reason classification instead of string matching). |
RFC THV-0080 gives CI a scriptable contract for sync/upgrade: exit 2 for check/freshness failures, 3 for partial failures, 4 for policy rejections, distinct from the generic 1 cobra already uses. It also requires a pre-install confirmation gate — skill content is a set of AI-followed instructions, so a non-interactive run must refuse outright without --yes rather than silently proceeding. - cmd/thv/app/exitcode.go: exitCodeError carries a specific exit code through a RunE return; main.go maps it via ExitCodeFromError instead of a hardcoded os.Exit(1) - cmd/thv/app/skill_confirm.go: requireConfirmation prompts on a TTY, refuses with ExitCodePolicyRejection off one - sync/upgrade gain --yes, skipped automatically by --check/ --preview (which never install anything); their exit-code mapping prioritizes partial failure over drift/ref-change-blocked - docs/arch/12-skills-system.md: new Project Lock File section covering rollout gating, schema, install/uninstall hooks, sync, upgrade, and the exit-code contract - CLI E2E coverage for all four exit codes (0/2/4, plus --yes) This is the last PR of Stack 1 (lock file + sync + upgrade) for RFC THV-0080. Stack 2 (Sigstore signing/verification) builds on top once this stack is fully merged; TOOLHIVE_SKILLS_LOCK_ENABLED stays off by default until that full v1 lands. Part of the production stack for #5715 (RFC THV-0080). Stack: 6/6.
RFC THV-0080's sync and upgrade commands operate over a project's
whole lock file rather than a single named skill, so they need
their own service boundary (SkillLockService) instead of extending
SkillService. Installed skills also need a durable "is this
tracked in the lock file" marker so sync --prune can distinguish
lock-managed installs from ones a user manages by hand.
- SkillLockService{Sync,Upgrade} interface with generated mock
- Sync/Upgrade option and result types, RFC's typed failure reasons
- installed_skills.managed column (migration 003)
Part of the production stack for #5715 (RFC THV-0080). Stack: 2/6.
No consumers yet — skillsvc wiring lands in PR3.
RFC THV-0080 requires every project-scoped skill install to be pinned in toolhive.lock.yaml, including transitively materialized toolhive.requires dependencies, with a fail-hard guarantee so a lock-write failure never leaves an install silently unpinned. The whole feature stays inert on main until the full RFC v1 (this lock-file stack plus the later Sigstore stack) has landed — gated behind TOOLHIVE_SKILLS_LOCK_ENABLED, following the existing TOOLHIVE_DEV precedent for staged rollouts. Each PR in the stack is independently mergeable without exposing partial behavior. - Install hooks into the single choke point (installAndRegister): compute contentDigest, upsert the lock entry, roll back the DB record if the lock write fails - toolhive.requires dependencies materialize recursively (visited set guards cycles, skills.MaxDependencies bounds the whole tree), with RequiredBy merged across shared dependencies - Uninstall cascades to orphaned, non-explicit dependencies via Lockfile.RemoveParentFromRequiredBy, itself cycle-safe - E2E coverage for the real HTTP API + thv serve subprocess path Part of the production stack for #5715 (RFC THV-0080). Stack: 3/6.
RFC THV-0080 needs a way to restore a project's pinned skill set on any machine (a fresh clone, CI, a teammate's laptop) and to verify on-disk content still matches the lock file without installing anything. This adds the full sync vertical slice: service logic, HTTP endpoint, client, and CLI command. - Sync reconciles installed skills against toolhive.lock.yaml: missing/drifted entries reinstall at the pinned reference (never re-resolving source, see buildPinnedReference), --check reports drift read-only, --adopt/--prune handle unlocked installs - Fixes a real bug this surfaced: reinstalling at an unchanged pinned digest hit both the OCI and git install paths' "same digest means content is already correct" fast path, silently skipping re-extraction — exactly the drift sync exists to repair. A new internal SyncRestore option bypasses that fast path. - POST /api/v1beta/skills/sync via a narrow skillSyncer interface (pkg/api/v1), so this PR doesn't need to ship an Upgrade stub - `thv skill sync` CLI (--check/--adopt/--prune/--clients/ --project-root, auto-detected via .git when omitted) Part of the production stack for #5715 (RFC THV-0080). Stack: 4/6.
RFC THV-0080 needs a controlled way to pull newer content for skills pinned by source, without silently drifting the lock file on every install. This adds the full upgrade vertical slice: service logic, HTTP endpoint, client, and CLI command. - Upgrade re-resolves each targeted entry's Source (via the same git/OCI/registry dispatch order Install uses, stopping short of extraction — resolveLatestState) and installs newer content when the digest changed; Source itself is never rewritten - Entries pinned to an immutable reference (an OCI digest or full git commit hash) report not-upgradable — there's nothing newer to resolve to - --preview reports what would change without persisting it; --allow-ref-change permits the resolved reference itself changing; --fail-on-changes gives CI a freshness gate - var _ skills.SkillLockService = (*service)(nil) lands here now that both Sync and Upgrade exist; SkillsRouter widens from the narrow skillSyncer interface (PR4) to the full interface - `thv skill upgrade [name...]` CLI Part of the production stack for #5715 (RFC THV-0080). Stack: 5/6.
RFC THV-0080 gives CI a scriptable contract for sync/upgrade: exit 2 for check/freshness failures, 3 for partial failures, 4 for policy rejections, distinct from the generic 1 cobra already uses. It also requires a pre-install confirmation gate — skill content is a set of AI-followed instructions, so a non-interactive run must refuse outright without --yes rather than silently proceeding. - cmd/thv/app/exitcode.go: exitCodeError carries a specific exit code through a RunE return; main.go maps it via ExitCodeFromError instead of a hardcoded os.Exit(1) - cmd/thv/app/skill_confirm.go: requireConfirmation prompts on a TTY, refuses with ExitCodePolicyRejection off one - sync/upgrade gain --yes, skipped automatically by --check/ --preview (which never install anything); their exit-code mapping prioritizes partial failure over drift/ref-change-blocked - docs/arch/12-skills-system.md: new Project Lock File section covering rollout gating, schema, install/uninstall hooks, sync, upgrade, and the exit-code contract - CLI E2E coverage for all four exit codes (0/2/4, plus --yes) This is the last PR of Stack 1 (lock file + sync + upgrade) for RFC THV-0080. Stack 2 (Sigstore signing/verification) builds on top once this stack is fully merged; TOOLHIVE_SKILLS_LOCK_ENABLED stays off by default until that full v1 lands. Part of the production stack for #5715 (RFC THV-0080). Stack: 6/6.
RFC THV-0080 requires every project-scoped skill install to be pinned in toolhive.lock.yaml, including transitively materialized toolhive.requires dependencies, with a fail-hard guarantee so a lock-write failure never leaves an install silently unpinned. The whole feature stays inert on main until the full RFC v1 (this lock-file stack plus the later Sigstore stack) has landed — gated behind TOOLHIVE_SKILLS_LOCK_ENABLED, following the existing TOOLHIVE_DEV precedent for staged rollouts. Each PR in the stack is independently mergeable without exposing partial behavior. - Install hooks into the single choke point (installAndRegister): compute contentDigest, upsert the lock entry, roll back the DB record if the lock write fails - toolhive.requires dependencies materialize recursively (visited set guards cycles, skills.MaxDependencies bounds the whole tree), with RequiredBy merged across shared dependencies - Uninstall cascades to orphaned, non-explicit dependencies via Lockfile.RemoveParentFromRequiredBy, itself cycle-safe - E2E coverage for the real HTTP API + thv serve subprocess path Part of the production stack for #5715 (RFC THV-0080). Stack: 3/6.
RFC THV-0080 needs a way to restore a project's pinned skill set on any machine (a fresh clone, CI, a teammate's laptop) and to verify on-disk content still matches the lock file without installing anything. This adds the full sync vertical slice: service logic, HTTP endpoint, client, and CLI command. - Sync reconciles installed skills against toolhive.lock.yaml: missing/drifted entries reinstall at the pinned reference (never re-resolving source, see buildPinnedReference), --check reports drift read-only, --adopt/--prune handle unlocked installs - Fixes a real bug this surfaced: reinstalling at an unchanged pinned digest hit both the OCI and git install paths' "same digest means content is already correct" fast path, silently skipping re-extraction — exactly the drift sync exists to repair. A new internal SyncRestore option bypasses that fast path. - POST /api/v1beta/skills/sync via a narrow skillSyncer interface (pkg/api/v1), so this PR doesn't need to ship an Upgrade stub - `thv skill sync` CLI (--check/--adopt/--prune/--clients/ --project-root, auto-detected via .git when omitted) Part of the production stack for #5715 (RFC THV-0080). Stack: 4/6.
RFC THV-0080 needs a controlled way to pull newer content for skills pinned by source, without silently drifting the lock file on every install. This adds the full upgrade vertical slice: service logic, HTTP endpoint, client, and CLI command. - Upgrade re-resolves each targeted entry's Source (via the same git/OCI/registry dispatch order Install uses, stopping short of extraction — resolveLatestState) and installs newer content when the digest changed; Source itself is never rewritten - Entries pinned to an immutable reference (an OCI digest or full git commit hash) report not-upgradable — there's nothing newer to resolve to - --preview reports what would change without persisting it; --allow-ref-change permits the resolved reference itself changing; --fail-on-changes gives CI a freshness gate - var _ skills.SkillLockService = (*service)(nil) lands here now that both Sync and Upgrade exist; SkillsRouter widens from the narrow skillSyncer interface (PR4) to the full interface - `thv skill upgrade [name...]` CLI Part of the production stack for #5715 (RFC THV-0080). Stack: 5/6.
RFC THV-0080 gives CI a scriptable contract for sync/upgrade: exit 2 for check/freshness failures, 3 for partial failures, 4 for policy rejections, distinct from the generic 1 cobra already uses. It also requires a pre-install confirmation gate — skill content is a set of AI-followed instructions, so a non-interactive run must refuse outright without --yes rather than silently proceeding. - cmd/thv/app/exitcode.go: exitCodeError carries a specific exit code through a RunE return; main.go maps it via ExitCodeFromError instead of a hardcoded os.Exit(1) - cmd/thv/app/skill_confirm.go: requireConfirmation prompts on a TTY, refuses with ExitCodePolicyRejection off one - sync/upgrade gain --yes, skipped automatically by --check/ --preview (which never install anything); their exit-code mapping prioritizes partial failure over drift/ref-change-blocked - docs/arch/12-skills-system.md: new Project Lock File section covering rollout gating, schema, install/uninstall hooks, sync, upgrade, and the exit-code contract - CLI E2E coverage for all four exit codes (0/2/4, plus --yes) This is the last PR of Stack 1 (lock file + sync + upgrade) for RFC THV-0080. Stack 2 (Sigstore signing/verification) builds on top once this stack is fully merged; TOOLHIVE_SKILLS_LOCK_ENABLED stays off by default until that full v1 lands. Part of the production stack for #5715 (RFC THV-0080). Stack: 6/6.
RFC THV-0080 needs a way to restore a project's pinned skill set on any machine (a fresh clone, CI, a teammate's laptop) and to verify on-disk content still matches the lock file without installing anything. This adds the full sync vertical slice: service logic, HTTP endpoint, client, and CLI command. - Sync reconciles installed skills against toolhive.lock.yaml: missing/drifted entries reinstall at the pinned reference (never re-resolving source, see buildPinnedReference), --check reports drift read-only, --adopt/--prune handle unlocked installs - Fixes a real bug this surfaced: reinstalling at an unchanged pinned digest hit both the OCI and git install paths' "same digest means content is already correct" fast path, silently skipping re-extraction — exactly the drift sync exists to repair. A new internal SyncRestore option bypasses that fast path. - POST /api/v1beta/skills/sync via a narrow skillSyncer interface (pkg/api/v1), so this PR doesn't need to ship an Upgrade stub - `thv skill sync` CLI (--check/--adopt/--prune/--clients/ --project-root, auto-detected via .git when omitted) Part of the production stack for #5715 (RFC THV-0080). Stack: 4/6.
RFC THV-0080 needs a controlled way to pull newer content for skills pinned by source, without silently drifting the lock file on every install. This adds the full upgrade vertical slice: service logic, HTTP endpoint, client, and CLI command. - Upgrade re-resolves each targeted entry's Source (via the same git/OCI/registry dispatch order Install uses, stopping short of extraction — resolveLatestState) and installs newer content when the digest changed; Source itself is never rewritten - Entries pinned to an immutable reference (an OCI digest or full git commit hash) report not-upgradable — there's nothing newer to resolve to - --preview reports what would change without persisting it; --allow-ref-change permits the resolved reference itself changing; --fail-on-changes gives CI a freshness gate - var _ skills.SkillLockService = (*service)(nil) lands here now that both Sync and Upgrade exist; SkillsRouter widens from the narrow skillSyncer interface (PR4) to the full interface - `thv skill upgrade [name...]` CLI Part of the production stack for #5715 (RFC THV-0080). Stack: 5/6.
RFC THV-0080 gives CI a scriptable contract for sync/upgrade: exit 2 for check/freshness failures, 3 for partial failures, 4 for policy rejections, distinct from the generic 1 cobra already uses. It also requires a pre-install confirmation gate — skill content is a set of AI-followed instructions, so a non-interactive run must refuse outright without --yes rather than silently proceeding. - cmd/thv/app/exitcode.go: exitCodeError carries a specific exit code through a RunE return; main.go maps it via ExitCodeFromError instead of a hardcoded os.Exit(1) - cmd/thv/app/skill_confirm.go: requireConfirmation prompts on a TTY, refuses with ExitCodePolicyRejection off one - sync/upgrade gain --yes, skipped automatically by --check/ --preview (which never install anything); their exit-code mapping prioritizes partial failure over drift/ref-change-blocked - docs/arch/12-skills-system.md: new Project Lock File section covering rollout gating, schema, install/uninstall hooks, sync, upgrade, and the exit-code contract - CLI E2E coverage for all four exit codes (0/2/4, plus --yes) This is the last PR of Stack 1 (lock file + sync + upgrade) for RFC THV-0080. Stack 2 (Sigstore signing/verification) builds on top once this stack is fully merged; TOOLHIVE_SKILLS_LOCK_ENABLED stays off by default until that full v1 lands. Part of the production stack for #5715 (RFC THV-0080). Stack: 6/6.
RFC THV-0080 needs a controlled way to pull newer content for skills pinned by source, without silently drifting the lock file on every install. This adds the full upgrade vertical slice: service logic, HTTP endpoint, client, and CLI command. - Upgrade re-resolves each targeted entry's Source (via the same git/OCI/registry dispatch order Install uses, stopping short of extraction — resolveLatestState) and installs newer content when the digest changed; Source itself is never rewritten - Entries pinned to an immutable reference (an OCI digest or full git commit hash) report not-upgradable — there's nothing newer to resolve to - --preview reports what would change without persisting it; --allow-ref-change permits the resolved reference itself changing; --fail-on-changes gives CI a freshness gate - var _ skills.SkillLockService = (*service)(nil) lands here now that both Sync and Upgrade exist; SkillsRouter widens from the narrow skillSyncer interface (PR4) to the full interface - `thv skill upgrade [name...]` CLI Part of the production stack for #5715 (RFC THV-0080). Stack: 5/6.
RFC THV-0080 gives CI a scriptable contract for sync/upgrade: exit 2 for check/freshness failures, 3 for partial failures, 4 for policy rejections, distinct from the generic 1 cobra already uses. It also requires a pre-install confirmation gate — skill content is a set of AI-followed instructions, so a non-interactive run must refuse outright without --yes rather than silently proceeding. - cmd/thv/app/exitcode.go: exitCodeError carries a specific exit code through a RunE return; main.go maps it via ExitCodeFromError instead of a hardcoded os.Exit(1) - cmd/thv/app/skill_confirm.go: requireConfirmation prompts on a TTY, refuses with ExitCodePolicyRejection off one - sync/upgrade gain --yes, skipped automatically by --check/ --preview (which never install anything); their exit-code mapping prioritizes partial failure over drift/ref-change-blocked - docs/arch/12-skills-system.md: new Project Lock File section covering rollout gating, schema, install/uninstall hooks, sync, upgrade, and the exit-code contract - CLI E2E coverage for all four exit codes (0/2/4, plus --yes) This is the last PR of Stack 1 (lock file + sync + upgrade) for RFC THV-0080. Stack 2 (Sigstore signing/verification) builds on top once this stack is fully merged; TOOLHIVE_SKILLS_LOCK_ENABLED stays off by default until that full v1 lands. Part of the production stack for #5715 (RFC THV-0080). Stack: 6/6.
RFC THV-0080 gives CI a scriptable contract for sync/upgrade: exit 2 for check/freshness failures, 3 for partial failures, 4 for policy rejections, distinct from the generic 1 cobra already uses. It also requires a pre-install confirmation gate — skill content is a set of AI-followed instructions, so a non-interactive run must refuse outright without --yes rather than silently proceeding. - cmd/thv/app/exitcode.go: exitCodeError carries a specific exit code through a RunE return; main.go maps it via ExitCodeFromError instead of a hardcoded os.Exit(1) - cmd/thv/app/skill_confirm.go: requireConfirmation prompts on a TTY, refuses with ExitCodePolicyRejection off one - sync/upgrade gain --yes, skipped automatically by --check/ --preview (which never install anything); their exit-code mapping prioritizes partial failure over drift/ref-change-blocked - docs/arch/12-skills-system.md: new Project Lock File section covering rollout gating, schema, install/uninstall hooks, sync, upgrade, and the exit-code contract - CLI E2E coverage for all four exit codes (0/2/4, plus --yes) This is the last PR of Stack 1 (lock file + sync + upgrade) for RFC THV-0080. Stack 2 (Sigstore signing/verification) builds on top once this stack is fully merged; TOOLHIVE_SKILLS_LOCK_ENABLED stays off by default until that full v1 lands. Part of the production stack for #5715 (RFC THV-0080). Stack: 6/6.
RFC THV-0080 needs a controlled way to pull newer content for skills pinned by source, without silently drifting the lock file on every install. This adds the full upgrade vertical slice: service logic, HTTP endpoint, client, and CLI command. - Upgrade re-resolves each targeted entry's Source (via the same git/OCI/registry dispatch order Install uses, stopping short of extraction — resolveLatestState) and installs newer content when the digest changed; Source itself is never rewritten - Entries pinned to an immutable reference (an OCI digest or full git commit hash) report not-upgradable — there's nothing newer to resolve to - --preview reports what would change without persisting it; --allow-ref-change permits the resolved reference itself changing; --fail-on-changes gives CI a freshness gate - var _ skills.SkillLockService = (*service)(nil) lands here now that both Sync and Upgrade exist; SkillsRouter widens from the narrow skillSyncer interface (PR4) to the full interface - `thv skill upgrade [name...]` CLI Part of the production stack for #5715 (RFC THV-0080). Stack: 5/6.
RFC THV-0080 gives CI a scriptable contract for sync/upgrade: exit 2 for check/freshness failures, 3 for partial failures, 4 for policy rejections, distinct from the generic 1 cobra already uses. It also requires a pre-install confirmation gate — skill content is a set of AI-followed instructions, so a non-interactive run must refuse outright without --yes rather than silently proceeding. - cmd/thv/app/exitcode.go: exitCodeError carries a specific exit code through a RunE return; main.go maps it via ExitCodeFromError instead of a hardcoded os.Exit(1) - cmd/thv/app/skill_confirm.go: requireConfirmation prompts on a TTY, refuses with ExitCodePolicyRejection off one - sync/upgrade gain --yes, skipped automatically by --check/ --preview (which never install anything); their exit-code mapping prioritizes partial failure over drift/ref-change-blocked - docs/arch/12-skills-system.md: new Project Lock File section covering rollout gating, schema, install/uninstall hooks, sync, upgrade, and the exit-code contract - CLI E2E coverage for all four exit codes (0/2/4, plus --yes) This is the last PR of Stack 1 (lock file + sync + upgrade) for RFC THV-0080. Stack 2 (Sigstore signing/verification) builds on top once this stack is fully merged; TOOLHIVE_SKILLS_LOCK_ENABLED stays off by default until that full v1 lands. Part of the production stack for #5715 (RFC THV-0080). Stack: 6/6.
Summary
sync/upgradewould restore or advance skills safely.toolhive.lock.yamlfor project scope withthv skill syncandthv skill upgrade, then fold RFC THV-0080 v1 Sigstore signing/verification into the same POC: verify on consume, sign on push, pin publisher identity (provenance) or recordunsigned: true, and re-verify stored bundles offline insync --check.POC scope (RFC THV-0080)
This PR is a feasibility vertical slice, not production-hardening split across many small PRs yet. It proves:
source,resolvedReference,digest,contentDigestcontentDigestdirhash +sync --checksync --adoptwrites lock entries (with provenance orunsigned)toolhive.requiresmaterialized;requiredBy+ cascade uninstall--allow-ref-change;--allow-signer-changethv skill pushsigns with--key(cosign);--no-signescape hatch--yes, exit codes 2/3/4Trust model (v1)
Three layers (see
docs/arch/12-skills-system.md):sync --checkprovenance.signerIdentity+provenance.certIssuerpinned at first verified install;signer-mismatchon divergencetoolhive.lock.yaml;unsigned: trueis an explicit reviewed exceptionDeferred: keyless OIDC push (E2E against Sigstore staging); catalog-supplied expected identity (needs toolhive-core
ProvenanceonSkilltypes).Lock entry schema (v1)
CLI surface
thv skill sync—--check,--adopt,--prune,--yes,--clients,--project-rootthv skill upgrade [name...]—--preview,--fail-on-changes,--allow-ref-change,--allow-signer-change,--yesthv skill install—--allow-unsigned(project scope)thv skill push—--key,--no-signExit codes:
0success ·2check/freshness failure ·3partial failure ·4policy rejectionCommit structure (19 commits, atomic by layer)
Lock file core
Sigstore v1
Type of change
Test plan
task test—pkg/skills/...passes)task lint-fix)sync --checkAPI compatibility
Additive:
POST /skills/sync,POST /skills/upgrade; new request fields (allowUnsigned,allowSignerChange,key,skipSigning).SkillLockServiceis separate fromSkillService. No operator CRD changes.Large PR justification
~6.6k lines / 74 files. Kept unified for POC feasibility: lock integrity, sync/upgrade guardrails, and Sigstore trust are coupled — splitting mid-stack leaves non-functional intermediate states. If validated, split into the stack below for production merge.
Review focus
contentDigestthe right integrity primitive?requiredBy/explicit) correct?--checksufficient?SkillLockServicethe right API boundary?Does this introduce a user-facing change?
Yes — new
sync/upgradecommands, lock file on project install, Sigstore flags, push signing.