Story Statement
As a CLI user or automation script wrapping pair install/pair update
I want --source <url> to always download and apply the specified KB archive
So that I can reliably switch between KB sources without manually deleting cache directories
Where: pair install --source <url> and pair update --source <url> CLI commands
Epic Context
Parent Epic: N/A (standalone bug fix)
Status: Done
Priority: P0 (Must-Have) — silent data staleness affects all --source users
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:
-
Given a cached KB exists at ~/.pair/kb/<version> (from a previous default download)
When the user runs pair update --source https://example.com/other-kb.zip
Then the CLI downloads and extracts the new archive, replacing the cached KB for that version
-
Given a cached KB exists at ~/.pair/kb/<version> (from a previous --source A URL)
When the user runs pair update --source <URL-B> (a different URL)
Then the CLI downloads from URL-B and replaces the cached KB
-
Given a cached KB exists at ~/.pair/kb/<version> and no --source is provided
When the user runs pair update (default)
Then the CLI returns the cached path immediately (existing cache-hit behavior preserved)
-
Given a cached KB exists at ~/.pair/kb/<version>
When the user runs pair install --source /local/path/to/kb
Then the CLI installs from the local path, replacing the cached KB
-
Given a cached KB exists and --source points to a URL that fails (404, network error)
When the download fails
Then the existing cached KB is NOT deleted and an actionable error is shown
Business Rules
- Cache bypass applies only when
customUrl / --source is explicitly provided
- Default behavior (no
--source) must remain unchanged: cache-hit returns immediately
- Failed re-downloads must not corrupt or delete the existing cache (atomic replacement)
- The fix must work for all source types: remote URL, local ZIP, local directory
Edge Cases and Error Handling
- Same URL re-download: When
--source points to the same URL as the cached version, the download still occurs (no URL comparison optimization needed for v1 — simplicity over optimization)
- Concurrent access: Not in scope — CLI is single-process per invocation
- Disk space: If extraction fails mid-way, cleanup must remove partial files (existing cleanup logic applies)
- Version with
v prefix: v0.4.2 and 0.4.2 must both work correctly (existing normalization in cache-manager.ts handles this)
Definition of Done Checklist
Development Completion
Quality Assurance
Deployment and Release
Story Sizing and Sprint Readiness
Refined Story Points
Final Story Points: S(2)
Confidence Level: High
Sizing Justification: The fix is a small, well-scoped change to ensureKBAvailable in kb-availability.ts — bypass cache early-return when customUrl is provided, plus delete-and-redownload logic. Affected surface is narrow (one function + one cache utility). Tests are straightforward using existing InMemoryFileSystemService and MockHttpClientService patterns.
Sprint Capacity Validation
Sprint Fit Assessment: Easily fits in a single sprint
Development Time Estimate: 0.5 days
Testing Time Estimate: 0.5 days
Total Effort Assessment: Fits within sprint capacity: Yes
Story Splitting Recommendations
No split needed — story is already XS/S sized.
Dependencies and Coordination
Story Dependencies
Prerequisite Stories: None
Dependent Stories: None
Shared Components: @pair/content-ops (InMemoryFileSystemService, MockHttpClientService — test utilities only)
Team Coordination
Development Roles Involved:
- Backend/CLI: Modify
kb-availability.ts and cache-manager.ts, write tests
External Dependencies
Third-party Integrations: None
Infrastructure Requirements: None
Compliance Requirements: None
Validation and Testing Strategy
Acceptance Testing Approach
Testing Methods: Unit tests using existing in-memory FS and mock HTTP patterns from kb-availability.test.ts
Test Data Requirements: Pre-seeded InMemoryFileSystemService with cache directory, MockHttpClientService with zip responses
Environment Requirements: Standard vitest test runner
User Validation
Success Metrics: pair update --source <url> always applies the new URL when cache exists
Rollback Plan: Revert the commit — existing behavior (always cache-hit) is restored
Notes and Additional Context
Refinement Session Insights: The bug is a missing condition check — ensureKBAvailable does not distinguish between "no source specified (use cache)" and "explicit source specified (bypass cache)". The simplest fix is to skip the cache early-return when customUrl is truthy. For atomic safety, a backup/restore pattern ensures the existing cache is preserved on failed downloads.
Future Considerations: A more sophisticated approach (URL-keyed cache slots, metadata tracking) could be added later if multi-source caching becomes a requirement. For now, bypass-on-explicit-source is the right trade-off.
Original Bug Report: Observed with pair update / consumer scripts wrapping the CLI; cache path logged as ~/.pair/kb/0.4.2 while release asset version differed.
Technical Analysis
Implementation Approach
Technical Strategy: Bypass the isKBCached early-return in ensureKBAvailable when deps.customUrl is provided. When bypassing, use atomic backup/restore pattern: rename existing cache to .bak, attempt install, restore on failure, delete backup on success.
Key Components:
apps/pair-cli/src/kb-manager/kb-availability.ts — add customUrl check before cache early-return + installFromSource helper
apps/pair-cli/src/kb-manager/cache-manager.ts — add backupCachedKB, restoreCachedKB, removeBackupKB utilities
Data Flow:
- CLI parses
--source <url> into customUrl
ensureKBAvailable(version, { customUrl, ... }) is called
- If
customUrl is truthy, backup existing cache to .bak
- Proceed to source detection and installation (via
installFromSource)
- On success: remove backup, return installed path
- On failure: restore backup from
.bak, rethrow error
Integration Points: No external integrations. Change is internal to kb-manager module.
Technical Requirements
ensureKBAvailable must bypass cache when customUrl is truthy
- Atomic replacement via backup/restore ensures cache survives failed downloads
backupCachedKB must be safe to call when no cache exists (returns false)
- All existing tests must remain green
Technical Risks and Mitigation
| Risk |
Impact |
Probability |
Mitigation Strategy |
Breaking default (no --source) cache behavior |
High |
Very Low |
AC #3 explicitly tests this. Existing cache-hit tests cover it. |
Spike Requirements
Required Spikes: None — implementation approach is clear from code analysis.
Task Breakdown
Dependency Graph
T-1 (failing tests) ── T-2 (fix) ── T-3 (regression tests)
AC Coverage
| AC |
Tasks |
AC-1 (cache exists + remote --source re-downloads) |
T-1, T-2 |
AC-2 (different --source URL re-downloads) |
T-1, T-2 |
AC-3 (no --source preserves cache-hit) |
T-3 |
AC-4 (local --source path re-installs) |
T-3 |
AC-5 (failed --source preserves cache via backup/restore) |
T-2, T-3 |
Refinement Completed By: AI Agent (Claude)
Refinement Date: 2026-04-11
Delivered: 2026-04-11 — PR #194 merged (squash → 3467d17)
Story Statement
As a CLI user or automation script wrapping
pair install/pair updateI want
--source <url>to always download and apply the specified KB archiveSo that I can reliably switch between KB sources without manually deleting cache directories
Where:
pair install --source <url>andpair update --source <url>CLI commandsEpic Context
Parent Epic: N/A (standalone bug fix)
Status: Done
Priority: P0 (Must-Have) — silent data staleness affects all
--sourceusersStatus Workflow
Acceptance Criteria
Functional Requirements
Given-When-Then Format:
Given a cached KB exists at
~/.pair/kb/<version>(from a previous default download)When the user runs
pair update --source https://example.com/other-kb.zipThen the CLI downloads and extracts the new archive, replacing the cached KB for that version
Given a cached KB exists at
~/.pair/kb/<version>(from a previous--source AURL)When the user runs
pair update --source <URL-B>(a different URL)Then the CLI downloads from URL-B and replaces the cached KB
Given a cached KB exists at
~/.pair/kb/<version>and no--sourceis providedWhen the user runs
pair update(default)Then the CLI returns the cached path immediately (existing cache-hit behavior preserved)
Given a cached KB exists at
~/.pair/kb/<version>When the user runs
pair install --source /local/path/to/kbThen the CLI installs from the local path, replacing the cached KB
Given a cached KB exists and
--sourcepoints to a URL that fails (404, network error)When the download fails
Then the existing cached KB is NOT deleted and an actionable error is shown
Business Rules
customUrl/--sourceis explicitly provided--source) must remain unchanged: cache-hit returns immediatelyEdge Cases and Error Handling
--sourcepoints to the same URL as the cached version, the download still occurs (no URL comparison optimization needed for v1 — simplicity over optimization)vprefix:v0.4.2and0.4.2must both work correctly (existing normalization incache-manager.tshandles this)Definition of Done Checklist
Development Completion
Quality Assurance
pnpm quality-gatepassescli.e2e.test.tsstill passDeployment and Release
Story Sizing and Sprint Readiness
Refined Story Points
Final Story Points: S(2)
Confidence Level: High
Sizing Justification: The fix is a small, well-scoped change to
ensureKBAvailableinkb-availability.ts— bypass cache early-return whencustomUrlis provided, plus delete-and-redownload logic. Affected surface is narrow (one function + one cache utility). Tests are straightforward using existingInMemoryFileSystemServiceandMockHttpClientServicepatterns.Sprint Capacity Validation
Sprint Fit Assessment: Easily fits in a single sprint
Development Time Estimate: 0.5 days
Testing Time Estimate: 0.5 days
Total Effort Assessment: Fits within sprint capacity: Yes
Story Splitting Recommendations
No split needed — story is already XS/S sized.
Dependencies and Coordination
Story Dependencies
Prerequisite Stories: None
Dependent Stories: None
Shared Components:
@pair/content-ops(InMemoryFileSystemService, MockHttpClientService — test utilities only)Team Coordination
Development Roles Involved:
kb-availability.tsandcache-manager.ts, write testsExternal Dependencies
Third-party Integrations: None
Infrastructure Requirements: None
Compliance Requirements: None
Validation and Testing Strategy
Acceptance Testing Approach
Testing Methods: Unit tests using existing in-memory FS and mock HTTP patterns from
kb-availability.test.tsTest Data Requirements: Pre-seeded InMemoryFileSystemService with cache directory, MockHttpClientService with zip responses
Environment Requirements: Standard vitest test runner
User Validation
Success Metrics:
pair update --source <url>always applies the new URL when cache existsRollback Plan: Revert the commit — existing behavior (always cache-hit) is restored
Notes and Additional Context
Refinement Session Insights: The bug is a missing condition check —
ensureKBAvailabledoes not distinguish between "no source specified (use cache)" and "explicit source specified (bypass cache)". The simplest fix is to skip the cache early-return whencustomUrlis truthy. For atomic safety, a backup/restore pattern ensures the existing cache is preserved on failed downloads.Future Considerations: A more sophisticated approach (URL-keyed cache slots, metadata tracking) could be added later if multi-source caching becomes a requirement. For now, bypass-on-explicit-source is the right trade-off.
Original Bug Report: Observed with
pair update/ consumer scripts wrapping the CLI; cache path logged as~/.pair/kb/0.4.2while release asset version differed.Technical Analysis
Implementation Approach
Technical Strategy: Bypass the
isKBCachedearly-return inensureKBAvailablewhendeps.customUrlis provided. When bypassing, use atomic backup/restore pattern: rename existing cache to.bak, attempt install, restore on failure, delete backup on success.Key Components:
apps/pair-cli/src/kb-manager/kb-availability.ts— addcustomUrlcheck before cache early-return +installFromSourcehelperapps/pair-cli/src/kb-manager/cache-manager.ts— addbackupCachedKB,restoreCachedKB,removeBackupKButilitiesData Flow:
--source <url>intocustomUrlensureKBAvailable(version, { customUrl, ... })is calledcustomUrlis truthy, backup existing cache to.bakinstallFromSource).bak, rethrow errorIntegration Points: No external integrations. Change is internal to
kb-managermodule.Technical Requirements
ensureKBAvailablemust bypass cache whencustomUrlis truthybackupCachedKBmust be safe to call when no cache exists (returns false)Technical Risks and Mitigation
--source) cache behaviorSpike Requirements
Required Spikes: None — implementation approach is clear from code analysis.
Task Breakdown
backupCachedKB/restoreCachedKB/removeBackupKB+ bypass cache inensureKBAvailableDependency Graph
AC Coverage
--sourcere-downloads)--sourceURL re-downloads)--sourcepreserves cache-hit)--sourcepath re-installs)--sourcepreserves cache via backup/restore)Refinement Completed By: AI Agent (Claude)
Refinement Date: 2026-04-11
Delivered: 2026-04-11 — PR #194 merged (squash →
3467d17)