Authoring concepts
The knowledge base is the product's moat. Authoring is intentionally careful — every published concept becomes regulatory advice your end-users may act on.
The review gate
Every concept moves through four statuses:
draft— being written. Invisible to live resolution.review— submitted for a domain expert's review. Still invisible.published— domain expert (named inreviewedBy) signed off. Visible to live resolution.deprecated— no longer accurate. Invisible.
The resolver in apps/knowledge-api/src/resolver.ts filters by status = 'published'. There is no override flag. This is intentional — it forces every piece of regulatory text in front of users to have a named human owner.
Anatomy of a concept entry
# knowledge-base/concepts/aml-kyc/ubo-threshold.yaml
conceptId: ubo-threshold
canonicalLabel: Ultimate Beneficial Owner (UBO) threshold
domain: compliance-aml-kyc
definition: "Plain-language definition — what this field captures."
regulatoryOrigin: "AMLD5 Article 30 + FATF Recommendation 10"
riskLevel: high # low | medium | high | critical
riskConsequence: "What goes wrong if the user gets this wrong."
sources: ["amld5", "fatf-r10", "cssf-12-552"]
jurisdictionVariants:
- jurisdiction: LU
definitionOverride: "Override for Luxembourg specifically."
regulatoryOriginOverride: "Loi du 13 janvier 2019..."
jurisdictionNote: "RBE registration mandatory within 30 days."
sources: ["amld5", "cssf-12-552"]
roleGuidance:
- role: kyc-analyst
guidance: "What an analyst should do."
permissionNote: "Can flag pending; cannot mark verified."
- role: compliance-officer
guidance: "What the compliance officer reviews."
workflowHints:
- appId: elyxen
module: kyc
submodule: onboarding
step: "UBO identification"
nextStep: "Source of funds verification"
version: 1
status: draft
reviewedBy: null
lastReviewedAt: nullEvery sources entry must reference an existing source file under knowledge-base/sources/<jurisdiction>/<id>.yaml. The grounding validator (in the AI Content Generator) rejects any output that cites a source not in this list.
Field aliases — wiring concepts to host-app fields
Each host app gets one alias file under knowledge-base/aliases/<appId>.yaml. This is what tells the Knowledge Engine's lookup cascade how to recognise a field on this app:
# knowledge-base/aliases/elyxen.yaml
- conceptId: ubo-threshold
appId: elyxen
module: kyc
matchers:
dataConceptId: ["ubo-threshold", "ubo"]
fieldId: ["ubo-percentage", "ubo_percent", "beneficial_owner_pct"]
ariaLabel: ["UBO percentage", "Beneficial owner percentage"]
fuzzyLabel: ["beneficial owner percentage", "ultimate beneficial owner"]The resolver tries matchers in this order: dataConceptId (confidence 1.0) → fieldId (0.9) → ariaLabel (0.85) → fuzzy with Jaccard ≥ 0.5 (0.55–0.75). First strong match wins.
The load workflow
# 1. Validate every YAML file against the zod schema.
# Refuses to write anything to the DB if any file is malformed.
pnpm --filter @help-layer/knowledge-api kb:validate knowledge-base
# 2. Upsert into Postgres (transactional per concept).
pnpm --filter @help-layer/knowledge-api kb:load knowledge-base v0.2.0
# 3. Confirm the load:
curl -s http://localhost:3027/knowledge/v1/kb-version
# → {"version":"v0.2.0","concept_count":N,"loaded_at":"..."}Every load creates a new row in kb_versions. Roll-forwards only — to roll back, re-load the previous YAML state with the previous version string.
Promotion checklist (the human gate)
- The definition is regulator-accurate, not LLM-paraphrased.
- The risk consequence is specific (e.g. "up to EUR 5M fine"), not generic ("may cause issues").
- Sources cite the actual regulation, not a secondary source.
- Jurisdiction variants exist for every jurisdiction the host apps target.
- Role guidance is differentiated — analysts should see different advice than officers.
- The reviewer is named and dated. This is the audit trail.
When all six are satisfied, change status: draft → status: published in the YAML, set reviewedBy + lastReviewedAt, and re-run kb:load.
status: draft in the YAML files. The running demo promoted them to published in the live DB only — for testing. See glossary for full definitions.