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{} }