Skip to content

Project-scoped adoption resolution for the Claude Code plugin (marketplace) channel #392

Description

@rucka

Story Statement

As a Claude Code user who installed pair through the marketplace channel
I want pair's skills to resolve my project's .pair/adoption/ (or none at all)
So that skill steps apply my project's decisions instead of the pair repository's own

Epic Context

Parent Epic: Skill marketplace + Quickstart + external KB scaffold #213
Status: Todo
Priority: P1 (Should-Have)

Status Workflow

  • Refined: Story is detailed, estimated, and ready for development
  • In Progress: Story is actively being developed
  • Done: Story delivered and accepted

User Value

User Benefit: skills behave against the consuming project's reality (its PM tool, its risk deltas, its coverage baseline) rather than pair's.
Business Impact: the marketplace channel (#277) is the zero-setup entry point; today it silently applies pair's own adoption decisions, which undermines trust in the channel's output.
Visible UI Value: a skill run on the marketplace channel reports the consuming project's PM tool (or the documented "no PM tool configured" HALT), never foomakers/pair.

Rough Sizing

Story Points: [M(3) — to confirm in refinement]
Confidence: Low
Reasoning: the mechanism is unknown — Claude Code plugins are copied into ~/.claude/plugins/cache and cannot reach outside the plugin root, so adoption resolution likely needs an explicit project-relative lookup convention in the skill corpus rather than a manifest change. Spike first.

Initial Scope

Likely In Scope

  • A resolution convention for .pair/adoption/ that prefers the consuming project's copy (CWD-relative) over the plugin-cache copy, applied across the skill corpus's adoption reads.
  • Deciding what "absent adoption" means on the plugin channel, so each skill's documented graceful-degradation path actually fires instead of silently reading pair's files.
  • Adoption WRITES, not just reads (added from Skill marketplace: marketplace.json + plugin packaging for native Claude Code install #277's review round 2). /pair-capability-record-decision is by its own frontmatter "the sole writer of adoption/context-map files" and resolves every target skill-relative (../../../.pair/adoption/...). On the marketplace channel both resolutions fail, differently: resolved as written, the user's ADR/ADL is written inside the plugin cache (verified layout: <cache>/pair/pair/<sha>/.pair/adoption/... exists), invisible to their repository and destroyed by the next /plugin update — silent loss of a recorded decision; resolved CWD-relative, a half-initialised .pair/adoption/ skeleton appears in a project with no adoption files. So this story owns the write destination (and the "no adoption present" behaviour for a write) as well as the read resolution. Skill marketplace: marketplace.json + plugin packaging for native Claude Code install #277 ships documentation only: the integration page states that decisions must be recorded on the CLI channel.
  • Spike: whether Claude Code exposes a project-root anchor to a plugin skill at runtime.

Likely Out of Scope

Open Questions

Definition of Done Expectations

Standard DoD Requirements (to be detailed during refinement):

  • Acceptance criteria met and verified on a real marketplace install
  • Docs updated: the adoption-resolution callout in integrations/claude-code.mdx reflects the fixed behavior
  • Guard/test coverage for the resolution rule

Dependencies

Story Dependencies: #277 (marketplace channel, shipped in PR #386)
Epic Dependencies: epic #213

Notes

Origin: independent code review of PR #386 (story #277). Verified during that review:

  • plugin.json uses source: "./", so the whole repository — including .pair/adoption/ — is copied into ~/.claude/plugins/cache.
  • Skill bodies read adoption by relative path (e.g. .claude/skills/pair-process-review/SKILL.md../../../.pair/adoption/tech/tech-stack.md; the PM-tool resolution convention reads .pair/adoption/tech/way-of-working.md).
  • pair's own way-of-working.md states "Github Projects is adopted ... The project is pair under the github organization foomakers", and tech/risk-matrix.md declares Active: risk.

Consequence on the marketplace channel: adoption files are present (pair's), so no graceful-degradation path fires and pair's decisions are applied to the consuming project. #277 documented this as "behaves as if adoption files were absent", which is the inaccuracy this story fixes structurally (PR #386 corrects the wording).


Scope extension — the setup assistant becomes the marketplace channel (agreed 2026-07-31)

Decided with the maintainer while reviewing PR #386. This story absorbs the shape; no new story is opened.

The shape

The marketplace ships exactly one skill: a setup assistant. It is KB-free by construction — it installs the knowledge base rather than reading it — and everything else arrives through pair-cli into the project, where relative KB paths resolve correctly.

That resolves what #386 could only document: two channels, "install one, never both", and nothing detects the wrong state. One channel, one payload, no overlap, nothing for the user to get wrong.

Why "isolated skills" cannot be the payload — measured, not assumed

Of pair's 42 installed skills, exactly 1 has no reference to the knowledge base, and it is agent-browser — a third-party skill, not pair's own. Every pair skill points at .pair/knowledge/ or .pair/adoption/ by relative path. So a marketplace payload of "the skills that need no installation" is empty today, and the setup assistant has to be written, not carved out of the corpus.

What the assistant does

  1. Bootstrap the target. No git repository → create it (git init) from the skill itself; the CLI's command surface stays unchanged.
  2. Get pair-cli the cheapest correct way. A package.json present ⇒ install it as a local dev dependency; otherwise npx @foomakers/pair-cli — no install at all.
  3. Run the install, then hand off to the normal skills now present in .claude/skills/.
  4. Default source, overridable. A default KB source, with the repository and configuration parameters (a different source, an org KB) passable as arguments — composing the Guided/Quick Setup Convention (Guided/Quick Setup Convention — unify interactive vs default resolution across setup skills #276) cascade, never a bespoke resolution order.
  5. Cover the other commands too, not only install: update and the rest. The goal is an assistant for installing and maintaining a pair setup, reading the current configuration state, proposing the right next action, and surfacing anomalies. It composes the 8 existing CLI commands — install, update, kb-info, kb-validate, kb-verify, validate-config, package, update-link, of which three are already diagnostic — rather than reimplementing their logic. Same shape as /pair-next: read state, recommend the step.

Costs this extension carries, so neither is discovered later

  • Excluding the setup skill from the CLI install is NOT a config tweak. The skills registry declares only source, behavior, flatten, prefix, description, targets — there is no exclude. SyncOptions.include exists but is documented for behavior: mirror (the skills registry runs overwrite) and is not honoured anywhere in the copy pipeline — grep finds no use in ops/copy/*.ts. So this needs a real exclusion mechanism in the registry plus support in content-ops. Small, but not free — the same shape as the flattenDepth threading in tech-debt: pair update mis-installs a skill's nested references/ subdir (flattened out of the skill, both links broken) #407 (SyncOptions → the copy pipeline → the CLI registry mapping, plus tests), with the filter belonging in collectFiles. Verifiable on its own, so it does not have to wait for the setup skill to exist: a smoke scenario can declare an exclusion and assert the file is absent after install, alongside the unit tests. One caveat for the task: the skills registry runs behavior: overwrite, which performs no cleanup — so an excluded skill that is ALREADY present in a repo is not removed by a later install. The exclusion prevents writing it, not having it.
  • Without that exclusion the duplicate is guaranteed, and it is not benign. A plugin lives in ~/.claude/plugins/cache; the CLI writes .claude/skills/ in the project. Two trees, so the CLI cannot overwrite the plugin copy and "install only if absent" cannot be decided by looking at the project. Both copies then answer the same bare /pair-… name, and per [US-277] feat: .claude-plugin marketplace + plugin manifests with dataset catalog guard #386's own ADL the both-installed resolution order has never been observed — verified only with the plugin as sole source. It is an execution ambiguity, not a cosmetic duplicate.
  • What the marketplace stops being. It no longer hands over pair's skills; it hands over an installer. "Try pair without installing anything" becomes impossible — a deliberate consequence of every pair skill needing the KB, to be stated in the docs rather than discovered.

Effect on PR #386

#386 is held, not rejected. It is green, and honest about its own limits (the channel table, the both-channels warning, the pointer here). But it delivers two channels with a documented trap where this story delivers one without it. The maintainer's call: #386 waits for this story and they merge together.

Metadata

Metadata

Assignees

Labels

user storyWork item representing a user story

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions