Skip to main content

Catalog module

AI-powered product information management, semantic search, configurable taxonomy, supplier shortlisting. Absorbs the Stella Catalog feature set — taxonomy is configurable per tenant (UNSPSC, ETIM, NATO FSC, CPV, or custom).

  • Source: apps/catalog/
  • Schema: catalog
  • Project Tracker prefix: CAT-*
  • Hosting: sub-zone behind Directory; production basePath is /catalog/* (rewritten from the root zone).

1. Purpose

Catalog is the platform's product knowledge layer. It owns the catalogue entries (products, services, capabilities), the configurable industry taxonomy that classifies them, and the shortlisting / supplier-matching primitives that downstream procurement modules rely on. Two technical pillars set Catalog apart: pgvector for semantic-search embeddings and ltree for hierarchical taxonomy paths. AI-generated descriptions (via @constellation-platform/ai-core) are an explicit feature of the module rather than a layer concern.

2. Component diagram (C4 L3)

Call direction is strict: API Route → Tool → Service → Repository. Routes never call services or repositories directly; workflows and event handlers go through tools or services. See route-wrapping.

3. Schema

catalog — owned by Catalog. Postgres extensions required: pgvector (embeddings on catalog_entries), ltree (path-based taxonomy hierarchies), pg_trgm (fuzzy text matching). RLS is enforced on every tenant-scoped table via app.tenant_id. Cross-module reads of identity.* are read-only via raw SQL.

4. Entities

AggregatePurpose
catalog_entriesProducts / services / capabilities. Lifecycle: DRAFT → PUBLISHED → ARCHIVED. Carries pgvector embeddings and AI-generated descriptions.
catalog_taxonomyConfigurable taxonomy — LTREE path enables sub-tree queries. Per-tenant choice of UNSPSC / ETIM / NATO FSC / CPV / custom.
shortlistsCurated subsets of catalogue entries — feeds Procurement RFQ flows. Lifecycle: DRAFT → PUBLISHED → ARCHIVED.
shortlist_entriesMembership join between a shortlist and its catalogue entries.

5. Domain events

Published from src/server/events/catalog.events.ts via the transactional outbox under the catalog.* namespace. Full payload schemas are in @constellation/contracts and indexed in the Domain events index.

catalog.entry.created, catalog.entry.updated, catalog.entry.deleted, catalog.entry.status_changed, catalog.taxonomy.created, catalog.taxonomy.updated, catalog.taxonomy.moved, catalog.taxonomy.deleted, catalog.shortlist.created, catalog.shortlist.updated, catalog.shortlist.deleted, catalog.shortlist.archived, catalog.shortlist.entry_added, catalog.shortlist.entry_removed.

6. Public API

The Catalog API reference is not yet wired into this site — adding Zod→OpenAPI to apps/catalog/ is tracked under INF-26. Illustrative routes:

  • POST /api/catalog-entries — create a draft catalogue entry; AI description and embedding are generated asynchronously.
  • POST /api/search — semantic search across published entries (uses pgvector ANN over the embeddings column).
  • POST /api/shortlists — create a shortlist from search results or a manual selection.

7. Layers + call direction

LayerPathMay import from
API Routessrc/app/api/Tools only
Toolssrc/server/tools/Services, Policies, Events
Servicessrc/server/services/Repositories, Policies, @constellation-platform/{db,ai-core,ai-embeddings}
Repositoriessrc/server/repositories/@constellation-platform/db (Prisma + raw SQL for ltree / vector ops)
Policiessrc/server/policies/@constellation-platform/auth-core
Workflowssrc/server/workflows/Tools or Services (never repositories)
Eventssrc/server/events/@constellation-platform/events publish() + outbox

Enforced at PR time by scripts/check-route-wrapping.ts — invoked via npm run check:routes, which scans apps/catalog/src/app/api and apps/directory/src/app/api. Every withAuth must be paired with withTenantAuth. Plus ESLint import boundaries.

8. Code entry points

9. Known exceptions / pitfalls

  • pgvector and ltree are required. Local dev brings them up via scripts/db-init.sql — verify with \dx in psql before running migrations.
  • AI calls always go through @constellation-platform/ai-core. Don't import provider SDKs (@anthropic-ai/sdk, openai) directly anywhere outside the platform packages — that's how the budget and audit interceptors stay in the request path. See provider-abstraction.
  • Embeddings are generated lazily. A new entry doesn't get an embedding inside the create transaction — there's a workflow that picks up entries with embedding IS NULL and fills them in. Search for an entry that's seconds-old returns no result; that's expected.
  • Optional clearance / classification gating. Tenants that need defence-style access control enable per-entry classification + per-user clearance. Off by default; switching it on changes the search filter shape — see the catalog spec.
  • Cross-module reads are read-only via raw SQL. Don't define Prisma cross-schema relations to identity.* — use @constellation-platform/db raw queries.

See also