Skip to content

install --source honours the source KB's declaration and reports what it actually did (consolidates #397) #396

Description

@rucka

Story Statement

As a maintainer installing an external KB with install --source
I want the install to honour what the source declares and to report what it actually did
So that a legitimate external KB installs with its own namespacing and ends on a green summary, instead of applying the consuming project's prefix and reporting missing registries as failures

Where: apps/pair-cli/src/commands/install/handler.ts (summary + registry resolution), apps/pair-cli/src/config/loader.ts (source-config resolution), and apps/website/content/docs/customization/external-kb.mdx (which documents both defects as known limitations today).

Consolidates #397 (maintainer decision, 2026-08-05). The two defects were filed separately from the same #391 review, but they touch the same handler, and both are documented as caveats on the same docs page. Separate cards would have meant two PRs on one file with a rebase between them, and two passes over the same paragraph.

Epic Context

Parent Epic: none — standalone defects from the #391 review
Status: Refined
Priority: P1 (Should-Have) — confirmed: both defects sit on the primary path of the external-KB feature

Classification

risk:yellow · cost: green (not projected — Active: risk only)

Matrix — per dimension
Dimension Tier Source Note
Service/domain criticality yellow KB default tech/risk-matrix.md declares no ## Criticality Table → Medium default (D21)
Change/diff risk yellow story scope Two behaviours in one handler plus a config-resolution change with a documented precedence rule. Contained, but it changes what install reports and what it applies
Business impact yellow subdomain class Integration & Process Standardization (Supporting) — install/distribution
Security relevance green path heuristic Reads a config file from the source KB; no credentials, no execution of source content
Coupling balance green subdomain volatility + integrations No new cross-context integration

Tier = yellow (max rule). Gate checks for 🟡: lint + type + build + unit.

Problem — two defects, one surface

