Skip to content

Validate KB Content #74

Description

@rucka

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

  1. 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

  2. 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

  3. 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

  1. 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

  2. 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

  3. Given registry skills with prefix: "pair" and flatten: true
    When validating target paths
    Then expected folder/file names account for prefix (e.g., skill bootstrappair-process-bootstrap/ under .claude/skills/)

  4. 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

  1. Given --skip-registries adoption,agents
    When pair kb validate --skip-registries adoption,agents
    Then registries adoption and agents excluded from validation; remaining validated normally

  2. Given --skip-registries skills
    When pair kb package --skip-registries skills
    Then registry skills excluded from packaging; remaining included

  3. Given invalid registry name in --skip-registries
    When command executed
    Then warning: "registry 'foo' not found in config, ignoring"

AC4: Link Integrity

  1. Given markdown files with internal links
    When pair kb validate
    Then broken internal links reported as errors

  2. Given --strict flag
    When pair kb validate --strict
    Then external links checked via HTTP HEAD; unreachable → warning

AC5: Metadata & Skills Validation

  1. 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

  2. Given adoption files with [placeholder] content
    When pair kb validate
    Then warnings for unpopulated template placeholders

AC6: Report & Exit Codes

  1. Given validation completes with mixed results
    When results display
    Then categorized output: errors (red), warnings (yellow), info (blue), summary counts

  2. Given 0 errors → exit 0; ≥1 error → exit 1; validation itself fails → exit 2

AC7: Package — Layout, Registry Filter & Link Transform

  1. Given KB with --layout source
    When pair kb package --layout source
    Then packaging reads from source paths, ZIP preserves source layout

  2. Given KB with --layout target
    When pair kb package --layout target
    Then packaging transforms target→source based on config mappings

  3. 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)

  4. Given custom --root /my/kb
    When pair kb package --root /my/kb
    Then links relativized to /my/kb instead of .pair/

  5. 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

  • All 21 acceptance criteria implemented and verified
  • --layout source|target, --strict, --ignore-config, --skip-registries flags on validate
  • --layout source|target, --skip-registries, --root flags on package
  • Symlink targets skipped in target layout validation
  • Prefix/flatten logic applied when resolving expected paths
  • Link rewriter: absolute→relative in package command
  • Colored terminal report (errors/warnings/info) with summary counts
  • Exit codes: 0/1/2
  • Code follows project coding standards
  • Code review completed and approved
  • Unit tests for each validator (structure, links, metadata, skills, link rewriter)
  • Integration tests: validate on test KB with known errors
    '- [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.md updated
  • docs/cli/commands.md updated
  • docs/cli/help-examples.md updated
  • docs/getting-started/02-cli-workflows.md updated
  • apps/pair-cli/README.md updated
  • packages/knowledge-hub/README.md updated
  • README.md (root) updated
  • scripts/workflows/release/README.md updated
  • docs/RELEASE.md updated
  • scripts/smoke-tests/README.md updated

