# Catalog ## Overview The Catalog is the persistence layer for all agent configuration. Resources (agents, models, tools, knowledge bases) are stored as Kubernetes CR YAML files in a Git-backed store. The `Catalog` class is the top-level entry point; it holds one `WorkspaceCatalog` per workspace, each backed by a `GitClient` appropriate to the workspace's persistence type. ## Architecture ``` Catalog └── Map WorkspaceCatalog ├── models: ModelCatalog ├── tools: ToolCatalog ├── kbs: KBCatalog ├── documents: DocumentCatalog ├── agents: AgentCatalog ├── stacks: StackCatalog └── secrets: SecretCatalog *Catalog ├── list() ├── listChanges(id) → PR history + commit log for that resource's file ├── create(entity) ├── update(entity) └── delete(id) ``` `Catalog` is initialized by `Server` at startup. `getWorkspaceCatalog(workspaceId)` returns the `WorkspaceCatalog` for the requested workspace ID; route handlers call this using `locals.workspace.id`. ## GitCatalog — generic base `GitCatalog` is an abstract base class that implements the full CRUD lifecycle for any entity type. Concrete subclasses (`GitModelCatalog`, `GitToolCatalog`, `GitKBCatalog`, `GitDocumentCatalog`, `GitAgentCatalog`) extend it, supplying entity-specific YAML parse and serialize functions. **Initialization + sync**: on the first `list()` call, `GitCatalog` reads the directory tree and parses all YAML files into entity objects. After that, it also registers `gitClient.onFileChange` and reloads entities when matching files change on the configured branch. **Mutation flow**: ``` create / update / delete → render CR YAML → gitClient.createFile / updateFile / deleteFile (commits to branch) → gitClient.createPR(title, branch) → if PR is auto-merged: update in-memory list immediately ``` For the `local` persistence type, `createPR` returns a synthetic merged PR, so the in-memory list is always updated immediately. **ID generation**: `create()` derives the resource ID from the entity name via `slugify()` (lowercase, spaces → hyphens, non-alphanumeric stripped). **Branch naming**: `feat/create-entity-{name}`, `feat/update-entity-{id}`, `feat/delete-entity-{id}`. ## Git Client `GitClient` is a provider-agnostic interface with operations for reading and writing files, traversing directory trees, listing and creating pull requests, and listing commits. The normalized PR and commit types carry only the fields needed by the catalog. ### LocalClient Used when `workspace.persistence.type === 'local'`. Reads and writes files directly on the local filesystem using `isomorphic-git` for commit tracking. - All write operations are serialized through a promise queue to prevent concurrent commit conflicts. - `branch` parameter is ignored; all operations target the working tree. - `listPRs` returns `[]`; `createPR` returns a synthetic already-merged PR so catalog mutations always take the auto-merge path. - Auto-initializes the git repository if `.git` does not exist. ### GitHubClient Used when `workspace.persistence.type === 'git'`. Auth via `Authorization: Bearer `. Supports GitHub Enterprise via optional `baseUrl`. Uses `BranchCache` for all read operations. ## Branch Snapshot Cache `BranchCache` (used by `GitHubClient`) avoids per-file HTTP round-trips by downloading the entire repository as a gzip-compressed tar archive in a single request, parsing it in memory, and serving subsequent reads from the snapshot. **Refresh strategy**: stale-while-revalidate (TTL: 60 s). A background refresh is triggered on the first access after the TTL elapses or after explicit `invalidate()`. Concurrent accesses during a refresh share the same in-flight promise and see the previous snapshot until the refresh completes. **Invalidation** happens automatically when any mutation completes (invalidate after write), and lazily when the TTL elapses on the next read.