# TRUEREF-0014 — Repository Version Management **Priority:** P1 **Status:** Pending **Depends On:** TRUEREF-0003 **Blocks:** — --- ## Overview Support indexing specific git tags and branches as distinct versioned snapshots of a repository. Users can query documentation for a specific version using the `/owner/repo/version` library ID format. Versions are registered via `trueref.json`'s `previousVersions` field or manually via the API. --- ## Acceptance Criteria - [ ] `GET /api/v1/libs/:id/versions` — list all indexed versions for a repository - [ ] `POST /api/v1/libs/:id/versions` — add a new version (tag or branch) - [ ] `DELETE /api/v1/libs/:id/versions/:versionTag` — remove a version and its snippets - [ ] `POST /api/v1/libs/:id/versions/:versionTag/index` — trigger indexing for a specific version - [ ] Version-specific queries: `/api/v1/context?libraryId=/facebook/react/v18.3.0` - [ ] Default branch queries: `/api/v1/context?libraryId=/facebook/react` (no version suffix) - [ ] `previousVersions` from `trueref.json` automatically registered during indexing (state: `pending`) - [ ] GitHub tag list endpoint used to validate tag existence before indexing - [ ] Version snippets stored with `versionId` FK; default branch snippets have `versionId = NULL` --- ## Version ID Convention ``` Version ID format: {repositoryId}/{tag} Examples: /facebook/react/v18.3.0 /facebook/react/v17.0.2 /vercel/next.js/v14.3.0-canary.1 ``` --- ## API Endpoints ### `GET /api/v1/libs/:id/versions` Response `200`: ```json { "versions": [ { "id": "/facebook/react/v18.3.0", "repositoryId": "/facebook/react", "tag": "v18.3.0", "title": "React v18.3.0", "state": "indexed", "totalSnippets": 892, "indexedAt": "2026-03-22T10:00:00Z" } ] } ``` ### `POST /api/v1/libs/:id/versions` Request body: ```json { "tag": "v18.3.0", "title": "React v18.3.0", "autoIndex": true } ``` Response `201`: ```json { "version": { ...RepositoryVersion }, "job": { "id": "uuid", "status": "queued" } } ``` ### `DELETE /api/v1/libs/:id/versions/:tag` Deletes the version record and all associated documents/snippets via cascade. Response `204`. ### `POST /api/v1/libs/:id/versions/:tag/index` Queues an indexing job for this specific version tag. Response `202` with job details. --- ## GitHub Tag Discovery ```typescript async function listGitHubTags( owner: string, repo: string, token?: string ): Promise> { const headers: Record = { 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'TrueRef/1.0', }; if (token) headers['Authorization'] = `Bearer ${token}`; const response = await fetch( `https://api.github.com/repos/${owner}/${repo}/tags?per_page=100`, { headers } ); if (!response.ok) throw new GitHubApiError(response.status); return response.json(); } ``` --- ## Query Routing In the search/context endpoints, the `libraryId` is parsed to extract the optional version: ```typescript function resolveSearchTarget(libraryId: string): { repositoryId: string; versionId?: string; } { const { repositoryId, version } = parseLibraryId(libraryId); if (!version) { // Query default branch: versionId = NULL return { repositoryId }; } // Look up versionId from tag const versionRecord = db.prepare( `SELECT id FROM repository_versions WHERE repository_id = ? AND tag = ?` ).get(repositoryId, version) as { id: string } | undefined; if (!versionRecord) { throw new NotFoundError( `Version "${version}" not found for library "${repositoryId}"` ); } return { repositoryId, versionId: versionRecord.id }; } ``` Snippets with `version_id IS NULL` belong to the default branch; snippets with a `version_id` belong to that specific version. Search queries filter by `version_id = ?` or `version_id IS NULL` accordingly. --- ## Version Service ```typescript export class VersionService { constructor(private db: BetterSQLite3.Database) {} list(repositoryId: string): RepositoryVersion[] add(repositoryId: string, tag: string, title?: string): RepositoryVersion remove(repositoryId: string, tag: string): void getByTag(repositoryId: string, tag: string): RepositoryVersion | null registerFromConfig( repositoryId: string, previousVersions: { tag: string; title: string }[] ): RepositoryVersion[] } ``` --- ## Files to Create - `src/lib/server/services/version.service.ts` - `src/routes/api/v1/libs/[id]/versions/+server.ts` — GET, POST - `src/routes/api/v1/libs/[id]/versions/[tag]/+server.ts` — DELETE - `src/routes/api/v1/libs/[id]/versions/[tag]/index/+server.ts` — POST - `src/lib/server/crawler/github-tags.ts`