fix(install): pre-validate provides.files + rollback on hook gate fail#96
Merged
fix(install): pre-validate provides.files + rollback on hook gate fail#96
Conversation
Issue #89: install previously left orphan symlinks under ~/.claude/{skills,agents,bin,...} when validation failed mid-flow. Two distinct partial-state shapes: 1. provides.files entry N's source missing → entries 1..N-1 already linked, per-type primary symlinks already in place. 2. Hook gate trips after symlinks landed → symlinks orphaned, settings.json untouched, DB unaware. Implement both fixes from the issue: - **Pre-pass validation (option 1):** in createArtifactSymlinks, walk every provides.files entry up front and assert each source exists in the package. If any missing, return immediately with filesMissingSource populated and zero filesystem mutations. The most common failure mode (manifest typo, repo drift) is now caught before any symlink lands. - **Rollback tracking (option 2):** every symlink (per-type primary, CLI bins, provides.files target) and shim is recorded in an ArtifactSymlinkRecord returned alongside filesCreated. New rollbackArtifactSymlinks() helper unlinks them best-effort. install.ts calls it on the hook-gate failure path, so a missing hook target tears down everything that was placed before the gate ran. Tests (test/commands/install.test.ts, +2): - Manifest with two provides.files, second source missing → install fails with provides.files diagnostic, the FIRST entry's target is also absent (pre-validation prevented it being created), and the per-type skill symlink is absent. - Manifest where provides.files succeeds but a separate hook command references an unlanded file → install fails with hook diagnostic, skill symlink AND provides.files target both rolled back. Closes #89. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address Luna's feedback on PR #96. - Tighten rollbackArtifactSymlinks JSDoc: was overselling postinstall coverage but install() never calls rollback on postinstall failure. Postinstall recovery is a strictly larger fix (also requires unregistering hooks from settings.json + DB cleanup); track separately. Doc now reflects what actually happens. - Surface non-ENOENT errors during rollback via console.warn instead of silent .catch. ENOENT still tolerated (best-effort across all entries); permission-denied or other unexpected failures now leave the user aware of orphans they need to inspect manually. - Tighten hook-gate rollback test: add provides.cli to the fixture so the bin symlink + PATH shim are actually created and the test asserts both are torn down. Previously the fixture had no CLI declaration, so the shim-rollback assertion would have been vacuously true. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Addressed Luna's review in commit 514bd47.
Suite stable at 624/624. Re-pinging. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #89.
`install` previously left orphan symlinks under `~/.claude/{skills,agents,bin,...}` when validation failed mid-flow. Two distinct partial-state shapes:
User had no clean recovery path: `arc remove` couldn't drive cleanup (no DB entry), so manual rm was the only option.
Changes
`src/lib/artifact-installer.ts` — implement both fixes from the issue:
`src/commands/install.ts` — call `rollbackArtifactSymlinks(symlinkResult.record)` on the hook-gate failure path. Missing hook target now tears down everything that was placed before the gate ran.
Tests (test/commands/install.test.ts, +2)
Suite: 624/624 pass, tsc clean.
🤖 Generated with Claude Code