You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As a CLI user maintaining a project with an installed pair KB I want the CLI to compare the installed KB version against the current/available one and surface drift So that version mismatch is visible and points me to the right migration page — before it breaks skills
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 an installed KB older than the source's current version When the user runs kb-info (or an explicit version check) Then the CLI reports installed vs current versions, flags the mismatch, and prints the migration-docs URL for the jump
Given an installed KB matching the current version When the check runs Then the CLI reports up-to-date, exit code 0
Giveninstall/update about to apply a version with breaking changes When the operation starts Then the CLI surfaces the mismatch hint (non-blocking) with the migration page pointer
Given a KB installed without version metadata (legacy install) When the check runs Then the CLI degrades gracefully: reports "unknown installed version" and suggests re-install/update to record it
Business Rules
Check compares metadata only — the CLI performs NO migration (D20)
Works for official and external KB sources (version from package/source manifest)
Machine-readable output (JSON flag) consistent with existing kb-info
Edge Cases and Error Handling
Source unreachable: report installed version, mark current as unavailable, exit 0 (no hard failure offline)
Pre-release/dev versions: compared verbatim, labeled as non-stable
Multiple registries: version reported per registry where applicable
Definition of Done Checklist
Development Completion
All acceptance criteria implemented and verified
Automated tests written and passing (fixtures: match, drift, legacy, offline)
Code review completed and approved
Documentation updated — docs site (apps/website): CLI reference for the check
CLI (apps/pair-cli) updated — this story is CLI work by definition
Final Story Points: 3 (M) Confidence Level: High Sizing Justification: kb-info already reads installed metadata; delta = current-version resolution + comparison + messaging — confirmed M(3)
Sprint Capacity Validation
Sprint Fit Assessment: yes, single sprint Total Effort Assessment: fits — Yes
Dependencies and Coordination
Story Dependencies
Prerequisite Stories: none (metadata base exists in kb-info) Dependent Stories: migration pages story (URL target); dogfood migration (uses the check to verify)
Validation and Testing Strategy
Acceptance Testing Approach
Testing Methods: unit + CLI integration tests with version fixtures; offline-mode test; manual smoke on the pair repo Test Data Requirements: KB fixtures at two versions; one legacy (no metadata)
Notes and Additional Context
Refinement Session Insights: D20 — check-only scope; base already in kb-info; mismatch message must name the exact migration page (one per jump) Documentation Links: D20 · Spec G11 · epic AC2/AC3
Technical Analysis
Implementation Approach
Technical Strategy: extend kb-info with current-version resolution from the configured source (registry/URL/--source), add comparison + human and JSON outputs; hook a non-blocking hint into install/update flows Key Components: version resolver (per source type), comparator, message formatter, migration-URL mapper (version jump → docs path) Integration Points: existing kb-info, install/update commands, docs Migrations index
Technical Risks and Mitigation
Risk
Impact
Probability
Mitigation Strategy
Source types report versions inconsistently
Medium
Medium
normalize in the resolver; verbatim fallback with "non-comparable" label
URL mapping drifts from docs structure
Low
Medium
single mapping helper + docs index test fixture
Task Breakdown
Checklist
T1 — Current-version resolver per source type (registry/URL/local)
T2 — Comparison + human/JSON output in kb-info
T3 — Non-blocking mismatch hint in install/update + migration-URL mapper
Summary: Resolve the "current" (available) KB version for any of the source kinds pair-cli already accepts (registry default, remote URL, local path/zip, git), plus read/write helpers for the version recorded as installed in a project.
Type: Feature Implementation
Description: Implemented in apps/pair-cli/src/commands/kb-info/version-resolver.ts. resolveCurrentVersion(fs, options) detects the source kind from an optional --source value (detectSourceKind: git URL → git, remote URL → remote, path → local, absent → registry) and dispatches to a per-kind resolver, never throwing — unexpected errors degrade to { available: false, error }. readVersionFromDirectory reads a version from manifest.json at a directory root, falling back to a sibling package.json (monorepo packages/knowledge-hub/dataset layout). resolveRegistryVersion mirrors install/update's own dataset resolution via resolveDatasetRoot(fs, { resolution: 'default' }, { cliVersion, httpClient }) — this is a fix (commit b442f54) over the original implementation, which called the monorepo dataset path directly and threw for real npm-installed (non-monorepo) users; it now falls back to download/cache exactly like install/update do, with cliVersion threaded through dispatcher → handler → resolver. resolveLocalVersion reads a directory or a .zip (via readManifestFromZip, shared with kb-info's package-display mode). resolveRemoteVersion extracts a version from the URL itself and probes reachability via an injected HttpClientService (offline/no-client degrades to unavailable, per AC "Source unreachable"). resolveInstalledVersion/writeInstalledVersion read/write a .pair/.kb-version.json marker ({ version, recordedAt }); a missing or malformed marker degrades to { version: null } (AC4, legacy installs). isStableVersion flags plain major.minor.patch as stable, anything else (pre-release/build tags) as non-stable.
Acceptance Criteria:
Primary deliverable: resolveCurrentVersion returns { sourceKind, version, available, stable, error? } for registry/local/remote/git sources; resolveInstalledVersion/writeInstalledVersion round-trip the installed-version marker.
Quality standard: never throws — all source resolvers degrade to { available: false, error } on failure (offline, missing path, bad zip, unsupported source).
Integration requirement: registry resolution reuses resolveDatasetRoot's 'default' resolution (same code path as install/update), not a bespoke monorepo-only lookup.
Verification method: unit tests per source kind in version-resolver.test.ts, including the release-context (non-monorepo, download-fallback) scenario.
Implementation Approach:
Technical Design: single resolveCurrentVersion entrypoint dispatching on KbSourceKind; each source resolver returns a partial result merged with sourceKind/stable by the entrypoint so isStableVersion is applied uniformly.
Define KbSourceKind, CurrentVersionResult, InstalledVersionResult types and detectSourceKind.
Implement readVersionFromDirectory (manifest.json → sibling package.json fallback) and isStableVersion.
Implement per-source resolvers (resolveRegistryVersion, resolveLocalVersion, resolveRemoteVersion, git stub) and the resolveCurrentVersion dispatcher with try/catch degradation.
Implement resolveInstalledVersion/writeInstalledVersion against the .pair/.kb-version.json marker.
Fix resolveRegistryVersion to use resolveDatasetRoot's 'default' resolution instead of a direct monorepo-only path, threading cliVersion through from the dispatcher; gitignore the marker file.
Testing Strategy:
Unit Tests: version-resolver.test.ts — isStableVersion, readVersionFromDirectory (manifest vs sibling package.json vs neither), registry source (available / unavailable / release-context download-fallback with isKBCached/ensureKBAvailable mocked), local source (dir/zip/missing), remote source (reachable/unreachable/no-client), git source (graceful degrade), installed-version marker round-trip (missing/present/malformed).
Manual Testing: none beyond the automated suite for this task — manual smoke is covered by T4.
Notes: The registry-fallback fix and the .gitignore entry (both from commit b442f54) are genuinely present on this branch — verified in the current diff and by the added version-resolver.test.ts case "falls back to download in a real release install (no monorepo, no local dataset, network available)". No open gap for this task specifically.
Summary: Add a version-check mode to pair kb-info (triggered when no package path is given) that compares installed vs. current version and prints a human-readable or JSON result.
Type: Feature Implementation
Description:apps/pair-cli/src/commands/kb-info/parser.ts now returns a discriminated KbInfoCommandConfig (mode: 'package' | 'version-check'): a positional arg selects package mode (unchanged behavior), its absence selects version-check mode with optional --json/--source. version-check.ts's compareVersions(installed, current) is a pure function producing one of four statuses: up-to-date (equal, known versions), drift (both known, differ — attaches migrationUrl via buildMigrationUrl), unknown-installed (no installed marker — checked first, AC4), current-unavailable (installed known but current not resolvable — AC offline case). version-check-formatter.ts renders this as either a human block (formatVersionCheckHuman, includes status/installed/current/source lines, a "(non-stable)" suffix, migration-guide line for drift, remediation copy for unknown/unavailable) or raw JSON (formatVersionCheckJSON, just JSON.stringify(result) — the whole VersionCheckResult shape, consistent with existing kb-info --json behavior in package mode). handler.ts's handleVersionCheckMode wires resolveInstalledVersion + resolveCurrentVersion + compareVersions + the formatter, and always returns exit code 0 (this is a check, never a hard failure — matches AC2's "exit code 0" and the docs' explicit "Exit code is 0 in all cases"). dispatcher.ts was updated to pass opts (containing httpClient/baseTarget/cliVersion) into the kb-info handler call, which it previously didn't need for package-only mode.
Acceptance Criteria:
Primary deliverable: pair kb-info (no path) prints installed/current/status/source, JSON via --json, with an explicit migration URL on drift.
Quality standard: exit code 0 for all four statuses (up-to-date/drift/unknown-installed/current-unavailable) — this is metadata-only, no hard failures (D20).
Integration requirement: JSON output is the full VersionCheckResult object (status, installed, current, optional migrationUrl), machine-consumable and consistent with the existing package-mode --json.
Verification method: version-check.test.ts (pure comparator, all 4+ statuses incl. pre-release verbatim compare) and handler.test.ts's version-check mode describe block (end-to-end through handleKbInfoCommand, incl. remote --source), plus version-check-formatter.test.ts and parser.test.ts.
Implementation Approach:
Technical Design: strict separation between comparison (pure, version-check.ts) and presentation (version-check-formatter.ts), matching the codebase's existing display-formatter.ts split for package mode.
Wire handleVersionCheckMode into handleKbInfoCommand, threading baseTarget/httpClient/cliVersion options.
Update dispatcher.ts to pass opts to the kb-info handler and metadata.ts's CLI help text.
Testing Strategy:
Unit Tests: version-check.test.ts (match/drift/legacy/offline fixtures + pre-release verbatim), version-check-formatter.test.ts (all four statuses' human output + JSON round-trip), parser.test.ts (mode discrimination, --source/--json passthrough).
Manual Testing: handler.test.ts's version-check describe block exercises the full flow through handleKbInfoCommand with an in-memory fs and a mock HTTP client (registry match, registry drift with JSON, legacy, registry-unavailable, remote unreachable, remote reachable) — no separate manual smoke was run for this task beyond that.
Notes: The AC "Multiple registries: version reported per registry where applicable" business rule is not implemented anywhere in this task (or elsewhere on the branch) — resolveCurrentVersion/compareVersions operate on a single resolved source/version, with no notion of iterating the project's configured asset_registries and reporting a version per registry. This is a genuine, unaddressed gap against the story's Business Rules section, not just a documentation nit. Also, "JSON output schema validated" (DoD) is only informally covered — tests assert toMatchObject shape expectations, not validation against a formal JSON schema document.
T-3: Non-blocking mismatch hint in install/update + migration-URL mapper
Summary: Print a non-blocking drift hint at the start of install/update when the version about to be applied differs from the recorded installed version, and record the newly-applied version on success.
Type: Feature Implementation
Description:migration-url.ts is the single source of truth for the docs URL pattern (D20: one page per version jump, no migration logic in the CLI): buildMigrationUrl(from, to) → ${DOCS_MIGRATIONS_BASE}/v{from}-to-v{to} (strips a leading v from either input), migrationsIndexUrl() returns the base index page. version-hint.ts exposes two best-effort, never-throwing helpers: emitVersionDriftHint({ fs, datasetRoot, baseTarget, presenter }) reads the new version from the about-to-be-applied dataset (readVersionFromDirectory), reads the recorded installed version (resolveInstalledVersion), and — only if both are known and differ — prints a presenter.phase(...) line with the migration URL; it silently no-ops if either version is unknown or they match. recordInstalledVersion({ fs, datasetRoot, baseTarget }) writes the just-applied version via writeInstalledVersion after a successful install/update, so future kb-info checks have something to compare against (closing the AC4 "unknown installed version" loop). Both handlers (install/handler.ts, update/handler.ts) call emitVersionDriftHint right after resolving the dataset root/registries/baseTarget and before validating/executing the copy, and call recordInstalledVersion at the very end of their success path (executeInstall/runUpdateSequence), after writeProjectLlmsTxt.
Acceptance Criteria:
Primary deliverable: install/update print a "KB version drift" heads-up (with migration URL) when a prior recorded version differs from the version being applied, and always record the applied version on success.
Quality standard: the hint is genuinely non-blocking — any resolution failure inside emitVersionDriftHint/recordInstalledVersion is swallowed (try/catch, no rethrow) and never aborts install/update.
Integration requirement: uses the same migration-url.ts mapper as kb-info's drift status — one URL-building implementation, not duplicated logic.
Verification method: install/handler.test.ts and update/handler.test.ts's KB version recording (#261) describe blocks (marker written, hint printed, no marker written when the source has no version metadata).
Implementation Approach:
Technical Design: hint emission is a pure side-effecting read (never mutates), recording is a pure side-effecting write; both delegate to version-resolver.ts primitives from T1 rather than re-implementing marker I/O.
apps/pair-cli/src/commands/install/handler.ts — hook both calls into handleInstallCommand/executeInstall
apps/pair-cli/src/commands/update/handler.ts — hook both calls into handleUpdateCommand/runUpdateSequence
Dependencies:
Tasks: T1 (resolver/marker primitives), T2 (shares migration-url.ts with the kb-info drift status — built alongside/after so the mapper is the single shared implementation)
Implementation Steps:
Implement buildMigrationUrl/migrationsIndexUrl in migration-url.ts.
Implement emitVersionDriftHint (read new + installed version, compare, print via CliPresenter.phase, swallow all errors).
Implement recordInstalledVersion (read new version, write marker, swallow all errors).
Hook emitVersionDriftHint into install/handler.ts (after setupInstallContext, before validation) and update/handler.ts (after setupUpdateContext, before validation).
Hook recordInstalledVersion into both handlers' success paths, after writeProjectLlmsTxt.
Testing Strategy:
Unit Tests: migration-url.test.ts (URL shape, leading-v stripping, index nesting); install/handler.test.ts and update/handler.test.ts "CLI KB version check: installed vs current + migration pointer #261" describe blocks (marker recorded on success, no marker when source lacks version metadata, drift hint text + migration URL printed on console when a prior differing version is recorded, install/update still completes successfully alongside the hint).
Manual Testing: none beyond the automated coverage above — no manual smoke test was run for the install/update hint on a real project in this task.
Notes: No discrepancy found for this task's own scope — the hint and recording behavior match what's tested. However, the story-level DoD item "Round-trip: install old fixture → check flags drift → update → check clean" is not implemented as a single test anywhere on the branch: install/handler.test.ts and update/handler.test.ts each test the hint/recording in isolation, and handler.test.ts's kb-info tests exercise the comparator independently, but no test chains install(old version) → kb-info (asserts drift) → update(new version) → kb-info (asserts up-to-date/clean) end-to-end. This is a real, open gap against the story's Quality Assurance checklist, not just a nice-to-have.
Summary: Cover all four version-check scenarios (match/drift/legacy/offline) end-to-end and document the new kb-info mode in the CLI reference.
Type: Testing / Documentation
Description: Rather than separate fixture files, the four required scenarios are expressed as inline InMemoryFileSystemService setups directly in each test file (a pattern already used throughout pair-cli's test suite): version-resolver.test.ts and version-check.test.ts each construct match/drift/legacy/offline states at the unit level, and handler.test.ts's "version-check mode" describe block re-exercises the same four scenarios end-to-end through handleKbInfoCommand (in-memory fs + MockHttpClientService), including both human and --json output for each. Docs were updated in apps/website/content/docs/reference/cli/commands.mdx: the kb-info section now documents the version-check mode (usage, --source option, the four status outcomes, exit-code-always-0 behavior, and the install/update hint), plus new examples (pair kb-info, pair kb-info --json, pair kb-info --source /path/to/kb) and an updated one-line command-list description.
Acceptance Criteria:
Primary deliverable: all four scenarios (match/drift/legacy/offline) are exercised both at the pure-comparator level and end-to-end through the CLI handler, in both human and JSON output modes.
Quality standard: tests use realistic in-memory fixtures (real manifest.json/package.json shapes, real marker-file JSON) rather than mocked-away comparator inputs only.
Integration requirement: docs site (apps/website) CLI reference reflects the new command surface (--source, version-check mode, exit-code semantics).
Verification method: pnpm --filter pair-cli test (all new/updated *.test.ts files pass); manual read-through of the rendered commands.mdx section.
Implementation Approach:
Technical Design: reuse existing in-memory fs/mock-http test utilities (InMemoryFileSystemService, MockHttpClientService, buildTestResponse/toIncomingMessage) already present in the @pair/content-ops test-support package; no new fixture-file format introduced.
Tasks: T1, T2, T3 (tests exercise all three layers; docs describe the combined behavior)
Implementation Steps:
Add match/drift/legacy/offline cases to version-resolver.test.ts and version-check.test.ts.
Extend handler.test.ts with the "version-check mode" describe block covering all four statuses in both output formats, plus remote --source reachable/unreachable cases.
Extend dispatcher.test.ts to confirm kb-info version-check mode dispatches (no path arg) without throwing.
Update commands.mdx with the new usage, options table, "Version Check Mode" subsection, and examples.
Testing Strategy:
Unit Tests: as listed above — this task is itself the test-writing task; see T1–T3 for what each layer actually asserts.
Manual Testing: no manual smoke test against a real installed pair project was performed/recorded for this story (the story's "manual smoke on the pair repo" testing-strategy line is not evidenced by any script, log, or note on the branch).
Notes: Two real gaps to flag honestly, both already called out in T2/T3 but restated here since T4 owns "tests + docs" in the checklist: (1) no single round-trip test chains install(old)→check(drift)→update→check(clean) as required by the story's Quality Assurance checklist — the coverage is real but fragmented across separate unit/handler tests, not one end-to-end test; (2) the "multiple registries: version reported per registry" business rule has no test (and no implementation) anywhere — there was nothing to write a fixture for. Additionally, the story's "manual smoke on the pair repo" testing method has no corresponding evidence on the branch (no CHANGELOG note, no manual test log) — this may have been done ad hoc but is not verifiable from the diff.
Story Statement
As a CLI user maintaining a project with an installed pair KB
I want the CLI to compare the installed KB version against the current/available one and surface drift
So that version mismatch is visible and points me to the right migration page — before it breaks skills
Where:
pair-cli(kb-infoextension + install/update hint)Epic Context
Parent Epic: Versioning & migrazione adoption/KB #214
Status: Refined
Priority: P0 (Must-Have)
Status Workflow
Acceptance Criteria
Functional Requirements
Given-When-Then Format:
Given an installed KB older than the source's current version
When the user runs
kb-info(or an explicit version check)Then the CLI reports installed vs current versions, flags the mismatch, and prints the migration-docs URL for the jump
Given an installed KB matching the current version
When the check runs
Then the CLI reports up-to-date, exit code 0
Given
install/updateabout to apply a version with breaking changesWhen the operation starts
Then the CLI surfaces the mismatch hint (non-blocking) with the migration page pointer
Given a KB installed without version metadata (legacy install)
When the check runs
Then the CLI degrades gracefully: reports "unknown installed version" and suggests re-install/update to record it
Business Rules
kb-infoEdge Cases and Error Handling
Definition of Done Checklist
Development Completion
apps/website): CLI reference for the checkapps/pair-cli) updated — this story is CLI work by definitionQuality Assurance
Story Sizing and Sprint Readiness
Refined Story Points
Final Story Points: 3 (M)
Confidence Level: High
Sizing Justification:
kb-infoalready reads installed metadata; delta = current-version resolution + comparison + messaging — confirmed M(3)Sprint Capacity Validation
Sprint Fit Assessment: yes, single sprint
Total Effort Assessment: fits — Yes
Dependencies and Coordination
Story Dependencies
Prerequisite Stories: none (metadata base exists in
kb-info)Dependent Stories: migration pages story (URL target); dogfood migration (uses the check to verify)
Validation and Testing Strategy
Acceptance Testing Approach
Testing Methods: unit + CLI integration tests with version fixtures; offline-mode test; manual smoke on the pair repo
Test Data Requirements: KB fixtures at two versions; one legacy (no metadata)
Notes and Additional Context
Refinement Session Insights: D20 — check-only scope; base already in
kb-info; mismatch message must name the exact migration page (one per jump)Documentation Links: D20 · Spec G11 · epic AC2/AC3
Technical Analysis
Implementation Approach
Technical Strategy: extend
kb-infowith current-version resolution from the configured source (registry/URL/--source), add comparison + human and JSON outputs; hook a non-blocking hint into install/update flowsKey Components: version resolver (per source type), comparator, message formatter, migration-URL mapper (version jump → docs path)
Integration Points: existing
kb-info, install/update commands, docs Migrations indexTechnical Risks and Mitigation
Task Breakdown
Checklist
kb-infoDependency Graph
T1 → T2 → T3 → T4
AC Coverage
T-1: Current-version resolver per source type (registry/URL/local)
Priority: P0 | Estimated Hours: 4h | Bounded Context: Integration & Process Standardization
Summary: Resolve the "current" (available) KB version for any of the source kinds
pair-clialready accepts (registrydefault,remoteURL,localpath/zip,git), plus read/write helpers for the version recorded as installed in a project.Type: Feature Implementation
Description: Implemented in
apps/pair-cli/src/commands/kb-info/version-resolver.ts.resolveCurrentVersion(fs, options)detects the source kind from an optional--sourcevalue (detectSourceKind: git URL →git, remote URL →remote, path →local, absent →registry) and dispatches to a per-kind resolver, never throwing — unexpected errors degrade to{ available: false, error }.readVersionFromDirectoryreads a version frommanifest.jsonat a directory root, falling back to a siblingpackage.json(monorepopackages/knowledge-hub/datasetlayout).resolveRegistryVersionmirrors install/update's own dataset resolution viaresolveDatasetRoot(fs, { resolution: 'default' }, { cliVersion, httpClient })— this is a fix (commitb442f54) over the original implementation, which called the monorepo dataset path directly and threw for real npm-installed (non-monorepo) users; it now falls back to download/cache exactly likeinstall/updatedo, withcliVersionthreaded throughdispatcher → handler → resolver.resolveLocalVersionreads a directory or a.zip(viareadManifestFromZip, shared with kb-info's package-display mode).resolveRemoteVersionextracts a version from the URL itself and probes reachability via an injectedHttpClientService(offline/no-client degrades to unavailable, per AC "Source unreachable").resolveInstalledVersion/writeInstalledVersionread/write a.pair/.kb-version.jsonmarker ({ version, recordedAt }); a missing or malformed marker degrades to{ version: null }(AC4, legacy installs).isStableVersionflags plainmajor.minor.patchas stable, anything else (pre-release/build tags) as non-stable.Acceptance Criteria:
resolveCurrentVersionreturns{ sourceKind, version, available, stable, error? }for registry/local/remote/git sources;resolveInstalledVersion/writeInstalledVersionround-trip the installed-version marker.{ available: false, error }on failure (offline, missing path, bad zip, unsupported source).resolveDatasetRoot's'default'resolution (same code path asinstall/update), not a bespoke monorepo-only lookup.version-resolver.test.ts, including the release-context (non-monorepo, download-fallback) scenario.Implementation Approach:
resolveCurrentVersionentrypoint dispatching onKbSourceKind; each source resolver returns a partial result merged withsourceKind/stableby the entrypoint soisStableVersionis applied uniformly.apps/pair-cli/src/commands/kb-info/version-resolver.ts— resolver, marker read/write, stability check (new)apps/pair-cli/src/commands/kb-info/manifest-reader.ts—readManifestFromZip, extracted/shared with kb-info package mode (new)apps/pair-cli/src/commands/kb-info/handler.ts— threadcliVersion/httpClientinto the resolver (touched by theb442f54fix).gitignore— ignore.pair/.kb-version.json(untracked dogfood artifact)Dependencies:
Implementation Steps:
KbSourceKind,CurrentVersionResult,InstalledVersionResulttypes anddetectSourceKind.readVersionFromDirectory(manifest.json → sibling package.json fallback) andisStableVersion.resolveRegistryVersion,resolveLocalVersion,resolveRemoteVersion, git stub) and theresolveCurrentVersiondispatcher with try/catch degradation.resolveInstalledVersion/writeInstalledVersionagainst the.pair/.kb-version.jsonmarker.resolveRegistryVersionto useresolveDatasetRoot's'default'resolution instead of a direct monorepo-only path, threadingcliVersionthrough from the dispatcher; gitignore the marker file.Testing Strategy:
version-resolver.test.ts—isStableVersion,readVersionFromDirectory(manifest vs sibling package.json vs neither), registry source (available / unavailable / release-context download-fallback withisKBCached/ensureKBAvailablemocked), local source (dir/zip/missing), remote source (reachable/unreachable/no-client), git source (graceful degrade), installed-version marker round-trip (missing/present/malformed).Notes: The registry-fallback fix and the
.gitignoreentry (both from commitb442f54) are genuinely present on this branch — verified in the current diff and by the addedversion-resolver.test.tscase "falls back to download in a real release install (no monorepo, no local dataset, network available)". No open gap for this task specifically.T-2: Comparison + human/JSON output in
kb-infoPriority: P0 | Estimated Hours: 3h | Bounded Context: Integration & Process Standardization
Summary: Add a
version-checkmode topair kb-info(triggered when no package path is given) that compares installed vs. current version and prints a human-readable or JSON result.Type: Feature Implementation
Description:
apps/pair-cli/src/commands/kb-info/parser.tsnow returns a discriminatedKbInfoCommandConfig(mode: 'package' | 'version-check'): a positional arg selects package mode (unchanged behavior), its absence selects version-check mode with optional--json/--source.version-check.ts'scompareVersions(installed, current)is a pure function producing one of four statuses:up-to-date(equal, known versions),drift(both known, differ — attachesmigrationUrlviabuildMigrationUrl),unknown-installed(no installed marker — checked first, AC4),current-unavailable(installed known but current not resolvable — AC offline case).version-check-formatter.tsrenders this as either a human block (formatVersionCheckHuman, includes status/installed/current/source lines, a "(non-stable)" suffix, migration-guide line for drift, remediation copy for unknown/unavailable) or raw JSON (formatVersionCheckJSON, justJSON.stringify(result)— the wholeVersionCheckResultshape, consistent with existingkb-info --jsonbehavior in package mode).handler.ts'shandleVersionCheckModewiresresolveInstalledVersion+resolveCurrentVersion+compareVersions+ the formatter, and always returns exit code 0 (this is a check, never a hard failure — matches AC2's "exit code 0" and the docs' explicit "Exit code is 0 in all cases").dispatcher.tswas updated to passopts(containinghttpClient/baseTarget/cliVersion) into thekb-infohandler call, which it previously didn't need for package-only mode.Acceptance Criteria:
pair kb-info(no path) prints installed/current/status/source, JSON via--json, with an explicit migration URL on drift.VersionCheckResultobject (status,installed,current, optionalmigrationUrl), machine-consumable and consistent with the existing package-mode--json.version-check.test.ts(pure comparator, all 4+ statuses incl. pre-release verbatim compare) andhandler.test.ts'sversion-check modedescribe block (end-to-end throughhandleKbInfoCommand, incl. remote--source), plusversion-check-formatter.test.tsandparser.test.ts.Implementation Approach:
version-check.ts) and presentation (version-check-formatter.ts), matching the codebase's existingdisplay-formatter.tssplit for package mode.apps/pair-cli/src/commands/kb-info/version-check.ts—compareVersions,VersionCheckStatus/VersionCheckResult(new)apps/pair-cli/src/commands/kb-info/version-check-formatter.ts— human/JSON renderers (new)apps/pair-cli/src/commands/kb-info/handler.ts—handleVersionCheckMode, kepthandlePackageModeunchangedapps/pair-cli/src/commands/kb-info/parser.ts— discriminated config,--sourcesupportapps/pair-cli/src/commands/kb-info/metadata.ts— usage/examples/notes updated for the new modeapps/pair-cli/src/commands/dispatcher.ts— passoptsthrough tokb-infoDependencies:
Implementation Steps:
parser.tswith themodediscriminant and--sourcepassthrough; drop the old "package path is required" throw.compareVersionswith the four-status decision order (unknown-installed checked before current-unavailable, per AC4 precedence).formatVersionCheckHuman/formatVersionCheckJSON.handleVersionCheckModeintohandleKbInfoCommand, threadingbaseTarget/httpClient/cliVersionoptions.dispatcher.tsto passoptsto thekb-infohandler andmetadata.ts's CLI help text.Testing Strategy:
version-check.test.ts(match/drift/legacy/offline fixtures + pre-release verbatim),version-check-formatter.test.ts(all four statuses' human output + JSON round-trip),parser.test.ts(mode discrimination,--source/--jsonpassthrough).handler.test.ts's version-check describe block exercises the full flow throughhandleKbInfoCommandwith an in-memory fs and a mock HTTP client (registry match, registry drift with JSON, legacy, registry-unavailable, remote unreachable, remote reachable) — no separate manual smoke was run for this task beyond that.Notes: The AC "Multiple registries: version reported per registry where applicable" business rule is not implemented anywhere in this task (or elsewhere on the branch) —
resolveCurrentVersion/compareVersionsoperate on a single resolved source/version, with no notion of iterating the project's configuredasset_registriesand reporting a version per registry. This is a genuine, unaddressed gap against the story's Business Rules section, not just a documentation nit. Also, "JSON output schema validated" (DoD) is only informally covered — tests asserttoMatchObjectshape expectations, not validation against a formal JSON schema document.T-3: Non-blocking mismatch hint in install/update + migration-URL mapper
Priority: P0 | Estimated Hours: 2.5h | Bounded Context: Integration & Process Standardization
Summary: Print a non-blocking drift hint at the start of
install/updatewhen the version about to be applied differs from the recorded installed version, and record the newly-applied version on success.Type: Feature Implementation
Description:
migration-url.tsis the single source of truth for the docs URL pattern (D20: one page per version jump, no migration logic in the CLI):buildMigrationUrl(from, to)→${DOCS_MIGRATIONS_BASE}/v{from}-to-v{to}(strips a leadingvfrom either input),migrationsIndexUrl()returns the base index page.version-hint.tsexposes two best-effort, never-throwing helpers:emitVersionDriftHint({ fs, datasetRoot, baseTarget, presenter })reads the new version from the about-to-be-applied dataset (readVersionFromDirectory), reads the recorded installed version (resolveInstalledVersion), and — only if both are known and differ — prints apresenter.phase(...)line with the migration URL; it silently no-ops if either version is unknown or they match.recordInstalledVersion({ fs, datasetRoot, baseTarget })writes the just-applied version viawriteInstalledVersionafter a successful install/update, so futurekb-infochecks have something to compare against (closing the AC4 "unknown installed version" loop). Both handlers (install/handler.ts,update/handler.ts) callemitVersionDriftHintright after resolving the dataset root/registries/baseTarget and before validating/executing the copy, and callrecordInstalledVersionat the very end of their success path (executeInstall/runUpdateSequence), afterwriteProjectLlmsTxt.Acceptance Criteria:
install/updateprint a "KB version drift" heads-up (with migration URL) when a prior recorded version differs from the version being applied, and always record the applied version on success.emitVersionDriftHint/recordInstalledVersionis swallowed (try/catch, no rethrow) and never aborts install/update.migration-url.tsmapper askb-info's drift status — one URL-building implementation, not duplicated logic.install/handler.test.tsandupdate/handler.test.ts'sKB version recording (#261)describe blocks (marker written, hint printed, no marker written when the source has no version metadata).Implementation Approach:
version-resolver.tsprimitives from T1 rather than re-implementing marker I/O.apps/pair-cli/src/commands/kb-info/migration-url.ts—buildMigrationUrl,migrationsIndexUrl(new)apps/pair-cli/src/commands/kb-info/version-hint.ts—emitVersionDriftHint,recordInstalledVersion(new)apps/pair-cli/src/commands/install/handler.ts— hook both calls intohandleInstallCommand/executeInstallapps/pair-cli/src/commands/update/handler.ts— hook both calls intohandleUpdateCommand/runUpdateSequenceDependencies:
migration-url.tswith thekb-infodrift status — built alongside/after so the mapper is the single shared implementation)Implementation Steps:
buildMigrationUrl/migrationsIndexUrlinmigration-url.ts.emitVersionDriftHint(read new + installed version, compare, print viaCliPresenter.phase, swallow all errors).recordInstalledVersion(read new version, write marker, swallow all errors).emitVersionDriftHintintoinstall/handler.ts(aftersetupInstallContext, before validation) andupdate/handler.ts(aftersetupUpdateContext, before validation).recordInstalledVersioninto both handlers' success paths, afterwriteProjectLlmsTxt.Testing Strategy:
migration-url.test.ts(URL shape, leading-vstripping, index nesting);install/handler.test.tsandupdate/handler.test.ts"CLI KB version check: installed vs current + migration pointer #261" describe blocks (marker recorded on success, no marker when source lacks version metadata, drift hint text + migration URL printed on console when a prior differing version is recorded, install/update still completes successfully alongside the hint).Notes: No discrepancy found for this task's own scope — the hint and recording behavior match what's tested. However, the story-level DoD item "Round-trip: install old fixture → check flags drift → update → check clean" is not implemented as a single test anywhere on the branch:
install/handler.test.tsandupdate/handler.test.tseach test the hint/recording in isolation, andhandler.test.ts'skb-infotests exercise the comparator independently, but no test chains install(old version) →kb-info(asserts drift) → update(new version) →kb-info(asserts up-to-date/clean) end-to-end. This is a real, open gap against the story's Quality Assurance checklist, not just a nice-to-have.T-4: Fixtures + integration tests (match/drift/legacy/offline) + docs
Priority: P1 | Estimated Hours: 2h | Bounded Context: Integration & Process Standardization
Summary: Cover all four version-check scenarios (match/drift/legacy/offline) end-to-end and document the new
kb-infomode in the CLI reference.Type: Testing / Documentation
Description: Rather than separate fixture files, the four required scenarios are expressed as inline
InMemoryFileSystemServicesetups directly in each test file (a pattern already used throughoutpair-cli's test suite):version-resolver.test.tsandversion-check.test.tseach construct match/drift/legacy/offline states at the unit level, andhandler.test.ts's "version-check mode" describe block re-exercises the same four scenarios end-to-end throughhandleKbInfoCommand(in-memory fs +MockHttpClientService), including both human and--jsonoutput for each. Docs were updated inapps/website/content/docs/reference/cli/commands.mdx: thekb-infosection now documents the version-check mode (usage,--sourceoption, the four status outcomes, exit-code-always-0 behavior, and the install/update hint), plus new examples (pair kb-info,pair kb-info --json,pair kb-info --source /path/to/kb) and an updated one-line command-list description.Acceptance Criteria:
apps/website) CLI reference reflects the new command surface (--source, version-check mode, exit-code semantics).pnpm --filter pair-cli test(all new/updated*.test.tsfiles pass); manual read-through of the renderedcommands.mdxsection.Implementation Approach:
InMemoryFileSystemService,MockHttpClientService,buildTestResponse/toIncomingMessage) already present in the@pair/content-opstest-support package; no new fixture-file format introduced.apps/pair-cli/src/commands/kb-info/version-resolver.test.ts— per-source-kind scenarios (extended)apps/pair-cli/src/commands/kb-info/version-check.test.ts— comparator match/drift/legacy/offline + pre-release (new)apps/pair-cli/src/commands/kb-info/handler.test.ts— end-to-end version-check describe block (extended)apps/pair-cli/src/commands/dispatcher.test.ts— version-check mode dispatches without throwing (extended)apps/website/content/docs/reference/cli/commands.mdx— kb-info docs (extended)Dependencies:
Implementation Steps:
version-resolver.test.tsandversion-check.test.ts.handler.test.tswith the "version-check mode" describe block covering all four statuses in both output formats, plus remote--sourcereachable/unreachable cases.dispatcher.test.tsto confirmkb-infoversion-check mode dispatches (no path arg) without throwing.commands.mdxwith the new usage, options table, "Version Check Mode" subsection, and examples.Testing Strategy:
Notes: Two real gaps to flag honestly, both already called out in T2/T3 but restated here since T4 owns "tests + docs" in the checklist: (1) no single round-trip test chains install(old)→check(drift)→update→check(clean) as required by the story's Quality Assurance checklist — the coverage is real but fragmented across separate unit/handler tests, not one end-to-end test; (2) the "multiple registries: version reported per registry" business rule has no test (and no implementation) anywhere — there was nothing to write a fixture for. Additionally, the story's "manual smoke on the pair repo" testing method has no corresponding evidence on the branch (no CHANGELOG note, no manual test log) — this may have been done ad hoc but is not verifiable from the diff.