Skip to content

Serialize skill sync and upgrade under the lock - #6352

Merged
samuv merged 7 commits into
mainfrom
skills-lock/fresh-sync-upgrade
Aug 20, 2026
Merged

Serialize skill sync and upgrade under the lock#6352
samuv merged 7 commits into
mainfrom
skills-lock/fresh-sync-upgrade

Conversation

@samuv

@samuv samuv commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Summary

The plugin lock-file review found that sync/upgrade can mutate from a stale lock/DB snapshot (resurrecting a concurrent uninstall, or overwriting a newer install) and that default sync never expands to newly detected clients. Skills had the same shapes.

  • Re-read each skill under the per-skill lock before classifying or mutating; nested Install/Uninstall skip a second acquire of the same key.
  • Default thv skill sync (no --clients) keeps Install’s all-detected default, and --check is not current unless every detected client is present.
  • Each upgrade reloads, plans, and applies under the same lock so a removed entry is not resurrected. Upgrade still preserves an existing skill’s client list.

Type of change

  • Bug fix
  • New feature
  • Refactoring (no behavior change)
  • Dependency update
  • Documentation
  • Other (describe):

Test plan

  • Unit tests (task test) — ran go test for ./pkg/skills/skillsvc/ (TestSync_, TestUpgrade_, TestInstall)
  • E2E tests (task test-e2e)
  • Linting (task lint-fix)
  • Manual testing (describe below)

Changes

