fix(engine): version-gate vendored plugin assets in check-bumps - #123
Merged
Conversation
check-bumps compared only ARTIFACT digests (skills/commands) recorded in index.json. A bundle's vendored plugin assets — vendor/plugins/<bundle>/**, a stark-skills plugin's own tools/ + config — are not artifacts, so they carried no digest and participated in no gate. A change confined to a plugin's tools/** therefore left every artifact digest untouched: the gate printed "OK: no un-bumped source changes", publish.sh left the bundle version alone, and the bundle shipped modified content under a version consumers already had and would never re-fetch. The change was invisible on every installed machine. Hit live 2026-07-27: a stark-gh lib/git.ts fix shipped un-bumped at 0.1.10 and needed a hand-applied bump to reach anyone (#122). - digest.Files / digest.Dir: deterministic content hash over a file set, LF- normalized to match what build writes to dist, path-sensitive so a rename counts. - index.Index.PluginAssets: {bundle, version, digest} per plugin-backed bundle. New optional field; consumers ignore unknown fields per the package contract. - check-bumps recomputes the digest from vendor/plugins/<bundle> and holds it to the same immutability rule, keyed <bundle>/plugin-assets/<bundle> so it can never collide with an artifact key. publish.sh needs no change: its auto-bump loop parses violation lines as ` - <bundle>/...`, so a plugin-asset violation now patch-bumps that bundle automatically instead of shipping silently. This affects FIVE bundles, not one — stark-analyze, stark-gh, stark-implement, stark-ops, stark-plan all carry vendored plugin assets. CLAUDE.md claimed only stark-gh did; corrected. Co-Authored-By: Claude Fable 5 <[email protected]>
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.
The hole
check-bumpscompared only artifact digests (skills/commands) recorded inindex.json. A bundle's vendored plugin assets —vendor/plugins/<bundle>/**, a stark-skills plugin's owntools/+ config — are not artifacts. They carried no digest and participated in no gate.So a change confined to a plugin's
tools/**left every artifact digest untouched. The gate printed "OK: no un-bumped source changes",publish.shleft the bundle version alone, and the bundle shipped modified content under a version consumers already had — and would never re-fetch. The change was invisible on every installed machine.Hit live on 2026-07-27: the stark-gh
lib/git.tsfix shipped un-bumped at 0.1.10 and only reached anyone because I caught it by hand in #122.Correction: this affects five bundles, not one
CLAUDE.mdclaimedvendor/plugins/existed "currently forstark-gh" alone. It does not —stark-analyze,stark-gh,stark-implement,stark-ops,stark-planall carry vendored plugin assets. I repeated that claim in #122; it understated the blast radius 5×. Corrected in this PR.The fix
digest.Files/digest.Dirbuildwrites todist/, length-prefixed fields so no(path, content)concatenation can collide, path-sensitive so a pure rename countsindex.Index.PluginAssets{bundle, version, digest}per plugin-backed bundle. New optional field; the index package's contract is that consumers ignore unknown fields, so existing readers are unaffectedcheck-bumpsvendor/plugins/<bundle>and applies the same immutability rule, keyed<bundle>/plugin-assets/<bundle>so it can never collide with an artifact key<bundle>/<type>/<name>publish.shneeds no change — its auto-bump loop parses violation lines as- <bundle>/..., so a plugin-asset violation now patch-bumps that bundle automatically. The manual step I had to perform in #122 is exactly what this makes unnecessary.Rollout is graceful: an
index.jsonpredating the field contributes no previous rows, so the first publish after this merges records them rather than failing the repo.Verification
The regression test is
TestCheckBumpsFailsWhenPluginToolChangedWithoutABump— a repo whose committed index records a stale plugin-asset digest at an unchanged version. Mutation-checked: with the new gate line disabled it fails withgot 0is precisely the old silent-pass. Restored, it passes.Plus controls that keep the gate satisfiable and honest: a bumped change passes, an unchanged tree passes, a missing
vendor/plugins/<bundle>digests to the stable empty set rather than erroring, CRLF checkouts don't force spurious bumps, a rename does count, the key can't collide with any artifact type, and a legacy index parses cleanly.go test ./...— 26 packages, all passstark build --check— OK: no driftstark check-bumps— OK: no un-bumped source changesVERSION→ 0.8.2🤖 Generated with Claude Code