Hide pulled skill artifacts from local builds listing#4971
Hide pulled skill artifacts from local builds listing#4971
Conversation
Introduce a file-backed index at <ociStoreRoot>/builds.json that records which tags in the shared local OCI store were produced by a local Build. The index lives next to the OCI layout, uses atomic temp-file + rename writes, and serializes access with an in-process mutex. This is a self-contained addition — no callers yet. Subsequent commits wire it into the skill service so ListBuilds can filter out artifacts that were only pulled (by install or the content API) for caching.
Skills pulled into the shared OCI store by install or the content API were tagged for caching and then surfaced by ListBuilds, making every previewed or installed remote skill look like a local build. Wire the build provenance index into the skill service: - New() initialises provenance from ociStore.Root() and runs a one-shot migration that grandfathers pre-existing simple tags (no '/', ':' or '@') so existing users do not lose their builds. - Build records each tagged artifact after the ociStore.Tag call. - DeleteBuild forgets the provenance entry after the store delete. - ListBuilds iterates provenance entries instead of every OCI tag and prunes stale entries whose tags no longer resolve. OCI pulls continue to tag the local store as a cache but are no longer visible to ListBuilds because those paths deliberately skip provenance recording.
Add regression tests that simulate the real Pull tagging side effect for both GetContent and installFromOCI and assert ListBuilds stays empty afterwards. Annotate each registry.Pull call site with a comment stating that skipping build provenance is intentional, so these paths are not silently 'fixed' later by adding a Record call.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4971 +/- ##
==========================================
+ Coverage 69.52% 69.56% +0.03%
==========================================
Files 551 552 +1
Lines 55918 56109 +191
==========================================
+ Hits 38879 39032 +153
- Misses 14042 14061 +19
- Partials 2997 3016 +19 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
So, quick thought on the Before renaming anything though, I'd like to push back on the sidecar itself. What if we encoded the "I built this" signal directly on the OCI manifest as an annotation? Something like Couple of reasons I like that better. Single source of truth, no sidecar to drift, no stale-entry pruning on list, no atomic-write path to maintain. No migration heuristic either, which is the part of the current PR that worries me most... One trade-off to flag: push mutates the manifest before sending, so the pushed digest differs from the local one. Reasonable cost for a local-intent marker, but worth naming. wdyt? |
Fair points, wanted to see the annotation approach in the flesh before answering. I agree, it's noticeably more straightforward and less mechanical than the sidecar: single source of truth, no builds.json to drift, no atomic-write path, no stale-entry pruning, and crucially no looksLikePulledRef grandfather heuristic that could silently hide an existing tag. On the "provenance" name: you're right, that's a loaded term in the OCI/supply-chain world and buildIndex would've been the honest rename. Moot now that the sidecar is gone. On the push-mutates-the-manifest trade-off you flagged: turned out not to bite us. Stamping the marker at the descriptor level in index.json (instead of inside the manifest blob) means the artifact digest is unchanged, and oras-go strips descriptor annotations when it resolves a reference by digest, which is exactly what push does. So the marker is genuinely local-only, no pre-push strip step needed, and pulled digests match exactly. Same mechanism is why install/content pulls don't accidentally inherit the marker either: Registry.Pull tags by digest, gets a plain descriptor back, so pulled tags are invisible to ListBuilds for free. Opened #5000 with that implementation and closing this one as superseded. The one honest gap vs the sidecar: pre-feature local tags won't show up in ListBuilds until rebuilt, no migration. I think that's the right trade (honest gap > silent misclassification) but flagging it explicitly. I don't think we have stable user based tho |
|
superseed by #5000 |
Summary
GET /api/v1beta/skills/contentand OCI-based install both tag the shared local OCI store as a cache — thenListBuilds(GET /api/v1beta/skills/builds) iterates every tag and reports them as local builds, so every previewed or installed remote skill pollutes the "what have I built locally?" view.<ociStoreRoot>/builds.jsonthat tracks only tags produced byBuild.ListBuildsnow iterates this index instead ofociStore.ListTags(); pulled tags stay in the store as a cache but are invisible to the builds listing.Buildrecords,DeleteBuildforgets,ListBuildsprunes stale entries on the fly. OCI pulls (install + content) deliberately skip the index — comments at each site explain why.New()runs a one-shot migration that grandfathers pre-existing simple tags (no/,:or@) so users with artifacts from before this change keep seeing their builds.Type of change
ListBuilds/GET /api/v1beta/skills/builds)Test plan
task testonpkg/skills/skillsvc/...)task lint-fix— 0 issues)ListBuildsstill returns what it returned before the callChanges
pkg/skills/skillsvc/provenance.gobuildProvenance(Record/Forget/List/Has/Seed/Exists) with atomic temp-file + rename writes and in-process mutexpkg/skills/skillsvc/service.goprovenance *buildProvenancetoservice, initialise it inNew()fromociStore.Root(), and addmigrateBuildProvenance+looksLikePulledReffor the one-shot grandfather migrationpkg/skills/skillsvc/build.goBuildrecords provenance after tag;ListBuildsiterates provenance entries and prunes stale ones;DeleteBuildforgets provenance after the store deletepkg/skills/skillsvc/content.goregistry.Pullsite clarifying that content-preview pulls intentionally skip provenancepkg/skills/skillsvc/install_oci.gopkg/skills/skillsvc/provenance_test.golooksLikePulledRefpkg/skills/skillsvc/build_test.goTestListBuildscases (pulled tags hidden, pull + build mix, migration, stale-entry pruning), newTestDeleteBuildcase (provenance forgotten), newTestBuild_RecordsProvenancepkg/skills/skillsvc/content_test.goremote pull does not pollute ListBuildscasepkg/skills/skillsvc/install_oci_test.goTestInstallFromOCI_DoesNotLeakIntoListBuildsDoes this introduce a user-facing change?
Yes — the response of
GET /api/v1beta/skills/buildschanges.thv skill build(or equivalentPOST /api/v1beta/skills/build) appear. Pulled skills stay on disk as a cache (transparent speed-up for re-previews / re-installs) but are no longer surfaced as builds.Existing users are migrated automatically on first service start: simple tags already in the store are grandfathered as builds, fully-qualified OCI references are treated as pulls and omitted.