feat(skillpack): SKILL.md-subscription battery
New additive, nil-safe battery for subscribing to skill packages in the Anthropic agent-skills format (SKILL.md manifest + bundled files): - Manifest/ParseManifest: SKILL.md frontmatter+body parse & validation (name/description required, allowed-tools passthrough, kebab/length limits) - Tree/Pack/LoadPack: self-contained file set, order-independent content digest (the pin identity + change signal), bundled-file listing, traversal- safe staging - Source (DirSource, GitSource): Fetch returns tree + resolved ref; git clones to temp, reads subpath into memory, cleans up (self-contained tree) - Subscription + Store + content-addressed PackCache, with Memory defaults - Syncer: Subscribe pins; Check records a PENDING update but never moves the pin; Apply is the only re-pin (supply-chain guard — upstream can't silently change what an agent runs) - Activate: resolved packs -> majordomo agent.Skill (catalog instructions + one skill_use tool) for progressive disclosure; Stage materializes files Third distinct 'skill' concept, deliberately separate from executus/skill (saved-agent noun) and majordomo/skill (eager capability bundle). Mort-side wiring (convars, .skillpack commands, Agent.SkillPacks, allowed-tools shim) is a later, separate step. Full unit + hermetic local-git tests; gofmt/vet clean; race-tested. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package skillpack
|
||||
|
||||
import "time"
|
||||
|
||||
// Subscription is a host's persisted "I track this pack, pinned here" record. It
|
||||
// is metadata only — the pinned pack's bytes live in a PackCache keyed by
|
||||
// PinnedDigest. A subscription is only ever advanced to new content by an
|
||||
// explicit Apply (see Syncer): a sync records a PendingDigest, it never moves
|
||||
// the pin. That is the supply-chain guard — a compromised or careless upstream
|
||||
// cannot change what an agent runs without a human re-pin.
|
||||
type Subscription struct {
|
||||
// ID is a stable host-assigned identifier.
|
||||
ID string
|
||||
// Name is the pack's manifest name (unique per host); what an agent lists in
|
||||
// its SkillPacks and what skill_use receives.
|
||||
Name string
|
||||
// Description is the pinned manifest's description, cached so the catalog
|
||||
// renders without opening the PackCache.
|
||||
Description string
|
||||
|
||||
// Source coordinates.
|
||||
SourceKind string // "dir" | "git"
|
||||
SourceURL string // dir path or git URL
|
||||
Subpath string // git subpath, if any
|
||||
// TrackRef is the git commit-ish the host follows (branch/tag/SHA); empty =
|
||||
// default branch. Sync fetches THIS; the pin only moves on Apply.
|
||||
TrackRef string
|
||||
|
||||
// Pinned* describe the currently-active content.
|
||||
PinnedDigest string // content digest = PackCache key + change signal
|
||||
PinnedSourceRef string // source's resolved ref (git SHA) — provenance
|
||||
PinnedAt time.Time
|
||||
PinnedBy string
|
||||
|
||||
// Pending* describe an update a sync found but has NOT applied. Empty
|
||||
// PendingDigest = no pending update. A pending digest equal to the pinned
|
||||
// one is impossible by construction (Syncer clears it).
|
||||
PendingDigest string
|
||||
PendingSourceRef string
|
||||
PendingAt time.Time
|
||||
|
||||
// Enabled lets a host keep a subscription but deactivate it without
|
||||
// deleting the pin/history.
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
// HasPending reports whether a sync found an unapplied update.
|
||||
func (s *Subscription) HasPending() bool {
|
||||
return s.PendingDigest != "" && s.PendingDigest != s.PinnedDigest
|
||||
}
|
||||
|
||||
// pinTo advances the active pin to a fetched pack and clears any pending state.
|
||||
// Used by initial pin and by Apply.
|
||||
func (s *Subscription) pinTo(p *Pack, sourceRef, by string, now time.Time) {
|
||||
s.Name = p.Manifest.Name
|
||||
s.Description = p.Manifest.Description
|
||||
s.PinnedDigest = p.Digest
|
||||
s.PinnedSourceRef = sourceRef
|
||||
s.PinnedAt = now
|
||||
s.PinnedBy = by
|
||||
s.PendingDigest = ""
|
||||
s.PendingSourceRef = ""
|
||||
s.PendingAt = time.Time{}
|
||||
}
|
||||
Reference in New Issue
Block a user