ADR-010: Same-repo plugin marketplace; dual-ship before template cutover
- Status: Accepted (amended 2026-06-12: cutover executed in PI-165 — see addendum)
- Date: 2026-06-12
- Implements: distribution decision required by #129
Context
The scaffolder copies ~30 .agents/ files into every target project.
Copies drift: a hook fix here never reaches already-scaffolded projects.
The Claude Code plugin ecosystem solves exactly this — plugins update
centrally, and a trusted marketplace offers them to every teammate.
Two questions needed answers: where the marketplace lives, and whether the scaffolder stops copying files the moment the plugin exists.
Decision Outcome
Marketplace lives in this repo. .agents-plugin/marketplace.json at
the repo root lists project-init-workflow with a relative source
(./plugins/project-init-workflow). No second repo to maintain; relative
sources resolve for git-based marketplace adds, which is how scaffolded
settings reference it. A dedicated company marketplace can supersede this
later without changing the plugin.
Dual-ship first. Scaffolded projects keep receiving file copies (the
active wiring), and settings.json additionally registers the
project-init marketplace via extraKnownMarketplaces — teammates who
trust the project get the plugin offered, not force-enabled:
- The plugin is deliberately not in
enabledPlugins: itshooks/hooks.jsonwires the same guard scripts the scaffoldedsettings.jsonalready wires, and enabling both would double-fire every PreToolUse/SessionStart hook (twice the lint latency on every commit). - Cutover (templates shrink to project-specific files, plugin becomes the single source of hooks/skills) is a follow-up once the plugin has real-world mileage. At that point scaffolds enable the plugin and stop copying the shared payload.
Plugin contents = the project-agnostic subset. Every non-.tmpl
SKILL.md tree and every hook script. Templated components (e.g.
plan/SKILL.md.tmpl, settings, rules) are project-specific by definition
and stay scaffold-only. tools/sync_plugin.py (just sync-plugin)
regenerates the plugin payload from templates/; a contract test fails CI
when the copies drift, so the duplication cannot rot silently.
Consequences
- A hook/skill fix shipped in the plugin reaches every project that
enabled it without re-scaffolding; projects that didn't still get fixes
through
project-init upgrade(PI-142). - Until cutover, template edits to shared skills/hooks require
just sync-plugin— enforced by CI, one command. - Plugin versioning starts at 0.1.0, independent of the scaffolder version; bump it when the payload changes behavior.
Addendum (2026-06-12, PI-165)
The owner confirmed the project has no users, so the dual-ship transition window closed the same day it opened. Scaffolds are now plugin-first:
enabledPluginsincludesproject-init-workflow@project-init; the duplicated hook wiring is gone from scaffoldedsettings.json(the double-fire concern above is thereby resolved).- The shared payload moved to
templates/fallback/, rendered only with--no-plugin(offline/no-trust fallback) — templates/base keeps justdag_workflow.py(the lifecycle scripts exec it) and project-specific components. tools/sync_plugin.pystill derives the plugin and the Codex/Gemini.agents/skillscopies from the repo source of truth; direction did not invert because the source stayed intemplates/.- Upgrade backfills
no_plugin=truefor pre-cutover records, so existing dual-ship projects re-render faithfully with their copies intact.