Deployment

  • Quality gate passes (pnpm quality-gate)
  • Demo-ready: run validation on sample KB showing error detection + colored report

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

  • T-1: Extend kb-validate parser (new flags)
  • T-2: Extend package parser (new flags)
  • T-3: Registry filtering & layout resolution utilities
  • T-4: Config-based structure validator
  • T-5: Link integrity checker (internal + strict external)
  • T-6: Metadata & skills frontmatter validator
  • T-7: Validation report formatter & exit codes
  • T-8: Package layout transform & link rewriter
  • T-9: Documentation update (10 files)
  • T-10: Smoke test — validate real dataset (source + target)
    '- [x] T-11: E2E test' — complex config, all validation casistics

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?: &#39;source&#39; | &#39;target&#39;, 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:

  1. Extend KbValidateCommandConfig interface with new fields
  2. Update parseKbValidateCommand to handle new options, parse comma-separated skip-registries
  3. Add validation: --layout only source|target, error on invalid value
  4. Update metadata with new options and examples
  5. Register options in cli.ts Commander definition
  6. 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?: &#39;source&#39; | &#39;target&#39;, 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:

  1. Extend PackageCommandConfig with new fields
  2. Update parser, parse comma-separated skip-registries
  3. Update metadata
  4. Register in cli.ts
  5. 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: &#34;symlink&#34; 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 =&gt; t.mode !== &#39;symlink&#39;).map(t =&gt; 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:

  1. Create layout-resolver.ts with filterRegistries (skip-list + unknown name warnings)
  2. Implement resolveExpectedPaths (source vs target, symlink filter)
  3. Implement resolveExpectedChildren (prefix/flatten logic for skills registry)
  4. Export from registry index
  5. 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:

  1. Define ValidationReport type: {errors: DiagnosticEntry[], warnings: DiagnosticEntry[], info: DiagnosticEntry[]}
  2. Implement validateKBStructure: load config → filter registries → resolve paths → check fs
  3. Handle --ignore-config (early return)
  4. Handle edge cases: no config.json → error, empty dir → error, all skipped → warning
  5. 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:

  1. Implement internal link checker: collect all markdown files → extract links → resolve relative to kbPath → check existence
  2. Implement external link checker (gated by strict): HTTP HEAD with 5s timeout, unreachable → warning (not error)
  3. Skip binary files
  4. Handle permission denied → warning
  5. 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:

  1. Find skills directory per layout (source: .skills/, target: .claude/skills/ considering prefix)
  2. Glob for **/SKILL.md, parse frontmatter (split on ---)
  3. Check required fields (name, description at minimum) → error if missing
  4. Scan adoption files for \\[.*\\] placeholder patterns → warning
  5. 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:

  1. Define formatReport: group by category, chalk coloring, summary line
  2. Define determineExitCode: 0/1/2 logic
  3. Rewrite handleKbValidateCommand: load config → run validators → format → print → exit code
  4. 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:

  1. Create link-rewriter.ts: rewriteAbsoluteLinks(content, rootPath) → replaces absolute markdown links with relative
  2. Handle edge cases: links outside root → warning + skip, non-markdown files → skip
  3. Update handlePackageCommand: add validation gate (call kb-validate, block on errors)
  4. Add layout logic: target → read from target paths, remap to source structure in ZIP
  5. Add skip-registries: filter extractRegistries output
  6. Apply link rewriter to all markdown content before ZIP creation
  7. Unit tests for link rewriter (various absolute paths, edge cases)
  8. 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:

  1. Update cli-contracts.md with formal flag definitions
  2. Update commands.md with full command reference
  3. Update help-examples.md with realistic examples
  4. Update remaining 7 files with relevant sections
  5. 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:

  1. Create kb-validate.sh scenario:
    • Test 1: Source layout validation — run_pair kb-validate --path &#34;$KB_SOURCE_PATH&#34; --layout sourceassert_success
    • Test 2: Target layout validation — install KB to workspace, then run_pair kb-validate --path &#34;$WORKSPACE&#34; --layout targetassert_success
    • Test 3: Skip registries — run_pair kb-validate --path &#34;$KB_SOURCE_PATH&#34; --layout source --skip-registries adoptionassert_success
    • Test 4: Ignore config — run_pair kb-validate --path &#34;$KB_SOURCE_PATH&#34; --ignore-configassert_success
    • Test 5: Validation failure — create workspace with missing registry paths, validate → assert_failure
  2. Add kb-validate.sh to CI_TESTS array in run-all.sh
  3. 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:

  1. Create complex config fixture:
    function createFullConfig() {
      return {
        asset_registries: {
          github: { source: &#39;.github&#39;, behavior: &#39;mirror&#39;, include: [&#39;/agents&#39;],
            description: &#39;GitHub config&#39;, targets: [{ path: &#39;.github&#39;, mode: &#39;canonical&#39; }] },
          knowledge: { source: &#39;.pair/knowledge&#39;, behavior: &#39;mirror&#39;,
            description: &#39;KB content&#39;, targets: [{ path: &#39;.pair/knowledge&#39;, mode: &#39;canonical&#39; }] },
          adoption: { source: &#39;.pair/adoption&#39;, behavior: &#39;add&#39;,
            description: &#39;Adoption guides&#39;, targets: [{ path: &#39;.pair/adoption&#39;, mode: &#39;canonical&#39; }] },
          agents: { source: &#39;AGENTS.md&#39;, behavior: &#39;mirror&#39;,
            description: &#39;Agent guidance&#39;, targets: [
              { path: &#39;AGENTS.md&#39;, mode: &#39;canonical&#39; },
              { path: &#39;CLAUDE.md&#39;, mode: &#39;copy&#39;, transform: { prefix: &#39;claude&#39; } }] },
          skills: { source: &#39;.skills&#39;, behavior: &#39;mirror&#39;, flatten: true, prefix: &#39;pair&#39;,
            description: &#39;Agent skills&#39;, targets: [
              { path: &#39;.claude/skills/&#39;, mode: &#39;canonical&#39; },
              { path: &#39;.github/skills/&#39;, mode: &#39;symlink&#39; },
              { path: &#39;.cursor/skills/&#39;, mode: &#39;symlink&#39; }] },
        }
      }
    }
  2. Create source layout FS helper — files at source paths (.skills/next/SKILL.md, .pair/knowledge/index.md, etc.)
  3. 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)
  4. 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)
  5. 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.

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