A — registries absent from the source are reported as failed (was #396)

Installing a KB that legitimately does not ship every registry (an external KB ships knowledge + skills, never adoption) ends in a red summary:

! Installation finished with errors (2 ok, 3 failed, 79ms)

with exit code 0 and no explanation of which registries were absent or why that is fine. A registry the source does not contain is not an error — it is nothing to install. There is also no way to declare the intent up front.

B — the source KB's own registry declaration is ignored (was #397)

An external KB declares its registries in its own pair.config.json (e.g. skills.prefix: "acme-kb"), but install --source <kb> resolves the consuming project's config (loadConfigWithOverrides), never the source's. With a default consuming project, the KB's skills install as pair-example-skill instead of acme-kb-example-skill, and the maintainer's declared namespacing silently does not apply. Today the only workaround is copying the KB's pair.config.json into the consuming project by hand.

Why they are one card

Both are on install/handler.ts; B additionally touches config/loader.ts. Both are currently written up as known limitations on the same docs page (external-kb.mdx), along with the caveats in the scaffolded README and the seed skill. Fixing one and not the other leaves that page half-corrected — and a reader with a caveat that no longer applies is worse served than one with two that do.

Acceptance Criteria

Functional Requirements

Given-When-Then Format:

  1. Given a source KB that ships only knowledge + skills
    When install --source runs
    Then the summary reads as success and names the skipped registries with the reason, e.g. ✓ Installation complete (2 ok, 3 skipped — not shipped by this source); no registry is counted as failed

  2. Given a registry present in the source that genuinely fails to install
    When install runs
    Then it is still reported as failedskipped never masks a real failure, and the exit code reflects it

  3. Given an external KB declaring skills.prefix: "acme-kb"
    When a default-configured project runs install --source <kb>
    Then its skills install as acme-kb-<skill> — the source's declaration is honoured without the consumer copying any config

  4. Given a consuming project that has deliberately overridden the registry
    When install runs
    Then the project's own configuration wins, and the precedence is documented (source declaration < consuming project override) — no silent surprise in either direction

  5. Given the summary
    When it distinguishes ok / skipped / failed
    Then the exit code follows the failures only, so automation can trust it (today it is 0 while the text says "with errors" — the two disagree)

  6. Given the documentation
    When this story lands
    Then the caveats are removed from external-kb.mdx, the scaffolded README template and the seed skill template — all three places that currently tell the reader to work around these defects

Business Rules

  • Absent is not failed. A registry the source does not ship is a non-event; only a registry that exists and breaks is a failure.
  • Exit code and text never disagree. Whatever the summary claims, the status code says the same thing.
  • The source declares, the consumer overrides. The KB's declaration applies by default; an explicit override in the consuming project wins. Stated once, in the docs, as precedence.
  • A fixed defect leaves no caveat behind. The three documents that describe the workaround are part of the change, not a follow-up.

Edge Cases and Error Handling

  • Source config malformed: fall back to the consuming project's resolution and warn — never abort the install over a source's bad config, and never apply a half-parsed declaration.
  • Source declares a prefix that collides with an existing installed skill name: report the collision rather than overwriting.
  • Source declares registries the CLI does not know (newer KB, older CLI): skipped with a distinct reason ("declared by source, unknown to this CLI"), not silently dropped.
  • Everything skipped (a source that ships nothing installable): success is the wrong answer — report it as a no-op with the reason.
  • --source pointing at the official KB: precedence must not change the current behaviour for the default path.

Definition of Done Checklist

  • All acceptance criteria implemented and verified
  • Test written first for both halves: skipped-vs-failed distinction, and source-prefix honoured
  • Unit coverage for the reporting module (ok / skipped / failed, and exit code parity)
  • Round-trip coverage proving the prefix survives without copying the KB's config
  • Precedence rule (source < consumer override) documented where the config schema is described
  • Caveats removed from external-kb.mdx, scaffolded README template, seed skill template
  • External-KB smoke scenario asserts the green summary
  • install --source: honour the source KB's own registry declaration (skills prefix) #397 closed as consolidated into this card, with a comment
  • 🟡 gate checks green: lint + type + build + unit

Story Sizing and Sprint Readiness

Refined Story Points

Final Story Points: 3 (M) — the two halves were 2 + 2 as separate cards; together they are 3, because the handler and the docs page are touched once
Confidence Level: High
Sizing Justification: Half A is a reporting distinction plus exit-code parity. Half B is a resolution change with one precedence rule. The saving from consolidation is real but modest: one pass over handler.ts, one over the docs page, one review.

Sprint Capacity Validation

Sprint Fit Assessment: Yes
Development Time Estimate: ~0.5 day
Testing Time Estimate: ~0.25 day

Dependencies and Coordination

Story Dependencies

Prerequisite Stories: none
Dependent Stories: none
Shared Components: install/handler.ts, config/loader.ts, external-kb.mdxno overlap with #395 (kb-manager/cache-manager.ts), so the two run in parallel. That separation is the point of the 2026-08-05 consolidation decision: same-file cards merge, different-file cards stay apart

External Dependencies

Related: #391 / #279 (where both were found) · #397 (consolidated here) · #395 (sibling from the same review, kept separate: it corrupts state outside the project and is P0)

Validation and Testing Strategy

Acceptance Testing Approach

Testing Methods: unit tests for the reporting distinction and for config precedence (source-only, consumer-override, malformed source); a round-trip install of a fixture external KB asserting both the prefix and the green summary; the external-KB smoke scenario extended.
Test Data Requirements: fixture external KB shipping a subset of registries and declaring a prefix; a consuming project with and without its own override.

Success Metrics

  • A legitimate external KB installs with a green summary that names what was skipped and why
  • Its declared prefix applies with no hand-copied config
  • No document still tells the reader to work around either defect

Notes and Additional Context

Refinement Session Insights (2026-08-05):

  1. Consolidated by decision, on evidence: both halves live in install/handler.ts and both are documented as caveats on the same page. Kept separate, they would have serialized on one file for a purely bureaucratic reason.
  2. install --source <zip> extracts an external KB into the official KB's cache slot (shared-cache contamination) #395 stayed out deliberately. It is on cache-manager.ts, it corrupts state outside the project, and it is P0 — bundling it here would have shipped a critical fix alongside two conveniences, classified by the worst dimension of the bundle.
  3. AC5 was added in refinement: today the text says "finished with errors" while the exit code is 0. Fixing the wording without fixing that disagreement would leave automation trusting the wrong signal.
  4. AC6 is part of the fix, not documentation hygiene: three documents currently instruct the reader to work around these defects. A stale workaround costs more than the defect it describes.

Technical Analysis

Implementation Approach

Technical Strategy: separate "not shipped" from "failed" in the install reporting, and resolve registry declarations from the source KB with the consuming project's explicit override taking precedence.

Key Components:

  • install/handler.ts — summary composition (ok / skipped / failed) + exit-code parity, and the registry resolution call site
  • config/loader.ts — resolve the source KB's pair.config.json, with documented precedence
  • external-kb.mdx, scaffolded README template, seed skill template — caveats removed
  • external-KB smoke scenario — asserts the green summary

Data Flow: --source → source config (if valid) → merged with consumer overrides (consumer wins) → per-registry install → outcome classified ok / skipped / failed → summary + exit code.

Integration Points: install path, config resolution, docs site, smoke suite.

Design: not required

Technical Risks and Mitigation

Risk Impact Mitigation
skipped used to hide a real failure A broken install reports success — worse than today's false alarm AC2 + unit coverage; exit code follows failures only (AC5)
Source config trusted blindly A malformed or hostile source config changes install behaviour Edge case: malformed ⇒ fall back + warn; no partial application
Precedence left implicit Users cannot predict whether their override or the KB's declaration wins AC4: documented rule, tested in both directions
Caveats left in the docs Readers keep applying a workaround that now breaks the correct behaviour AC6 lists all three documents
Consolidation hides one half at review time The reviewer checks the summary change and skims the config change Two halves stated separately in AC and in the PR description; both have their own tests

Metadata

Metadata

Assignees

Labels

clirisk:yellowClassification: medium risk tieruser 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