Address PR #222 re-review: fix source-uniqueness detection + coverage/cleanups
MUST-FIX - isSourceUniqueViolation read the wrong error field: kysely-postgres-js (postgres@3.4.8) puts the violated constraint on `constraint_name`, not node-postgres' `.constraint`, so a concurrent same-slug+language import's 23505 was never recognized as a source-collision and surfaced a false "name already exists" error. Now read `constraint_name` (with `.constraint` as a fallback for other drivers). Fix the faked test fixture (it built the error with the same wrong `.constraint` field, masking the bug): it now uses `constraint_name`, so the test genuinely exercises the skip path and FAILS against the unfixed code. - Extract the catalog modal's role-state computation into a pure `catalogRoleInstallState(role, workspaceRoles, language)` helper (mirrors role-launch.ts) and cover it with vitest: import / installed / update / same-slug-different-language. SUGGESTIONS - Restore IAiRoleUpdateFromCatalogResult as a discriminated union mirroring the server; narrow the consumer via `"reason" in result` (the boolean discriminant does not narrow under strictNullChecks:false). - README: add a "How it's served" section documenting AI_AGENT_ROLES_CATALOG_URL (remote http(s) base / local path / empty => in-repo folder). - check.mjs: drop the redundant `const key = slug` alias. - Cover the reason->message mapping in useUpdateAiRoleFromCatalogMutation (4 branches) via renderHook with a mocked service. - Cover importFromCatalog "bundle not in index" => BadGateway. - Cover updateFromCatalog "slug in index but missing in bundle file" => not-in-catalog. ARCHITECTURE - Extract the shared catalog read prefix: a private `loadBundleById` (fetchIndex -> meta -> fetchBundle -> versionMap) reused by getCatalogBundle and importFromCatalog, and a `catalogRoleContentFields` mapper shared by the import insert and update patch. The three orchestrations and their distinct write paths stay separate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,25 @@ Currently shipped bundles:
|
||||
copy-editor, fact-checker, proofreader, narrator), languages `ru`, `en`.
|
||||
- `research` — a single `researcher` role, languages `ru`, `en`.
|
||||
|
||||
## How it's served
|
||||
|
||||
The server does not bundle this data; it reads it at request time from a single
|
||||
configured location, the `AI_AGENT_ROLES_CATALOG_URL` env var
|
||||
(`EnvironmentService.getAiAgentRolesCatalogSource()`). The value selects one of
|
||||
three sources:
|
||||
|
||||
- **`http(s)://…`** — a REMOTE base URL. The server fetches `<base>/index.json`
|
||||
for the manifest and `<base>/bundles/<bundle-id>/<lang>.json` for each opened
|
||||
bundle file (e.g. the raw GitHub base of the catalog repo in production).
|
||||
- **any other non-empty value** — a LOCAL filesystem directory; the same
|
||||
`index.json` / `bundles/<id>/<lang>.json` paths are read from disk.
|
||||
- **empty / unset** (the default) — the in-repo `agent-roles-catalog/` folder
|
||||
(this directory), i.e. local dev reads these files directly.
|
||||
|
||||
In every case the layout below is what the server expects, and the fetched JSON
|
||||
is re-validated server-side (the catalog is treated as untrusted input). See
|
||||
`.env.example` for the variable and the CHANGELOG for the rollout.
|
||||
|
||||
## `index.json` schema
|
||||
|
||||
```jsonc
|
||||
|
||||
Reference in New Issue
Block a user