Follow-up to #233 (the "related consolidation" that issue set aside).
1. Domain logic living in the CLI
internal/cli/marketplace.go holds three pieces of internal/marketplace domain logic — parseMarketplaceSource, deriveMarketplaceSlug, sanitizeSlug — plus the fetch + declared-name + re-slot body of addMarketplaceSource. The poll engine (plugin_poll.go, same package) calls parseMarketplaceSource too, while nothing under internal/marketplace can use any of it.
Proposal: move the three helpers there with their tests, and consider a marketplace.FetchInto(home, src, rawURL) returning (name, headSHA) that owns the fetch and the cache re-slot (reslotMarketplaceCache, added by #233's fix), leaving the CLI the TOML/state writes and the printing.
2. A second reader/writer for marketplaces/*.toml
marketplaceTOMLSpec (marketplace.go) gives marketplaces/*.toml a second shape alongside source.MarketplaceSpec + marketplaceStrictFile (internal/source/loader.go). The split is deliberate and documented (head_sha/name are fetch-cache metadata, not canonical schema — #171), but three structs describe one file. Worth deciding whether the CLI shape should sit beside the canonical one so a fourth key cannot be added to one and missed by the others. Docs to update with it: docs/components.md, plus docs/architecture.md §9 if the network-boundary description moves.
3. Orphan caches left by pre-fix re-adds
Before #233's fix, every re-add of a marketplace whose declared name differs from its URL slug left a duplicate tree under .state/cache/marketplaces/<slug>. Those pre-fix orphans are consumed only when that marketplace is re-added (the next add pulls/copies into the orphan and swaps it in); nothing else touches them, marketplace remove cannot reach them, and searchAllMarketplaces offers them to a bare-id plugin add as unregistered marketplaces. A doctor check (a cache dir with no marketplaces/<name>.toml) and/or a cleanup for orphans under .state/cache/marketplaces is a reasonable item here.
Deliberately out of scope
marketplace add is last-writer-wins when two sources declare the same name (TOML, state entry and, since #233's fix, the cache). Whether that should be refused is its own question.
Follow-up to #233 (the "related consolidation" that issue set aside).
1. Domain logic living in the CLI
internal/cli/marketplace.goholds three pieces ofinternal/marketplacedomain logic —parseMarketplaceSource,deriveMarketplaceSlug,sanitizeSlug— plus the fetch + declared-name + re-slot body ofaddMarketplaceSource. The poll engine (plugin_poll.go, same package) callsparseMarketplaceSourcetoo, while nothing underinternal/marketplacecan use any of it.Proposal: move the three helpers there with their tests, and consider a
marketplace.FetchInto(home, src, rawURL)returning(name, headSHA)that owns the fetch and the cache re-slot (reslotMarketplaceCache, added by #233's fix), leaving the CLI the TOML/state writes and the printing.2. A second reader/writer for
marketplaces/*.tomlmarketplaceTOMLSpec(marketplace.go) givesmarketplaces/*.tomla second shape alongsidesource.MarketplaceSpec+marketplaceStrictFile(internal/source/loader.go). The split is deliberate and documented (head_sha/nameare fetch-cache metadata, not canonical schema — #171), but three structs describe one file. Worth deciding whether the CLI shape should sit beside the canonical one so a fourth key cannot be added to one and missed by the others. Docs to update with it:docs/components.md, plusdocs/architecture.md§9 if the network-boundary description moves.3. Orphan caches left by pre-fix re-adds
Before #233's fix, every re-add of a marketplace whose declared name differs from its URL slug left a duplicate tree under
.state/cache/marketplaces/<slug>. Those pre-fix orphans are consumed only when that marketplace is re-added (the next add pulls/copies into the orphan and swaps it in); nothing else touches them,marketplace removecannot reach them, andsearchAllMarketplacesoffers them to a bare-idplugin addas unregistered marketplaces. Adoctorcheck (a cache dir with nomarketplaces/<name>.toml) and/or a cleanup for orphans under.state/cache/marketplacesis a reasonable item here.Deliberately out of scope
marketplace addis last-writer-wins when two sources declare the same name (TOML, state entry and, since #233's fix, the cache). Whether that should be refused is its own question.