Context
The tool-surface-contract mission (01KV2K2P) established C-006 / FR-016: the
plugin-bundle projection and validation path must be inert and declarative — it
must never auto-install a bundle and never publish it to a marketplace.
The original regression test injected a marketplace_publish symbol and asserted
the guard fails; reverting it restored green.
That guard (tests/specify_cli/tool_surface/providers/test_plugin_bundle.py,
test_no_install_or_publish_tokens_in_source) was implemented by scanning bundle
module source for a list of forbidden tokens, which included the bare token
marketplace. This was a reasonable proxy for "no marketplace publish" at the
time, because no code legitimately needed to name a marketplace at all.
This mission (01KV3NGS) introduced FR-023 (Claude) and FR-029 (Codex):
the bundle build must generate a marketplace.json catalog descriptor
alongside the bundle so a user can later run, by hand:
claude plugin marketplace add <repo-url-or-path>
claude plugin install spec-kitty@spec-kitty-plugins
marketplace.json is a static, declarative manifest written into the output
staging tree (dist/spec-kitty-plugins/...). Writing it performs no install,
no publish, no network I/O — it is inert output, exactly like plugin.json.
The bare-marketplace token, however, cannot distinguish "writes a marketplace
descriptor" (_write_marketplace_json, the string literal "marketplace.json")
from "publishes to a marketplace" (marketplace_publish). After WP05/WP06
landed, the guard tripped on the legitimate descriptor write — a false positive
that surfaced only once all nine lanes were merged (the per-WP review suites do
not run the tests/specify_cli/tool_surface/providers/ guard).
Decision
Narrow C-006 to its actual intent: prohibit install/publish actions; permit declarative descriptor output.
Concretely, the bare marketplace token is removed from the guard's
_FORBIDDEN_TOKENS. The remaining tokens (publish, auto_install,
autoinstall, register_plugin, enable_plugin, upload) continue to enforce
the prohibition — in particular the original marketplace_publish regression is
still caught by the publish token, and any *_publish / *upload* /
*auto_install* action symbol still fails the guard.
What remains prohibited (unchanged):
- Auto-installing a generated bundle into a live agent directory.
- Publishing/uploading a bundle to a remote marketplace.
- Any networked or side-effecting distribution action inside
tool_surface/bundles/.
What is now explicitly permitted:
- Writing a declarative
marketplace.jsoncatalog descriptor into the bundle'sdist/staging tree (FR-023, FR-029). It is inert data the operator consumes manually viamarketplace add.
Consequences
- WP05/WP06 code is unchanged; the bundle projectors remain declarative.
- The guard still fails closed on real install/publish actions.
- The "merge-publish layer boundary" ADR
(
2026-06-05-1-merge-publish-layer-boundary.md) is unaffected: publishing remains a separate, explicit, out-of-band concern — generating a catalog descriptor is not publishing. - Future tightening option (not taken now): relocate descriptor generation into a
dedicated distribution layer so
tool_surface/bundles/need not name a marketplace at all. Deferred as unnecessary given the narrowed, intent-aligned guard.