File Change
pkg/skills/skillsvc/service.go Nested-safe lockSkill
pkg/skills/skillsvc/install.go / _git.go / _oci.go / uninstall.go Use lockSkill so sync/upgrade can call them while holding the key
pkg/skills/skillsvc/sync.go Per-skill fresh read; default clients = all detected
pkg/skills/skillsvc/upgrade.go Per-skill reload/plan/apply
pkg/skills/skillsvc/*_test.go Stale-list, new-client, and removed-entry coverage

Does this introduce a user-facing change?

Yes. thv skill sync without --clients now materializes newly detected skill-supporting clients instead of keeping only the clients recorded on the previous install. thv skill upgrade is unchanged: it still preserves the existing client list.

Special notes for reviewers

This is the skills counterpart of the plugin lock-file review on #6316 / #6317. Plugin-only issues (lock-restore callback errors, Materialize-after-extract cleanup) do not apply here: skills uninstall still treats installed-but-unlocked as the safe inconsistency, and skills extract without a post-extract marketplace/settings step.

@samuv
samuv requested a review from JAORMX as a code owner August 17, 2026 15:24
@samuv samuv self-assigned this Aug 17, 2026
Comment thread pkg/skills/skillsvc/install_git.go Outdated
Comment thread pkg/skills/skillsvc/service.go Outdated

@JAORMX JAORMX left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full PR against main. The single-key context marker does not safely model nested dependency locks, and direct Git/OCI installs still release the lock before lock-file/group bookkeeping, so the intended serialization is incomplete and can deadlock. Requesting changes for the two inline blockers.

@samuv
samuv requested a review from amirejaz as a code owner August 17, 2026 17:26
@github-actions github-actions Bot added the size/XL Extra large PR: 1000+ lines changed label Aug 17, 2026
@samuv

samuv commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Update

f01ead3dd replaces heldSkillLock with a project-scoped transaction lock held through install bookkeeping and deps. Git/OCI no longer unlock before installAndRegister. Nested deps use locked helpers under the held tx.

env -u TOOLHIVE_DEV go test ./pkg/skills/skillsvc/ ./pkg/groups/ -count=1 passes.

Comment thread pkg/skills/skillsvc/file_snapshot.go Fixed
Comment thread pkg/skills/skillsvc/file_snapshot.go Fixed
Comment thread pkg/skills/skillsvc/file_snapshot.go Fixed
Comment thread pkg/skills/skillsvc/file_snapshot.go Fixed
Comment thread pkg/skills/skillsvc/file_snapshot.go Fixed
Comment thread pkg/skills/skillsvc/file_snapshot.go Fixed
@github-actions github-actions Bot removed the size/XL Extra large PR: 1000+ lines changed label Aug 17, 2026
@samuv

samuv commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (8343851e8). All previously unanswered review threads have replies and are marked resolved — please take another look when convenient.

Comment thread pkg/skills/skillsvc/file_snapshot.go Fixed
Comment thread pkg/skills/skillsvc/file_snapshot.go Fixed
Comment thread pkg/skills/skillsvc/file_snapshot.go Fixed
Comment thread pkg/skills/skillsvc/file_snapshot.go Fixed

@JAORMX JAORMX left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed commits after 4769eb2 against main. The transaction refactor still has four merge-blocking rollback/resource-lifecycle gaps.

Comment thread pkg/skills/skillsvc/install_extraction.go Outdated
Comment thread pkg/skills/skillsvc/install.go Outdated
Comment thread pkg/skills/skillsvc/install_extraction.go Outdated
Comment thread pkg/skills/skillsvc/service.go
@samuv
samuv force-pushed the skills-lock/fresh-sync-upgrade branch from 4439ba5 to 79a7cdb Compare August 18, 2026 08:20
@github-actions github-actions Bot added the size/XL Extra large PR: 1000+ lines changed label Aug 18, 2026
@samuv

samuv commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Aug-18 review round addressed

0a9d7b615 + 79a7cdb9e (rebased onto current main):

  • All extraction/write paths snapshot targets first and join every compensation error (removeSkillDirs is gone)
  • rollbackInstall results are joined at every call site
  • RestoreFiles now removes freshly created trees and restores pre-existing ones (tracked via dirBackup.existed)
  • projectTx is a fixed 64-stripe mutex set (no unbounded map)
  • Snapshot/restore run through os.Root handles for OS-enforced containment (CodeQL path-injection alerts)

env -u TOOLHIVE_DEV go test ./pkg/skills/... ./pkg/groups/ and task lint-fix pass. The full task test run shows two pre-existing local-env failures (SSRF test under TOOLHIVE_DEV=true, streamable proxy test that also fails on clean main) — neither is introduced by this PR.

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.86996% with 121 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.06%. Comparing base (0b8acf2) to head (a0c937a).

Files with missing lines Patch % Lines
pkg/skills/skillsvc/install.go 68.06% 25 Missing and 13 partials ⚠️
pkg/skills/skillsvc/file_snapshot.go 64.70% 17 Missing and 13 partials ⚠️
pkg/skills/skillsvc/sync.go 68.00% 18 Missing and 6 partials ⚠️
pkg/skills/skillsvc/install_oci.go 33.33% 7 Missing and 3 partials ⚠️
pkg/skills/skillsvc/install_extraction.go 82.35% 3 Missing and 3 partials ⚠️
pkg/skills/skillsvc/install_git.go 77.77% 4 Missing and 2 partials ⚠️
pkg/skills/skillsvc/upgrade.go 83.33% 3 Missing and 2 partials ⚠️
pkg/skills/skillsvc/lock.go 60.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6352      +/-   ##
==========================================
+ Coverage   73.04%   73.06%   +0.01%     
==========================================
  Files         745      746       +1     
  Lines       79208    79505     +297     
==========================================
+ Hits        57857    58088     +231     
- Misses      17300    17337      +37     
- Partials     4051     4080      +29     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@samuv
samuv force-pushed the skills-lock/fresh-sync-upgrade branch from 79a7cdb to c776a3d Compare August 18, 2026 09:06
@samuv

samuv commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Patch coverage improved

c776a3dd8 adds failure-path tests for the areas codecov flagged:

  • file_snapshot.go: unit tests for snapshot/restore (missing parent, non-directory target, nested trees, executable-mode preservation, fresh-tree removal, dedupe)
  • Rollback: storage failure during pre-existing-record restore, lock-snapshot corruption after DB create (fresh tree removed, errors joined), force-reinstall group failure restoring the prior lock entry
  • Sync/upgrade: adopt compensation when marking managed fails (entry removed), corrupt-lock failures at both the entry points and per-name reconcile
  • depState: nil-receiver, cycle, and completion semantics
  • groups.RemoveSkillFromGroup: full unit matrix

Also rebased onto current main (7a0c0a51b), which fixes the zizmor static-analysis failure. The remaining uncovered patch lines are deep I/O error branches (e.g. os.Root open/walk failures) that are impractical to inject portably.

@github-actions github-actions Bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Aug 18, 2026
@samuv
samuv requested a review from JAORMX August 18, 2026 12:46
samuv added 6 commits August 19, 2026 15:42
A stale lock/DB snapshot can resurrect uninstalls, and default
sync must expand to newly detected clients like install does.

Signed-off-by: Samuele Verzi <samu@stacklok.com>
Replace context lock reentrancy with a project-root mutex held
through install bookkeeping and dependency materialization.

Signed-off-by: Samuele Verzi <samu@stacklok.com>
Fix snapshot file-copy checks and annotate the transactional
install helpers that exceed the gocyclo budget.

Signed-off-by: Samuele Verzi <samu@stacklok.com>
Replace temp-dir copies with in-memory snapshots and reject
escaping relative paths to clear CodeQL path-injection alerts.

Signed-off-by: Samuele Verzi <samu@stacklok.com>
Extraction paths snapshot every target through os.Root, remove
fresh trees and restore prior content on any failure, surface all
compensation errors, and stripe the project transaction locks.

Signed-off-by: Samuele Verzi <samu@stacklok.com>
Cover snapshot/restore units, rollback storage and lock-snapshot
failures, adopt compensation, group removal, and corrupt-lock
sync/upgrade entry points flagged by patch coverage.

Signed-off-by: Samuele Verzi <samu@stacklok.com>
@samuv
samuv force-pushed the skills-lock/fresh-sync-upgrade branch from c776a3d to e315a1a Compare August 19, 2026 13:42
@github-actions github-actions Bot removed the size/XL Extra large PR: 1000+ lines changed label Aug 19, 2026
@github-actions github-actions Bot added the size/XL Extra large PR: 1000+ lines changed label Aug 19, 2026
JAORMX
JAORMX previously approved these changes Aug 19, 2026
…ync-upgrade

# Conflicts:
#	pkg/skills/skillsvc/service.go
@samuv
samuv requested a review from JAORMX August 19, 2026 15:43
@github-actions github-actions Bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Aug 19, 2026
@samuv

samuv commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

Resolved the merge conflict with main via a merge commit (a0c937a20) — no history rewrite, no code changes beyond the conflict itself: a single import-block hunk in pkg/skills/skillsvc/service.go where #6383's toolhive-core/container/signer import collided with this branch's httperr import (kept both).

The repo dismisses stale approvals on push, so the earlier approval was auto-dismissed; re-requested review. Tests and lint pass on the merged head.

@samuv
samuv merged commit b6aa047 into main Aug 20, 2026
80 of 81 checks passed
@samuv
samuv deleted the skills-lock/fresh-sync-upgrade branch August 20, 2026 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Extra large PR: 1000+ lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants