Resolution
Delivered by PR #137 (merged). Board: Done.
feat: KB content validation command.
Story Statement
As a content creator
I want comprehensive validation of my KB content before packaging
So that I can ensure quality and prevent broken installations for users
Where: CLI — pair kb validate command with colored terminal report
Epic Context
Parent Epic: #65 Enhanced CLI Packaging & Distribution
Status: Refined
Priority: P0 (Must-Have)
Status Workflow
- Refined: Story is detailed, estimated, and ready for development
- In Progress: Story is actively being developed
- Done: Story delivered and accepted
Acceptance Criteria
Functional Requirements
Given-When-Then Format:
AC1: Folder & Layout Selection
-
Given a KB at custom path /my/kb
When I run pair kb validate /my/kb
Then validation runs against /my/kb instead of current directory
-
Given a KB in source layout (files at source paths from config: .skills, AGENTS.md, .pair/knowledge, etc.)
When I run pair kb validate --layout source
Then validation checks paths match asset_registries[*].source from config.json
-
Given a KB in target layout (files at targets[*].path from config)
When I run pair kb validate (default, no flag)
Then validation checks paths match asset_registries[*].targets from config.json
AC2: Config-Based Structure Validation
-
Given config.json with 5 registries
When pair kb validate
Then each registry's expected paths (per layout) are checked for existence and non-emptiness
-
Given registry skills with targets [.claude/skills/ (canonical), .github/skills/ (symlink), ...]
When --layout target
Then only targets with mode !== "symlink" are validated; symlink targets skipped
-
Given registry skills with prefix: "pair" and flatten: true
When validating target paths
Then expected folder/file names account for prefix (e.g., skill bootstrap → pair-process-bootstrap/ under .claude/skills/)
-
Given --ignore-config
When pair kb validate --ignore-config
Then config-based registry structure validation skipped; only generic checks run (links, metadata, markdown)
AC3: Registry Filtering
-
Given --skip-registries adoption,agents
When pair kb validate --skip-registries adoption,agents
Then registries adoption and agents excluded from validation; remaining validated normally
-
Given --skip-registries skills
When pair kb package --skip-registries skills
Then registry skills excluded from packaging; remaining included
-
Given invalid registry name in --skip-registries
When command executed
Then warning: "registry 'foo' not found in config, ignoring"
AC4: Link Integrity
-
Given markdown files with internal links
When pair kb validate
Then broken internal links reported as errors
-
Given --strict flag
When pair kb validate --strict
Then external links checked via HTTP HEAD; unreachable → warning
AC5: Metadata & Skills Validation
-
Given .skills/ (source) or .claude/skills/ (target) with SKILL.md files
When pair kb validate
Then each SKILL.md checked for required frontmatter; missing fields → error
-
Given adoption files with [placeholder] content
When pair kb validate
Then warnings for unpopulated template placeholders
AC6: Report & Exit Codes
-
Given validation completes with mixed results
When results display
Then categorized output: errors (red), warnings (yellow), info (blue), summary counts
-
Given 0 errors → exit 0; ≥1 error → exit 1; validation itself fails → exit 2
AC7: Package — Layout, Registry Filter & Link Transform
-
Given KB with --layout source
When pair kb package --layout source
Then packaging reads from source paths, ZIP preserves source layout
-
Given KB with --layout target
When pair kb package --layout target
Then packaging transforms target→source based on config mappings
-
Given markdown files with absolute links (e.g., /Users/x/projects/pair/.pair/knowledge/...)
When pair kb package
Then all absolute links transformed to relative paths (relative to .pair/ or custom --root)
-
Given custom --root /my/kb
When pair kb package --root /my/kb
Then links relativized to /my/kb instead of .pair/
-
Given KB that fails validation
When pair kb package
Then packaging blocked with message to run pair kb validate first
Business Rules
--layout source|target — mutually exclusive, default target
--skip-registries — comma-separated registry names, applies to both validate and package
--ignore-config — skips config-based registry structure checks (validate only)
- Target validation ignores targets with
mode: "symlink"
- Prefixes and flatten considered when computing expected paths
- Link transform: absolute→relative always applied in package (relative to
.pair/ or --root)
- Errors block packaging; warnings do not
- No network by default (
--strict enables external link checks)
- Exit codes: 0=pass, 1=errors found, 2=validation failure
Edge Cases and Error Handling
- No config.json (without
--ignore-config): error — config required for structure validation
- Invalid config.json: report config schema errors before proceeding
- Empty directory: "no KB content found" error
- Mixed layout (some source, some target paths): report per-registry which paths found/missing
- All registries skipped via
--skip-registries: warning "no registries to validate"
- Circular symlinks: detect and skip with warning
- Absolute links outside KB root: warning, link not transformed
- Binary files: skip during link/metadata checks
- Permission denied on files: warning, continue validation
Definition of Done Checklist
Development Completion
Documentation
Deployment
Story Sizing and Sprint Readiness
Refined Story Points
Final Story Points: 8
Confidence Level: High
Sizing Justification: Skeleton command exists, validators available, pattern clear from package. Complexity distributed: parser (S), validate handler (M), package handler update (M), link rewriter (S), docs (S), smoke+e2e tests (M).
Sprint Capacity Validation
Sprint Fit Assessment: Yes — 8 points, tasks parallelizable
Total Effort Assessment: Fits within sprint capacity: Yes
Dependencies and Coordination
Story Dependencies
Prerequisite Stories: None (command infrastructure exists)
Dependent Stories: US-002 (package) uses validate as gate
External Dependencies
None — all building blocks exist in codebase.
Validation and Testing Strategy
Acceptance Testing Approach
Testing Methods:
- Unit test per validator: structure (config-based), links (internal + external), metadata, skills frontmatter
- Unit test for link rewriter (absolute→relative, edge cases)
- Unit test for
--skip-registries filtering and --ignore-config bypass
- Integration test: validate on test KB with planted errors (missing files, broken links, bad metadata)
- Smoke test:
pair kb validate on real dataset in both source and target layouts
- E2E test: complex config exercising all validation scenarios for both layouts
Test Data: Fixture KBs in source and target layouts with known errors
Success Metrics
- All 21 AC pass
- Exit codes correct
- Colored report readable
- <30s on real dataset
Notes and Additional Context
Refinement Date: 2026-02-15
Key Decisions:
--layout source|target chosen over --as-source/--as-target (cleaner, works for both commands)
- Symlink targets skipped because they're derived copies, not authoritative
- Link transform always applied in package (absolute→relative) to ensure portability
--skip-registries comma-separated for brevity
- 10 documentation files identified for update
Technical Analysis
Implementation Approach
Technical Strategy: Extend existing kb-validate skeleton and package handler. Reuse loadConfigWithOverrides, extractRegistries, validateAllRegistries from registry module. New link rewriter utility.
Data Flow (validate):
CLI args → parser → load config.json → filter registries (skip + ignore-config)
→ per-registry: resolve paths (source|target, skip symlinks, apply prefix)
→ check existence/non-empty → link integrity → metadata/skills check → report
Data Flow (package with layout):
CLI args → parser → load config → filter registries
→ if --layout target: map target paths → source paths via config
→ rewrite absolute links → relative (to --root or .pair/)
→ create ZIP
Technical Risks
| Risk |
Impact |
Probability |
Mitigation |
| Prefix/flatten logic complex for skills |
Medium |
Medium |
Reuse existing logic from copyPathOps |
| Link rewriting corrupts markdown |
High |
Low |
Conservative regex, exhaustive tests, only absolute links matching root |
Task Breakdown
Dependency Graph
T-1 ──┐
├── T-3 ──┬── T-4 ──┐
T-2 ──┘ ├── T-5 ──┼── T-7 ──┬── T-9
└── T-6 ──┘ ├── T-10
└── T-11
T-2 + T-3 ──── T-8
AC Coverage
| AC |
Tasks |
| AC1 (folder & layout) |
T-1, T-3 |
| AC2 (config structure) |
T-3, T-4, T-11 |
| AC3 (registry filtering) |
T-1, T-2, T-3, T-11 |
| AC4 (link integrity) |
T-5, T-11 |
| AC5 (metadata/skills) |
T-6, T-11 |
| AC6 (report/exit codes) |
T-7, T-10, T-11 |
| AC7 (package layout/links) |
T-2, T-8, T-11 |
| Docs DoD |
T-9 |
| Smoke (real dataset) |
T-10 |
| E2E (all casistics) |
T-11 |
T-1: Extend kb-validate parser (new flags)
Priority: P0 | Estimated Hours: 3h | Bounded Context: CLI parser
Summary: Add --layout, --strict, --ignore-config, --skip-registries options to KbValidateCommandConfig and parser.
Type: Feature Implementation
Description: Extend the existing skeleton parser to accept all new flags. --layout accepts source|target (default target, mutually exclusive). --skip-registries accepts comma-separated registry names. Update metadata with new options and examples.
Acceptance Criteria:
- Primary deliverable: Updated
KbValidateCommandConfig interface + parseKbValidateCommand function
- Quality standard: 100% parser branch coverage
- Integration requirement: Dispatcher passes new config fields to handler
- Verification method: Unit tests for all flag combinations
Implementation Approach:
- Technical Design: Extend interface with optional fields
layout?: 'source' | 'target', strict?: boolean, ignoreConfig?: boolean, skipRegistries?: string[]. Parse comma-separated --skip-registries in parser.
- Bounded Context & Modules:
commands/kb-validate/
- Files to Modify/Create:
apps/pair-cli/src/commands/kb-validate/parser.ts - Add new fields + parsing
apps/pair-cli/src/commands/kb-validate/parser.test.ts - Tests for all flag combos
apps/pair-cli/src/commands/kb-validate/metadata.ts - Update options/examples
apps/pair-cli/src/cli.ts - Register new Commander options
Dependencies:
- Technical: None
- Tasks: None (independent)
Implementation Steps:
- Extend
KbValidateCommandConfig interface with new fields
- Update
parseKbValidateCommand to handle new options, parse comma-separated skip-registries
- Add validation:
--layout only source|target, error on invalid value
- Update metadata with new options and examples
- Register options in
cli.ts Commander definition
- Write unit tests for all combinations (layout, strict, ignore-config, skip-registries, invalid values)
Testing Strategy:
- Unit Tests: Parse with each flag, combined flags, invalid layout value, empty skip-registries, comma parsing
- Integration Tests: N/A (parser is pure)
T-2: Extend package parser (new flags)
Priority: P0 | Estimated Hours: 2h | Bounded Context: CLI parser
Summary: Add --layout, --skip-registries, --root options to PackageCommandConfig and parser.
Type: Feature Implementation
Description: Extend package parser to accept layout mode, registry filtering, and root path for link relativization. Same patterns as T-1.
Acceptance Criteria:
- Primary deliverable: Updated
PackageCommandConfig interface + parsePackageCommand function
- Quality standard: 100% parser branch coverage
- Integration requirement: Handler receives new config fields
- Verification method: Unit tests
Implementation Approach:
- Technical Design: Add
layout?: 'source' | 'target', skipRegistries?: string[], root?: string to config. Default layout target.
- Files to Modify/Create:
apps/pair-cli/src/commands/package/parser.ts - Add fields + parsing
apps/pair-cli/src/commands/package/parser.test.ts - Tests
apps/pair-cli/src/commands/package/metadata.ts - Update options
apps/pair-cli/src/cli.ts - Register new Commander options
Dependencies:
- Tasks: None (independent, parallel with T-1)
Implementation Steps:
- Extend
PackageCommandConfig with new fields
- Update parser, parse comma-separated skip-registries
- Update metadata
- Register in cli.ts
- Unit tests
Testing Strategy:
- Unit Tests: All flag combinations, invalid values, comma parsing
T-3: Registry filtering & layout resolution utilities
Priority: P0 | Estimated Hours: 4h | Bounded Context: Registry module
Summary: Create shared utilities for: filtering registries by skip-list, resolving expected paths per layout (source vs target), skipping symlink targets, applying prefix/flatten to path expectations.
Type: Feature Implementation
Description: Central utilities consumed by both kb-validate handler (T-4) and package handler (T-8). Loads config, filters registries, resolves the expected filesystem paths per layout mode. For target layout: filters out mode: "symlink" targets, applies prefix/flatten when computing expected child entries.
Acceptance Criteria:
- Primary deliverable:
resolveRegistryPaths(config, layout, skipRegistries) → array of {registry, expectedPaths[]}
- Quality standard: Handles all 5 registries in config.json correctly
- Integration requirement: Used by T-4, T-5, T-6, T-8
- Verification method: Unit tests with config.json fixture
Implementation Approach:
- Technical Design: New file
apps/pair-cli/src/registry/layout-resolver.ts. Functions: filterRegistries(registries, skipList) → filters + warns on unknown names. resolveExpectedPaths(registry, layout) → for source: return [registry.source]; for target: return registry.targets.filter(t => t.mode !== 'symlink').map(t => t.path). resolveExpectedChildren(registry, layout, fsService) → for registries with prefix/flatten, compute expected child names (e.g., read source dir entries, apply prefix).
- Files to Modify/Create:
apps/pair-cli/src/registry/layout-resolver.ts - New: filtering + path resolution
apps/pair-cli/src/registry/layout-resolver.test.ts - New: tests
apps/pair-cli/src/registry/index.ts - Re-export new utilities
Dependencies:
- Technical:
loadConfigWithOverrides, extractRegistries from #config/#registry
- Tasks: T-1, T-2 (need config types, but can develop in parallel against interfaces)
Implementation Steps:
- Create
layout-resolver.ts with filterRegistries (skip-list + unknown name warnings)
- Implement
resolveExpectedPaths (source vs target, symlink filter)
- Implement
resolveExpectedChildren (prefix/flatten logic for skills registry)
- Export from registry index
- Unit tests: all 5 registries × both layouts, symlink filtering, prefix application, unknown skip names
Testing Strategy:
- Unit Tests: Config fixture matching real config.json, test each registry in both layouts, edge cases (all skipped, unknown names)
- Integration Tests: N/A (pure functions)
T-4: Config-based structure validator
Priority: P0 | Estimated Hours: 4h | Bounded Context: kb-validate handler
Summary: Implement the core validation logic: for each registry (per layout), check expected paths exist and are non-empty. Produce categorized validation results (errors/warnings).
Type: Feature Implementation
Description: Main validation engine. Loads config (unless --ignore-config), calls T-3 utilities to resolve expected paths, checks filesystem. Returns structured results: {errors: [], warnings: [], info: []}.
Acceptance Criteria:
- Primary deliverable:
validateKBStructure(config, kbPath, layout, options, fs) → ValidationReport
- Quality standard: Covers AC1-AC3, AC7.21 (gate for package)
- Integration requirement: Called by kb-validate handler, results fed to T-7 formatter
- Verification method: Unit tests with InMemoryFileSystemService
Implementation Approach:
- Technical Design: New file
apps/pair-cli/src/commands/kb-validate/structure-validator.ts. Uses resolveExpectedPaths from T-3. For each registry: check path exists → error if missing; check non-empty → warning if empty. When --ignore-config: skip entirely, return empty report.
- Files to Modify/Create:
apps/pair-cli/src/commands/kb-validate/structure-validator.ts - New
apps/pair-cli/src/commands/kb-validate/structure-validator.test.ts - New
apps/pair-cli/src/commands/kb-validate/types.ts - New: ValidationReport type
Dependencies:
- Tasks: T-3 (layout resolution)
Implementation Steps:
- Define
ValidationReport type: {errors: DiagnosticEntry[], warnings: DiagnosticEntry[], info: DiagnosticEntry[]}
- Implement
validateKBStructure: load config → filter registries → resolve paths → check fs
- Handle
--ignore-config (early return)
- Handle edge cases: no config.json → error, empty dir → error, all skipped → warning
- Unit tests with InMemoryFileSystemService: missing paths, empty dirs, ignore-config, skip-registries
Testing Strategy:
- Unit Tests: Full source layout, full target layout, missing registries, empty registries, ignore-config bypass, skip-registries filter
- Integration Tests: N/A (uses InMemoryFS)
T-5: Link integrity checker (internal + strict external)
Priority: P0 | Estimated Hours: 3h | Bounded Context: kb-validate handler
Summary: Validate markdown links: internal link resolution (always), external HTTP HEAD check (only with --strict).
Type: Feature Implementation
Description: Reuse validatePathOps from @pair/content-ops for internal links. Add external link checker (HTTP HEAD with timeout) gated behind --strict. Returns DiagnosticEntry[] entries. Respects layout: resolves links relative to the correct paths per layout.
Acceptance Criteria:
- Primary deliverable:
validateLinks(kbPath, layout, strict, fs) → DiagnosticEntry[]
- Quality standard: Covers AC4 (AC11-12)
- Verification method: Unit tests with fixture markdown containing broken + valid links
Implementation Approach:
- Files to Modify/Create:
apps/pair-cli/src/commands/kb-validate/link-validator.ts - New
apps/pair-cli/src/commands/kb-validate/link-validator.test.ts - New
Dependencies:
- Technical:
validatePathOps from @pair/content-ops
- Tasks: T-3 (path resolution for layout-aware link base)
Implementation Steps:
- Implement internal link checker: collect all markdown files → extract links → resolve relative to kbPath → check existence
- Implement external link checker (gated by strict): HTTP HEAD with 5s timeout, unreachable → warning (not error)
- Skip binary files
- Handle permission denied → warning
- Unit tests: broken internal link → error, valid link → pass, external link without strict → skipped, external link with strict → checked
Testing Strategy:
- Unit Tests: Fixture markdown files in InMemoryFS, broken/valid internal links
- Manual Testing:
--strict on real KB with external links
T-6: Metadata & skills frontmatter validator
Priority: P0 | Estimated Hours: 3h | Bounded Context: kb-validate handler
Summary: Validate SKILL.md frontmatter (required fields) and adoption file placeholder detection.
Type: Feature Implementation
Description: For skills registry (respecting layout → .skills/ or .claude/skills/): find all SKILL.md files, parse YAML frontmatter, check required fields. For adoption files: regex-scan for [placeholder] patterns → warning.
Acceptance Criteria:
- Primary deliverable:
validateMetadata(kbPath, layout, registries, fs) → DiagnosticEntry[]
- Quality standard: Covers AC5 (AC13-14)
- Verification method: Unit tests with fixture SKILL.md files
Implementation Approach:
- Files to Modify/Create:
apps/pair-cli/src/commands/kb-validate/metadata-validator.ts - New
apps/pair-cli/src/commands/kb-validate/metadata-validator.test.ts - New
Dependencies:
- Tasks: T-3 (path resolution for skills path per layout)
Implementation Steps:
- Find skills directory per layout (source:
.skills/, target: .claude/skills/ considering prefix)
- Glob for
**/SKILL.md, parse frontmatter (split on ---)
- Check required fields (name, description at minimum) → error if missing
- Scan adoption files for
\\[.*\\] placeholder patterns → warning
- Unit tests
Testing Strategy:
- Unit Tests: Valid SKILL.md, missing frontmatter, missing fields, placeholder detection
T-7: Validation report formatter & exit codes
Priority: P0 | Estimated Hours: 2h | Bounded Context: kb-validate handler
Summary: Assemble validation results from T-4/T-5/T-6 into colored terminal report. Set process exit code.
Type: Feature Implementation
Description: Collect ValidationReport from all validators. Format with chalk: errors (red), warnings (yellow), info (blue). Print summary counts. Set exit code: 0 if no errors, 1 if errors, 2 if validation itself threw.
Acceptance Criteria:
- Primary deliverable:
formatReport(report) → formatted string; determineExitCode(report) → number
- Quality standard: Covers AC6 (AC15-16)
- Verification method: Unit tests for formatting and exit code logic
Implementation Approach:
- Files to Modify/Create:
apps/pair-cli/src/commands/kb-validate/report-formatter.ts - New
apps/pair-cli/src/commands/kb-validate/report-formatter.test.ts - New
apps/pair-cli/src/commands/kb-validate/handler.ts - Rewrite: orchestrate T-3→T-4+T-5+T-6→T-7
Dependencies:
- Technical: chalk
- Tasks: T-4, T-5, T-6 (provides DiagnosticEntry arrays)
Implementation Steps:
- Define
formatReport: group by category, chalk coloring, summary line
- Define
determineExitCode: 0/1/2 logic
- Rewrite
handleKbValidateCommand: load config → run validators → format → print → exit code
- Unit tests for formatter (mock chalk or test plain text), exit code logic
Testing Strategy:
- Unit Tests: Empty report → exit 0, errors only → exit 1, mixed → exit 1, thrown error → exit 2
- Manual Testing: Visual check of colored output
T-8: Package layout transform & link rewriter
Priority: P0 | Estimated Hours: 5h | Bounded Context: package handler
Summary: Add layout-aware packaging (target→source transform), registry filtering, and absolute→relative link rewriting to the package command.
Type: Feature Implementation
Description: When --layout target: read from target paths, remap to source paths in ZIP based on config mappings. When --layout source: read from source paths as-is. Always: scan markdown for absolute links matching root (or --root), rewrite to relative. Run validation before packaging (AC21). Apply --skip-registries.
Acceptance Criteria:
- Primary deliverable: Updated
handlePackageCommand with layout transform + link rewriter
- Quality standard: Covers AC7 (AC17-21)
- Integration requirement: Uses T-3 utilities, calls kb-validate before packaging
- Verification method: Unit + integration tests
Implementation Approach:
- Technical Design: New
apps/pair-cli/src/commands/package/link-rewriter.ts — regex-based markdown link rewriter: matches [text](absolutePath) where absolutePath starts with root, replaces with relative. In handler: before createPackageZip, if layout=target, build source↔target mapping from config, remap files. Filter registries via skip-list.
- Files to Modify/Create:
apps/pair-cli/src/commands/package/link-rewriter.ts - New
apps/pair-cli/src/commands/package/link-rewriter.test.ts - New
apps/pair-cli/src/commands/package/handler.ts - Update: layout logic, validation gate, link rewrite
apps/pair-cli/src/commands/package/handler.test.ts - Update tests
Dependencies:
- Tasks: T-2 (parser flags), T-3 (registry filtering/layout resolution)
Implementation Steps:
- Create
link-rewriter.ts: rewriteAbsoluteLinks(content, rootPath) → replaces absolute markdown links with relative
- Handle edge cases: links outside root → warning + skip, non-markdown files → skip
- Update
handlePackageCommand: add validation gate (call kb-validate, block on errors)
- Add layout logic: target → read from target paths, remap to source structure in ZIP
- Add skip-registries: filter
extractRegistries output
- Apply link rewriter to all markdown content before ZIP creation
- Unit tests for link rewriter (various absolute paths, edge cases)
- Update handler tests for new flows
Testing Strategy:
- Unit Tests: Link rewriter with various paths, outside-root links, non-markdown skip
- Integration Tests: Package with layout=source, layout=target, skip-registries, validation gate blocking
T-9: Documentation update (10 files)
Priority: P1 | Estimated Hours: 3h | Bounded Context: Documentation
Summary: Update all 10 documentation files with new CLI flags for kb validate and kb package.
Type: Documentation
Description: Update command reference, examples, workflows, and READMEs to reflect new --layout, --strict, --ignore-config, --skip-registries, --root flags on both commands.
Acceptance Criteria:
- Primary deliverable: All 10 doc files updated with accurate CLI reference
- Quality standard: Examples runnable, flags documented with defaults
- Verification method: Manual review, mdlint pass
Implementation Approach:
- Files to Modify:
docs/specs/cli-contracts.md - Contract definitions for new flags
docs/cli/commands.md - Command reference
docs/cli/help-examples.md - Usage examples
docs/getting-started/02-cli-workflows.md - Workflow examples
apps/pair-cli/README.md - CLI app docs
packages/knowledge-hub/README.md - KB docs
README.md - Root readme
scripts/workflows/release/README.md - Release docs
docs/RELEASE.md - Release process
scripts/smoke-tests/README.md - Smoke test docs
Dependencies:
- Tasks: T-7 (final CLI surface must be confirmed)
Implementation Steps:
- Update
cli-contracts.md with formal flag definitions
- Update
commands.md with full command reference
- Update
help-examples.md with realistic examples
- Update remaining 7 files with relevant sections
- Run
pnpm mdlint:fix to verify markdown quality
Testing Strategy:
- Manual Testing: Review each file, verify examples match implementation
- Automated: mdlint pass
T-10: Smoke test — validate real dataset (source + target)
Priority: P0 | Estimated Hours: 3h | Bounded Context: Testing
Summary: Add a smoke test scenario that installs the real KB dataset using the default config, then runs pair kb validate in both source and target layouts to verify end-to-end validation works on real content.
Type: Testing
Description: New shell scenario scripts/smoke-tests/scenarios/kb-validate.sh. Uses the existing smoke test infrastructure (run_pair, assert_success, setup_workspace, etc.). The flow: (1) use KB_SOURCE_PATH (real dataset at packages/knowledge-hub/dataset/) as source layout — validate with --layout source; (2) install the KB to a workspace via run_pair install — validate the installed result with --layout target (default); (3) verify both pass with exit 0. Also test that --skip-registries and --ignore-config don't break on real data.
Acceptance Criteria:
- Primary deliverable:
scripts/smoke-tests/scenarios/kb-validate.sh passing in CI
- Quality standard: Covers AC1-AC3, AC6 on real dataset
- Integration requirement: Registered in
run-all.sh CI_TESTS array
- Verification method:
pnpm smoke-tests passes
Implementation Approach:
- Files to Modify/Create:
scripts/smoke-tests/scenarios/kb-validate.sh - New scenario
scripts/smoke-tests/run-all.sh - Add kb-validate.sh to CI_TESTS
Dependencies:
- Tasks: T-7 (kb-validate command must be fully functional)
Implementation Steps:
- Create
kb-validate.sh scenario:
- Test 1: Source layout validation —
run_pair kb-validate --path "$KB_SOURCE_PATH" --layout source → assert_success
- Test 2: Target layout validation — install KB to workspace, then
run_pair kb-validate --path "$WORKSPACE" --layout target → assert_success
- Test 3: Skip registries —
run_pair kb-validate --path "$KB_SOURCE_PATH" --layout source --skip-registries adoption → assert_success
- Test 4: Ignore config —
run_pair kb-validate --path "$KB_SOURCE_PATH" --ignore-config → assert_success
- Test 5: Validation failure — create workspace with missing registry paths, validate →
assert_failure
- Add
kb-validate.sh to CI_TESTS array in run-all.sh
- Mark as
OFFLINE_SAFE=true (no network needed for default validation)
Testing Strategy:
- Smoke Test: Runs as part of
pnpm smoke-tests
- CI: Included in CI_TESTS for automated regression
T-11: E2E test — complex config, all validation casistics
Priority: P0 | Estimated Hours: 4h | Bounded Context: Testing
Summary: Add comprehensive E2E tests in apps/pair-cli/src/cli.e2e.test.ts using InMemoryFileSystemService with a complex config (all 5 registry types including skills with prefix/flatten/symlink targets). Tests cover every validation scenario for both source and target layouts.
Type: Testing
Description: New describe blocks in the existing e2e test file. Uses a complex config fixture mirroring the real config.json (github, knowledge, adoption, agents, skills with prefix+flatten+symlink targets). Creates source and target layout filesystems with planted errors. Exercises all validators: structure, links, metadata, skills frontmatter, report output, exit codes. Tests --skip-registries, --ignore-config, --strict (with mocked HTTP), and --layout source|target.
Acceptance Criteria:
- Primary deliverable: E2E test suite covering all 21 ACs via
InMemoryFileSystemService
- Quality standard: Every AC exercised in at least one test case, both layouts
- Integration requirement: Runs via
pnpm --filter pair-cli test
- Verification method:
pnpm quality-gate passes
Implementation Approach:
- Files to Modify/Create:
apps/pair-cli/src/cli.e2e.test.ts - Add new describe blocks
Dependencies:
- Tasks: T-7 (all validators + handler must be complete)
Implementation Steps:
- Create complex config fixture:
function createFullConfig() {
return {
asset_registries: {
github: { source: '.github', behavior: 'mirror', include: ['/agents'],
description: 'GitHub config', targets: [{ path: '.github', mode: 'canonical' }] },
knowledge: { source: '.pair/knowledge', behavior: 'mirror',
description: 'KB content', targets: [{ path: '.pair/knowledge', mode: 'canonical' }] },
adoption: { source: '.pair/adoption', behavior: 'add',
description: 'Adoption guides', targets: [{ path: '.pair/adoption', mode: 'canonical' }] },
agents: { source: 'AGENTS.md', behavior: 'mirror',
description: 'Agent guidance', targets: [
{ path: 'AGENTS.md', mode: 'canonical' },
{ path: 'CLAUDE.md', mode: 'copy', transform: { prefix: 'claude' } }] },
skills: { source: '.skills', behavior: 'mirror', flatten: true, prefix: 'pair',
description: 'Agent skills', targets: [
{ path: '.claude/skills/', mode: 'canonical' },
{ path: '.github/skills/', mode: 'symlink' },
{ path: '.cursor/skills/', mode: 'symlink' }] },
}
}
}
- Create source layout FS helper — files at source paths (
.skills/next/SKILL.md, .pair/knowledge/index.md, etc.)
- Create target layout FS helper — files at target paths (
.claude/skills/pair-next/SKILL.md, .pair/knowledge/index.md, etc.), symlink targets NOT present (they'd be symlinks)
- Test cases:
- Source layout — all valid: validate
--layout source → exit 0
- Target layout — all valid: validate (default) → exit 0
- Target layout — symlink targets ignored: target FS missing
.github/skills/ (symlink) → still exit 0
- Target layout — prefix/flatten applied:
.claude/skills/pair-next/SKILL.md expected, not .claude/skills/next/SKILL.md
- Source layout — missing registry: remove
.pair/adoption/ → error for adoption registry
- Target layout — missing registry: remove
.pair/knowledge/ → error for knowledge registry
- Source layout — empty registry dir:
.skills/ exists but empty → warning
- Skip registries: skip adoption → no error even if adoption missing
- Ignore config:
--ignore-config → structure checks skipped, only generic checks
- Broken internal link: markdown with
[link](./nonexistent.md) → error
- Valid internal link: markdown with
[link](./existing.md) → pass
- Missing SKILL.md frontmatter: SKILL.md without
--- frontmatter → error
- Missing required field in frontmatter: SKILL.md with frontmatter but no
name → error
- Placeholder detection: adoption file with
[user persona] → warning
- Exit code 0: all valid → 0
- Exit code 1: errors present → 1
- All registries skipped:
--skip-registries github,knowledge,adoption,agents,skills → warning "no registries to validate"
- Invalid skip registry name:
--skip-registries nonexistent → warning "not found in config"
- Mixed errors and warnings: some missing paths + some placeholders → exit 1 (errors take precedence)
- Verify all tests pass with
pnpm --filter pair-cli test
Testing Strategy:
- E2E Tests: All cases above run in vitest via InMemoryFS
- CI: Part of
pnpm quality-gate
Notes: Follows existing e2e patterns in cli.e2e.test.ts — uses InMemoryFileSystemService, withTempConfig, direct function calls to handlers.
Resolution
Delivered by PR #137 (merged). Board: Done.
feat: KB content validation command.
Story Statement
As a content creator
I want comprehensive validation of my KB content before packaging
So that I can ensure quality and prevent broken installations for users
Where: CLI —
pair kb validatecommand with colored terminal reportEpic Context
Parent Epic: #65 Enhanced CLI Packaging & Distribution
Status: Refined
Priority: P0 (Must-Have)
Status Workflow
Acceptance Criteria
Functional Requirements
Given-When-Then Format:
AC1: Folder & Layout Selection
Given a KB at custom path
/my/kbWhen I run
pair kb validate /my/kbThen validation runs against
/my/kbinstead of current directoryGiven a KB in source layout (files at
sourcepaths from config:.skills,AGENTS.md,.pair/knowledge, etc.)When I run
pair kb validate --layout sourceThen validation checks paths match
asset_registries[*].sourcefrom config.jsonGiven a KB in target layout (files at
targets[*].pathfrom config)When I run
pair kb validate(default, no flag)Then validation checks paths match
asset_registries[*].targetsfrom config.jsonAC2: Config-Based Structure Validation
Given config.json with 5 registries
When
pair kb validateThen each registry's expected paths (per layout) are checked for existence and non-emptiness
Given registry
skillswith targets[.claude/skills/ (canonical), .github/skills/ (symlink), ...]When
--layout targetThen only targets with
mode !== "symlink"are validated; symlink targets skippedGiven registry
skillswithprefix: "pair"andflatten: trueWhen validating target paths
Then expected folder/file names account for prefix (e.g., skill
bootstrap→pair-process-bootstrap/under.claude/skills/)Given
--ignore-configWhen
pair kb validate --ignore-configThen config-based registry structure validation skipped; only generic checks run (links, metadata, markdown)
AC3: Registry Filtering
Given
--skip-registries adoption,agentsWhen
pair kb validate --skip-registries adoption,agentsThen registries
adoptionandagentsexcluded from validation; remaining validated normallyGiven
--skip-registries skillsWhen
pair kb package --skip-registries skillsThen registry
skillsexcluded from packaging; remaining includedGiven invalid registry name in
--skip-registriesWhen command executed
Then warning: "registry 'foo' not found in config, ignoring"
AC4: Link Integrity
Given markdown files with internal links
When
pair kb validateThen broken internal links reported as errors
Given
--strictflagWhen
pair kb validate --strictThen external links checked via HTTP HEAD; unreachable → warning
AC5: Metadata & Skills Validation
Given
.skills/(source) or.claude/skills/(target) with SKILL.md filesWhen
pair kb validateThen each SKILL.md checked for required frontmatter; missing fields → error
Given adoption files with
[placeholder]contentWhen
pair kb validateThen warnings for unpopulated template placeholders
AC6: Report & Exit Codes
Given validation completes with mixed results
When results display
Then categorized output: errors (red), warnings (yellow), info (blue), summary counts
Given 0 errors → exit 0; ≥1 error → exit 1; validation itself fails → exit 2
AC7: Package — Layout, Registry Filter & Link Transform
Given KB with
--layout sourceWhen
pair kb package --layout sourceThen packaging reads from source paths, ZIP preserves source layout
Given KB with
--layout targetWhen
pair kb package --layout targetThen packaging transforms target→source based on config mappings
Given markdown files with absolute links (e.g.,
/Users/x/projects/pair/.pair/knowledge/...)When
pair kb packageThen all absolute links transformed to relative paths (relative to
.pair/or custom--root)Given custom
--root /my/kbWhen
pair kb package --root /my/kbThen links relativized to
/my/kbinstead of.pair/Given KB that fails validation
When
pair kb packageThen packaging blocked with message to run
pair kb validatefirstBusiness Rules
--layout source|target— mutually exclusive, defaulttarget--skip-registries— comma-separated registry names, applies to both validate and package--ignore-config— skips config-based registry structure checks (validate only)mode: "symlink".pair/or--root)--strictenables external link checks)Edge Cases and Error Handling
--ignore-config): error — config required for structure validation--skip-registries: warning "no registries to validate"Definition of Done Checklist
Development Completion
--layout source|target,--strict,--ignore-config,--skip-registriesflags on validate--layout source|target,--skip-registries,--rootflags on package'- [x] Smoke test: on real dataset (source + target layouts)'
'- [x] E2E test: complex config with all validation casistics (source + target)'
Documentation
docs/specs/cli-contracts.mdupdateddocs/cli/commands.mdupdateddocs/cli/help-examples.mdupdateddocs/getting-started/02-cli-workflows.mdupdatedapps/pair-cli/README.mdupdatedpackages/knowledge-hub/README.mdupdatedREADME.md(root) updatedscripts/workflows/release/README.mdupdateddocs/RELEASE.mdupdatedscripts/smoke-tests/README.mdupdatedDeployment
pnpm quality-gate)Story Sizing and Sprint Readiness
Refined Story Points
Final Story Points: 8
Confidence Level: High
Sizing Justification: Skeleton command exists, validators available, pattern clear from package. Complexity distributed: parser (S), validate handler (M), package handler update (M), link rewriter (S), docs (S), smoke+e2e tests (M).
Sprint Capacity Validation
Sprint Fit Assessment: Yes — 8 points, tasks parallelizable
Total Effort Assessment: Fits within sprint capacity: Yes
Dependencies and Coordination
Story Dependencies
Prerequisite Stories: None (command infrastructure exists)
Dependent Stories: US-002 (package) uses validate as gate
External Dependencies
None — all building blocks exist in codebase.
Validation and Testing Strategy
Acceptance Testing Approach
Testing Methods:
--skip-registriesfiltering and--ignore-configbypasspair kb validateon real dataset in both source and target layoutsTest Data: Fixture KBs in source and target layouts with known errors
Success Metrics
Notes and Additional Context
Refinement Date: 2026-02-15
Key Decisions:
--layout source|targetchosen over--as-source/--as-target(cleaner, works for both commands)--skip-registriescomma-separated for brevityTechnical Analysis
Implementation Approach
Technical Strategy: Extend existing
kb-validateskeleton andpackagehandler. ReuseloadConfigWithOverrides,extractRegistries,validateAllRegistriesfrom registry module. New link rewriter utility.Data Flow (validate):
Data Flow (package with layout):
Technical Risks
Task Breakdown
'- [x] T-11: E2E test' — complex config, all validation casistics
Dependency Graph
AC Coverage
T-1: Extend kb-validate parser (new flags)
Priority: P0 | Estimated Hours: 3h | Bounded Context: CLI parser
Summary: Add
--layout,--strict,--ignore-config,--skip-registriesoptions toKbValidateCommandConfigand parser.Type: Feature Implementation
Description: Extend the existing skeleton parser to accept all new flags.
--layoutacceptssource|target(defaulttarget, mutually exclusive).--skip-registriesaccepts comma-separated registry names. Update metadata with new options and examples.Acceptance Criteria:
KbValidateCommandConfiginterface +parseKbValidateCommandfunctionImplementation Approach:
layout?: 'source' | 'target',strict?: boolean,ignoreConfig?: boolean,skipRegistries?: string[]. Parse comma-separated--skip-registriesin parser.commands/kb-validate/apps/pair-cli/src/commands/kb-validate/parser.ts- Add new fields + parsingapps/pair-cli/src/commands/kb-validate/parser.test.ts- Tests for all flag combosapps/pair-cli/src/commands/kb-validate/metadata.ts- Update options/examplesapps/pair-cli/src/cli.ts- Register new Commander optionsDependencies:
Implementation Steps:
KbValidateCommandConfiginterface with new fieldsparseKbValidateCommandto handle new options, parse comma-separated skip-registries--layoutonlysource|target, error on invalid valuecli.tsCommander definitionTesting Strategy:
T-2: Extend package parser (new flags)
Priority: P0 | Estimated Hours: 2h | Bounded Context: CLI parser
Summary: Add
--layout,--skip-registries,--rootoptions toPackageCommandConfigand parser.Type: Feature Implementation
Description: Extend package parser to accept layout mode, registry filtering, and root path for link relativization. Same patterns as T-1.
Acceptance Criteria:
PackageCommandConfiginterface +parsePackageCommandfunctionImplementation Approach:
layout?: 'source' | 'target',skipRegistries?: string[],root?: stringto config. Default layouttarget.apps/pair-cli/src/commands/package/parser.ts- Add fields + parsingapps/pair-cli/src/commands/package/parser.test.ts- Testsapps/pair-cli/src/commands/package/metadata.ts- Update optionsapps/pair-cli/src/cli.ts- Register new Commander optionsDependencies:
Implementation Steps:
PackageCommandConfigwith new fieldsTesting Strategy:
T-3: Registry filtering & layout resolution utilities
Priority: P0 | Estimated Hours: 4h | Bounded Context: Registry module
Summary: Create shared utilities for: filtering registries by skip-list, resolving expected paths per layout (source vs target), skipping symlink targets, applying prefix/flatten to path expectations.
Type: Feature Implementation
Description: Central utilities consumed by both kb-validate handler (T-4) and package handler (T-8). Loads config, filters registries, resolves the expected filesystem paths per layout mode. For target layout: filters out
mode: "symlink"targets, applies prefix/flatten when computing expected child entries.Acceptance Criteria:
resolveRegistryPaths(config, layout, skipRegistries)→ array of{registry, expectedPaths[]}Implementation Approach:
apps/pair-cli/src/registry/layout-resolver.ts. Functions:filterRegistries(registries, skipList)→ filters + warns on unknown names.resolveExpectedPaths(registry, layout)→ for source: return[registry.source]; for target: returnregistry.targets.filter(t => t.mode !== 'symlink').map(t => t.path).resolveExpectedChildren(registry, layout, fsService)→ for registries withprefix/flatten, compute expected child names (e.g., read source dir entries, apply prefix).apps/pair-cli/src/registry/layout-resolver.ts- New: filtering + path resolutionapps/pair-cli/src/registry/layout-resolver.test.ts- New: testsapps/pair-cli/src/registry/index.ts- Re-export new utilitiesDependencies:
loadConfigWithOverrides,extractRegistriesfrom#config/#registryImplementation Steps:
layout-resolver.tswithfilterRegistries(skip-list + unknown name warnings)resolveExpectedPaths(source vs target, symlink filter)resolveExpectedChildren(prefix/flatten logic for skills registry)Testing Strategy:
T-4: Config-based structure validator
Priority: P0 | Estimated Hours: 4h | Bounded Context: kb-validate handler
Summary: Implement the core validation logic: for each registry (per layout), check expected paths exist and are non-empty. Produce categorized validation results (errors/warnings).
Type: Feature Implementation
Description: Main validation engine. Loads config (unless
--ignore-config), calls T-3 utilities to resolve expected paths, checks filesystem. Returns structured results:{errors: [], warnings: [], info: []}.Acceptance Criteria:
validateKBStructure(config, kbPath, layout, options, fs)→ValidationReportImplementation Approach:
apps/pair-cli/src/commands/kb-validate/structure-validator.ts. UsesresolveExpectedPathsfrom T-3. For each registry: check path exists → error if missing; check non-empty → warning if empty. When--ignore-config: skip entirely, return empty report.apps/pair-cli/src/commands/kb-validate/structure-validator.ts- Newapps/pair-cli/src/commands/kb-validate/structure-validator.test.ts- Newapps/pair-cli/src/commands/kb-validate/types.ts- New:ValidationReporttypeDependencies:
Implementation Steps:
ValidationReporttype:{errors: DiagnosticEntry[], warnings: DiagnosticEntry[], info: DiagnosticEntry[]}validateKBStructure: load config → filter registries → resolve paths → check fs--ignore-config(early return)Testing Strategy:
T-5: Link integrity checker (internal + strict external)
Priority: P0 | Estimated Hours: 3h | Bounded Context: kb-validate handler
Summary: Validate markdown links: internal link resolution (always), external HTTP HEAD check (only with
--strict).Type: Feature Implementation
Description: Reuse
validatePathOpsfrom@pair/content-opsfor internal links. Add external link checker (HTTP HEAD with timeout) gated behind--strict. ReturnsDiagnosticEntry[]entries. Respects layout: resolves links relative to the correct paths per layout.Acceptance Criteria:
validateLinks(kbPath, layout, strict, fs)→DiagnosticEntry[]Implementation Approach:
apps/pair-cli/src/commands/kb-validate/link-validator.ts- Newapps/pair-cli/src/commands/kb-validate/link-validator.test.ts- NewDependencies:
validatePathOpsfrom@pair/content-opsImplementation Steps:
Testing Strategy:
--stricton real KB with external linksT-6: Metadata & skills frontmatter validator
Priority: P0 | Estimated Hours: 3h | Bounded Context: kb-validate handler
Summary: Validate SKILL.md frontmatter (required fields) and adoption file placeholder detection.
Type: Feature Implementation
Description: For skills registry (respecting layout →
.skills/or.claude/skills/): find all SKILL.md files, parse YAML frontmatter, check required fields. For adoption files: regex-scan for[placeholder]patterns → warning.Acceptance Criteria:
validateMetadata(kbPath, layout, registries, fs)→DiagnosticEntry[]Implementation Approach:
apps/pair-cli/src/commands/kb-validate/metadata-validator.ts- Newapps/pair-cli/src/commands/kb-validate/metadata-validator.test.ts- NewDependencies:
Implementation Steps:
.skills/, target:.claude/skills/considering prefix)**/SKILL.md, parse frontmatter (split on---)\\[.*\\]placeholder patterns → warningTesting Strategy:
T-7: Validation report formatter & exit codes
Priority: P0 | Estimated Hours: 2h | Bounded Context: kb-validate handler
Summary: Assemble validation results from T-4/T-5/T-6 into colored terminal report. Set process exit code.
Type: Feature Implementation
Description: Collect
ValidationReportfrom all validators. Format with chalk: errors (red), warnings (yellow), info (blue). Print summary counts. Set exit code: 0 if no errors, 1 if errors, 2 if validation itself threw.Acceptance Criteria:
formatReport(report)→ formatted string;determineExitCode(report)→ numberImplementation Approach:
apps/pair-cli/src/commands/kb-validate/report-formatter.ts- Newapps/pair-cli/src/commands/kb-validate/report-formatter.test.ts- Newapps/pair-cli/src/commands/kb-validate/handler.ts- Rewrite: orchestrate T-3→T-4+T-5+T-6→T-7Dependencies:
Implementation Steps:
formatReport: group by category, chalk coloring, summary linedetermineExitCode: 0/1/2 logichandleKbValidateCommand: load config → run validators → format → print → exit codeTesting Strategy:
T-8: Package layout transform & link rewriter
Priority: P0 | Estimated Hours: 5h | Bounded Context: package handler
Summary: Add layout-aware packaging (target→source transform), registry filtering, and absolute→relative link rewriting to the package command.
Type: Feature Implementation
Description: When
--layout target: read from target paths, remap to source paths in ZIP based on config mappings. When--layout source: read from source paths as-is. Always: scan markdown for absolute links matching root (or--root), rewrite to relative. Run validation before packaging (AC21). Apply--skip-registries.Acceptance Criteria:
handlePackageCommandwith layout transform + link rewriterImplementation Approach:
apps/pair-cli/src/commands/package/link-rewriter.ts— regex-based markdown link rewriter: matches[text](absolutePath)where absolutePath starts with root, replaces with relative. In handler: beforecreatePackageZip, if layout=target, build source↔target mapping from config, remap files. Filter registries via skip-list.apps/pair-cli/src/commands/package/link-rewriter.ts- Newapps/pair-cli/src/commands/package/link-rewriter.test.ts- Newapps/pair-cli/src/commands/package/handler.ts- Update: layout logic, validation gate, link rewriteapps/pair-cli/src/commands/package/handler.test.ts- Update testsDependencies:
Implementation Steps:
link-rewriter.ts:rewriteAbsoluteLinks(content, rootPath)→ replaces absolute markdown links with relativehandlePackageCommand: add validation gate (call kb-validate, block on errors)extractRegistriesoutputTesting Strategy:
T-9: Documentation update (10 files)
Priority: P1 | Estimated Hours: 3h | Bounded Context: Documentation
Summary: Update all 10 documentation files with new CLI flags for
kb validateandkb package.Type: Documentation
Description: Update command reference, examples, workflows, and READMEs to reflect new
--layout,--strict,--ignore-config,--skip-registries,--rootflags on both commands.Acceptance Criteria:
Implementation Approach:
docs/specs/cli-contracts.md- Contract definitions for new flagsdocs/cli/commands.md- Command referencedocs/cli/help-examples.md- Usage examplesdocs/getting-started/02-cli-workflows.md- Workflow examplesapps/pair-cli/README.md- CLI app docspackages/knowledge-hub/README.md- KB docsREADME.md- Root readmescripts/workflows/release/README.md- Release docsdocs/RELEASE.md- Release processscripts/smoke-tests/README.md- Smoke test docsDependencies:
Implementation Steps:
cli-contracts.mdwith formal flag definitionscommands.mdwith full command referencehelp-examples.mdwith realistic examplespnpm mdlint:fixto verify markdown qualityTesting Strategy:
T-10: Smoke test — validate real dataset (source + target)
Priority: P0 | Estimated Hours: 3h | Bounded Context: Testing
Summary: Add a smoke test scenario that installs the real KB dataset using the default config, then runs
pair kb validatein both source and target layouts to verify end-to-end validation works on real content.Type: Testing
Description: New shell scenario
scripts/smoke-tests/scenarios/kb-validate.sh. Uses the existing smoke test infrastructure (run_pair,assert_success,setup_workspace, etc.). The flow: (1) useKB_SOURCE_PATH(real dataset atpackages/knowledge-hub/dataset/) as source layout — validate with--layout source; (2) install the KB to a workspace viarun_pair install— validate the installed result with--layout target(default); (3) verify both pass with exit 0. Also test that--skip-registriesand--ignore-configdon't break on real data.Acceptance Criteria:
scripts/smoke-tests/scenarios/kb-validate.shpassing in CIrun-all.shCI_TESTS arraypnpm smoke-testspassesImplementation Approach:
scripts/smoke-tests/scenarios/kb-validate.sh- New scenarioscripts/smoke-tests/run-all.sh- Addkb-validate.shto CI_TESTSDependencies:
Implementation Steps:
kb-validate.shscenario:run_pair kb-validate --path "$KB_SOURCE_PATH" --layout source→assert_successrun_pair kb-validate --path "$WORKSPACE" --layout target→assert_successrun_pair kb-validate --path "$KB_SOURCE_PATH" --layout source --skip-registries adoption→assert_successrun_pair kb-validate --path "$KB_SOURCE_PATH" --ignore-config→assert_successassert_failurekb-validate.shtoCI_TESTSarray inrun-all.shOFFLINE_SAFE=true(no network needed for default validation)Testing Strategy:
pnpm smoke-testsT-11: E2E test — complex config, all validation casistics
Priority: P0 | Estimated Hours: 4h | Bounded Context: Testing
Summary: Add comprehensive E2E tests in
apps/pair-cli/src/cli.e2e.test.tsusingInMemoryFileSystemServicewith a complex config (all 5 registry types including skills with prefix/flatten/symlink targets). Tests cover every validation scenario for both source and target layouts.Type: Testing
Description: New
describeblocks in the existing e2e test file. Uses a complex config fixture mirroring the realconfig.json(github, knowledge, adoption, agents, skills with prefix+flatten+symlink targets). Creates source and target layout filesystems with planted errors. Exercises all validators: structure, links, metadata, skills frontmatter, report output, exit codes. Tests--skip-registries,--ignore-config,--strict(with mocked HTTP), and--layout source|target.Acceptance Criteria:
InMemoryFileSystemServicepnpm --filter pair-cli testpnpm quality-gatepassesImplementation Approach:
apps/pair-cli/src/cli.e2e.test.ts- Add new describe blocksDependencies:
Implementation Steps:
.skills/next/SKILL.md,.pair/knowledge/index.md, etc.).claude/skills/pair-next/SKILL.md,.pair/knowledge/index.md, etc.), symlink targets NOT present (they'd be symlinks)--layout source→ exit 0.github/skills/(symlink) → still exit 0.claude/skills/pair-next/SKILL.mdexpected, not.claude/skills/next/SKILL.md.pair/adoption/→ error for adoption registry.pair/knowledge/→ error for knowledge registry.skills/exists but empty → warning--ignore-config→ structure checks skipped, only generic checks[link](./nonexistent.md)→ error[link](./existing.md)→ pass---frontmatter → errorname→ error[user persona]→ warning--skip-registries github,knowledge,adoption,agents,skills→ warning "no registries to validate"--skip-registries nonexistent→ warning "not found in config"pnpm --filter pair-cli testTesting Strategy:
pnpm quality-gateNotes: Follows existing e2e patterns in
cli.e2e.test.ts— usesInMemoryFileSystemService,withTempConfig, direct function calls to handlers.