diff --git a/multilingual-entity-alias-guard/README.md b/multilingual-entity-alias-guard/README.md
new file mode 100644
index 00000000..705c4302
--- /dev/null
+++ b/multilingual-entity-alias-guard/README.md
@@ -0,0 +1,27 @@
+# Multilingual Entity Alias Guard
+
+This module adds a focused Scientific Knowledge Graph Integration slice for SCIBASE issue #17. It normalizes multilingual scientific mentions before they become graph nodes, entity-page aliases, or recommendation signals.
+
+The guard accepts trusted translated aliases only when numeric confidence evidence is present, preserves original language tags, normalizes language-tag casing and underscore or hyphen regional separators for lookup, falls back from regional language tags to their base language, emits JSON-LD-style entity packets, holds homographs, false friends, same-language alias collisions, extractor-candidate/alias conflicts, malformed mention text, and mixed-script Latin-language lookalikes including lowercase Greek or Cyrillic confusables for curator review, suppresses low-confidence or missing-confidence aliases before recommendations are shown, and treats omitted or malformed localized names, mentions, or homograph policies as sparse graph evidence instead of crashing corpus review.
+
+## Run
+
+```bash
+npm test
+npm run demo
+npm run video
+npm run check
+```
+
+## Outputs
+
+- `reports/alias-guard-packet.json`
+- `reports/sparse-alias-guard-packet.json`
+- `reports/candidate-alias-conflict-packet.json`
+- `reports/malformed-mention-text-packet.json`
+- `reports/malformed-alias-evidence-packet.json`
+- `reports/alias-guard-report.md`
+- `reports/summary.svg`
+- `reports/demo.mp4`
+
+All data is synthetic. The module does not call live ontologies, identity providers, external APIs, private corpora, search indexes, or recommendation systems.
diff --git a/multilingual-entity-alias-guard/acceptance-notes.md b/multilingual-entity-alias-guard/acceptance-notes.md
new file mode 100644
index 00000000..5a825aac
--- /dev/null
+++ b/multilingual-entity-alias-guard/acceptance-notes.md
@@ -0,0 +1,28 @@
+# Acceptance Notes
+
+This #17 slice focuses specifically on multilingual scientific alias quality before graph nodes and recommendations are produced.
+
+It is not:
+
+- a broad entity extractor or navigator
+- an ontology deprecation or synonym migration tool
+- a recommendation visibility or diversity guard
+- a geospatial, clinical trial, biological accession, software runtime, or temporal validity guard
+
+Validation coverage:
+
+- trusted CRISPR aliases in English, German, and Spanish map to one canonical MeSH entity
+- Spanish `control` is held as a homograph/false friend instead of silently creating a statistical control-group edge
+- same-language translated alias collisions are held instead of silently attaching a mention to the wrong canonical entity
+- extractor candidate IDs that disagree with multilingual alias lookup are held instead of silently overriding either signal
+- language-tag case differences do not suppress trusted translated aliases
+- regional language tags such as `es-MX` use base-language alias and homograph policy while preserving the original tag
+- underscore regional language tags such as `es_MX` use the same base-language alias and homograph policy while preserving the original tag
+- mixed-script Latin-language aliases such as Cyrillic-lookalike `CRISPR` text or lowercase Greek-alpha `CRISPR-Cαs9` text are held for curator review instead of becoming quiet unknowns
+- low-confidence French alias output is suppressed from recommendations
+- missing or non-numeric confidence evidence is suppressed before graph recommendations
+- sparse ontology/corpus exports with omitted localized names, mention lists, or homograph policies do not crash corpus review
+- malformed localized-name entries are omitted from alias lookup and JSON-LD alternate names, with alias evidence issues preserved for review
+- malformed mention text values are held for curator review instead of crashing alias normalization or reaching recommendation-safe IDs
+- localized names remain language-tagged on entity packets
+- audit output is deterministic and private-data free
diff --git a/multilingual-entity-alias-guard/demo.js b/multilingual-entity-alias-guard/demo.js
new file mode 100644
index 00000000..e479303d
--- /dev/null
+++ b/multilingual-entity-alias-guard/demo.js
@@ -0,0 +1,172 @@
+const fs = require('fs');
+const path = require('path');
+const { evaluateAliasGuard, buildSampleCorpus } = require('./index');
+
+const reportsDir = path.join(__dirname, 'reports');
+fs.mkdirSync(reportsDir, { recursive: true });
+
+const result = evaluateAliasGuard(buildSampleCorpus());
+const sparseResult = evaluateAliasGuard({
+ corpusId: 'kg-sparse-ontology-export-17',
+ generatedAt: '2026-05-30T12:00:00Z',
+ entities: [
+ {
+ id: 'entity:mesh:D012345',
+ canonicalName: 'Sparse Ontology Entity',
+ ontology: 'MeSH',
+ identifier: 'D012345'
+ }
+ ]
+});
+const conflictResult = evaluateAliasGuard({
+ ...buildSampleCorpus(),
+ corpusId: 'kg-candidate-alias-conflict-17',
+ generatedAt: '2026-05-30T12:30:00Z',
+ mentions: [
+ {
+ id: 'mention-diabetes-conflicting-candidate',
+ documentId: 'paper-17',
+ text: 'diabetes mellitus',
+ language: 'es',
+ confidence: 0.93,
+ candidateEntityId: 'entity:stat:control-group'
+ }
+ ]
+});
+const malformedMentionResult = evaluateAliasGuard({
+ ...buildSampleCorpus(),
+ corpusId: 'kg-malformed-mention-text-17',
+ generatedAt: '2026-05-31T10:45:00Z',
+ mentions: [
+ {
+ id: 'mention-malformed-text',
+ documentId: 'paper-18',
+ text: { value: 'diabetes mellitus' },
+ language: 'es',
+ confidence: 0.94,
+ candidateEntityId: 'entity:mesh:D003920'
+ }
+ ]
+});
+const malformedAliasEvidenceResult = evaluateAliasGuard({
+ ...buildSampleCorpus(),
+ corpusId: 'kg-malformed-localized-name-17',
+ generatedAt: '2026-05-31T10:46:00Z',
+ entities: [
+ {
+ id: 'entity:mesh:D003920',
+ canonicalName: 'Diabetes Mellitus',
+ ontology: 'MeSH',
+ identifier: 'D003920',
+ localizedNames: {
+ es: ['diabetes mellitus', { value: 'diabete mellitus' }]
+ }
+ }
+ ],
+ mentions: [
+ {
+ id: 'mention-diabetes-es',
+ documentId: 'paper-19',
+ text: 'diabetes mellitus',
+ language: 'es',
+ confidence: 0.94
+ }
+ ]
+});
+
+const packetPath = path.join(reportsDir, 'alias-guard-packet.json');
+const sparsePacketPath = path.join(reportsDir, 'sparse-alias-guard-packet.json');
+const conflictPacketPath = path.join(reportsDir, 'candidate-alias-conflict-packet.json');
+const malformedMentionPacketPath = path.join(reportsDir, 'malformed-mention-text-packet.json');
+const malformedAliasEvidencePacketPath = path.join(reportsDir, 'malformed-alias-evidence-packet.json');
+const reportPath = path.join(reportsDir, 'alias-guard-report.md');
+const svgPath = path.join(reportsDir, 'summary.svg');
+
+fs.writeFileSync(packetPath, `${JSON.stringify(result, null, 2)}\n`);
+fs.writeFileSync(sparsePacketPath, `${JSON.stringify(sparseResult, null, 2)}\n`);
+fs.writeFileSync(conflictPacketPath, `${JSON.stringify(conflictResult, null, 2)}\n`);
+fs.writeFileSync(malformedMentionPacketPath, `${JSON.stringify(malformedMentionResult, null, 2)}\n`);
+fs.writeFileSync(malformedAliasEvidencePacketPath, `${JSON.stringify(malformedAliasEvidenceResult, null, 2)}\n`);
+
+const accepted = result.mentionDecisions
+ .filter((decision) => decision.decision === 'accept-canonical-entity')
+ .map((decision) => `- ${decision.id}: ${decision.text} (${decision.language}) -> ${decision.candidateEntityId}`)
+ .join('\n');
+
+const held = result.curatorActions
+ .map((action) => `- ${action.id}: ${action.action} (${action.language}:${action.text})`)
+ .join('\n');
+
+const markdown = `# Multilingual Entity Alias Guard
+
+Corpus: ${result.corpusId}
+Generated: ${result.generatedAt}
+
+## Summary
+
+- Accepted mentions: ${result.summary.acceptedMentions}
+- Held curator-review mentions: ${result.summary.heldMentions}
+- Suppressed low-confidence mentions: ${result.summary.suppressedMentions}
+- Entity packets emitted: ${result.summary.entityPackets}
+- Audit digest: ${result.auditDigest}
+
+## Accepted Canonical Mappings
+
+${accepted}
+
+## Curator Actions
+
+${held}
+
+## Recommendation Guard
+
+Held or suppressed mentions are not allowed to drive entity-page recommendations until a curator verifies the alias mapping.
+
+## Sparse Corpus Guard
+
+Sparse ontology or corpus exports that omit localized names, mention lists, or homograph policy still produce deterministic graph review evidence. The sparse fixture emitted ${sparseResult.summary.entityPackets} entity packet and ${sparseResult.mentionDecisions.length} mention decisions.
+
+## Candidate Alias Conflict Guard
+
+Extractor candidates that disagree with trusted multilingual alias lookup are held for curator review instead of silently overriding the upstream candidate. The conflict fixture decision is ${conflictResult.mentionDecisions[0].decision} with reason ${conflictResult.mentionDecisions[0].reason}.
+
+## Malformed Mention Text Guard
+
+Malformed mention text values are held for curator review instead of crashing alias normalization. The malformed fixture decision is ${malformedMentionResult.mentionDecisions[0].decision} with reason ${malformedMentionResult.mentionDecisions[0].reason}, and it emits ${malformedMentionResult.curatorActions[0].action}.
+
+## Malformed Alias Evidence Guard
+
+Malformed localized-name evidence is omitted from alias lookup and JSON-LD alternate names instead of crashing ontology review. The malformed alias fixture records ${malformedAliasEvidenceResult.entityPackets[0].aliasEvidenceIssues.length} alias evidence issue with reason ${malformedAliasEvidenceResult.entityPackets[0].aliasEvidenceIssues[0].reason}.
+
+## Safety
+
+All fixtures are synthetic. The module does not call live ontologies, identity providers, external APIs, private corpora, search indexes, or recommendation systems.
+`;
+
+fs.writeFileSync(reportPath, markdown);
+
+const svg = `
+`;
+
+fs.writeFileSync(svgPath, svg);
+
+console.log(`Wrote ${path.relative(__dirname, packetPath)}`);
+console.log(`Wrote ${path.relative(__dirname, sparsePacketPath)}`);
+console.log(`Wrote ${path.relative(__dirname, conflictPacketPath)}`);
+console.log(`Wrote ${path.relative(__dirname, malformedMentionPacketPath)}`);
+console.log(`Wrote ${path.relative(__dirname, malformedAliasEvidencePacketPath)}`);
+console.log(`Wrote ${path.relative(__dirname, reportPath)}`);
+console.log(`Wrote ${path.relative(__dirname, svgPath)}`);
+console.log(`Accepted mentions: ${result.summary.acceptedMentions}`);
+console.log(`Suppressed mentions: ${result.summary.suppressedMentions}`);
diff --git a/multilingual-entity-alias-guard/index.js b/multilingual-entity-alias-guard/index.js
new file mode 100644
index 00000000..b5646fab
--- /dev/null
+++ b/multilingual-entity-alias-guard/index.js
@@ -0,0 +1,559 @@
+const crypto = require('crypto');
+
+function stableStringify(value) {
+ if (Array.isArray(value)) {
+ return `[${value.map(stableStringify).join(',')}]`;
+ }
+
+ if (value && typeof value === 'object') {
+ return `{${Object.keys(value)
+ .sort()
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
+ .join(',')}}`;
+ }
+
+ return JSON.stringify(value);
+}
+
+function digest(value) {
+ return `sha256:${crypto.createHash('sha256').update(stableStringify(value)).digest('hex')}`;
+}
+
+function evidenceList(value) {
+ return Array.isArray(value) ? value : [];
+}
+
+function valueType(value) {
+ return value === null ? 'null' : Array.isArray(value) ? 'array' : typeof value;
+}
+
+function localizedNamesFor(entity) {
+ const localizedNames =
+ entity.localizedNames && typeof entity.localizedNames === 'object' ? entity.localizedNames : {};
+ return Object.fromEntries(
+ Object.entries(localizedNames)
+ .map(([language, terms]) => [language, evidenceList(terms).filter(isTextValue)])
+ .filter(([, terms]) => terms.length > 0)
+ );
+}
+
+function localizedNameIssuesFor(entity) {
+ const localizedNames =
+ entity.localizedNames && typeof entity.localizedNames === 'object' ? entity.localizedNames : {};
+ const issues = [];
+
+ for (const [language, terms] of Object.entries(localizedNames)) {
+ if (!Array.isArray(terms)) {
+ issues.push({
+ language,
+ reason: 'malformed-localized-name-list',
+ valueType: valueType(terms)
+ });
+ continue;
+ }
+
+ for (const term of terms) {
+ if (!isTextValue(term)) {
+ issues.push({
+ language,
+ reason: 'malformed-localized-name',
+ valueType: valueType(term)
+ });
+ }
+ }
+ }
+
+ return issues;
+}
+
+function evidenceObject(value) {
+ return value && typeof value === 'object' ? value : {};
+}
+
+const LATIN_SCRIPT_LANGUAGES = new Set([
+ 'ca',
+ 'cs',
+ 'da',
+ 'de',
+ 'en',
+ 'es',
+ 'fi',
+ 'fr',
+ 'hr',
+ 'hu',
+ 'id',
+ 'it',
+ 'nl',
+ 'no',
+ 'pl',
+ 'pt',
+ 'ro',
+ 'sk',
+ 'sl',
+ 'sv',
+ 'tr',
+ 'vi'
+]);
+
+const LATIN_LETTER_RE = /[A-Za-z\u00C0-\u024F]/u;
+const CYRILLIC_LATIN_CONFUSABLE_RE = /[\u0405\u0410\u0412\u0415\u041A\u041C\u041D\u041E\u0420\u0421\u0422\u0425\u0430\u0435\u043E\u0440\u0441\u0445\u0455]/u;
+const GREEK_LATIN_CONFUSABLE_RE = /[\u0391\u0392\u0395\u0396\u0397\u0399\u039A\u039C\u039D\u039F\u03A1\u03A4\u03A5\u03A7\u03B1\u03B5\u03B9\u03BA\u03BC\u03BD\u03BF\u03C1\u03C4\u03C5\u03C7]/u;
+
+function normalizeTerm(term) {
+ return term.normalize('NFKC').trim().replace(/\s+/g, ' ').toLocaleLowerCase();
+}
+
+function isTextValue(value) {
+ return typeof value === 'string';
+}
+
+function normalizeLanguageTag(language) {
+ return String(language || '').normalize('NFKC').trim().replace(/_/g, '-').toLocaleLowerCase();
+}
+
+function languageLookupKeys(language) {
+ const normalized = normalizeLanguageTag(language);
+ if (!normalized) return [''];
+
+ const primary = normalized.split('-')[0];
+ return primary && primary !== normalized ? [normalized, primary] : [normalized];
+}
+
+function primaryLanguage(language) {
+ return normalizeLanguageTag(language).split('-')[0] || '';
+}
+
+function hasMixedScriptConfusableRisk(mention) {
+ const primary = primaryLanguage(mention.language);
+ if (!LATIN_SCRIPT_LANGUAGES.has(primary)) {
+ return false;
+ }
+
+ const text = String(mention.text || '').normalize('NFKC');
+ return (
+ LATIN_LETTER_RE.test(text) &&
+ (CYRILLIC_LATIN_CONFUSABLE_RE.test(text) || GREEK_LATIN_CONFUSABLE_RE.test(text))
+ );
+}
+
+function confidenceScore(value) {
+ const score = Number(value);
+ return Number.isFinite(score) ? score : null;
+}
+
+function buildAliasIndex(entities) {
+ const index = new Map();
+
+ for (const entity of evidenceList(entities)) {
+ for (const [language, terms] of Object.entries(localizedNamesFor(entity))) {
+ for (const term of evidenceList(terms)) {
+ const key = `${normalizeLanguageTag(language)}:${normalizeTerm(term)}`;
+ const existing = index.get(key);
+
+ if (!existing) {
+ index.set(key, {
+ kind: 'alias',
+ entity,
+ language,
+ term
+ });
+ continue;
+ }
+
+ const entitiesForAlias =
+ existing.kind === 'collision' ? existing.entities.slice() : [existing.entity];
+
+ if (entitiesForAlias.some((candidate) => candidate.id === entity.id)) {
+ continue;
+ }
+
+ entitiesForAlias.push(entity);
+ index.set(key, {
+ kind: 'collision',
+ entities: entitiesForAlias,
+ entityIds: entitiesForAlias.map((candidate) => candidate.id).sort(),
+ language,
+ term
+ });
+ }
+ }
+ }
+
+ return index;
+}
+
+function mentionDecision(mention, aliasIndex, homographs) {
+ const languageKeys = languageLookupKeys(mention.language);
+ if (!isTextValue(mention.text)) {
+ const candidateEntityId = mention.candidateEntityId || null;
+ return {
+ id: mention.id,
+ language: mention.language,
+ text: mention.text,
+ documentId: mention.documentId,
+ decision: 'hold-for-curator-review',
+ reason: 'malformed-mention-text',
+ candidateEntityId,
+ candidateEntityIds: candidateEntityId ? [candidateEntityId] : [],
+ confidence: mention.confidence,
+ preservedLanguageTag: mention.language
+ };
+ }
+
+ const termKey = normalizeTerm(mention.text);
+ const aliasEntry = languageKeys
+ .map((languageKey) => aliasIndex.get(`${languageKey}:${termKey}`))
+ .find(Boolean);
+ const alias = aliasEntry && aliasEntry.kind === 'alias' ? aliasEntry : null;
+ const candidateEntityId = alias ? alias.entity.id : mention.candidateEntityId || null;
+ const homographEntry = languageKeys
+ .map((languageKey) => homographs[`${languageKey}:${termKey}`])
+ .find(Boolean);
+ const confidence = confidenceScore(mention.confidence);
+
+ if (hasMixedScriptConfusableRisk(mention)) {
+ return {
+ id: mention.id,
+ language: mention.language,
+ text: mention.text,
+ documentId: mention.documentId,
+ decision: 'hold-for-curator-review',
+ reason: 'script-confusable-alias',
+ candidateEntityId,
+ candidateEntityIds: candidateEntityId ? [candidateEntityId] : [],
+ confidence: mention.confidence,
+ preservedLanguageTag: mention.language
+ };
+ }
+
+ if (homographEntry) {
+ return {
+ id: mention.id,
+ language: mention.language,
+ text: mention.text,
+ documentId: mention.documentId,
+ decision: 'hold-for-curator-review',
+ reason: 'false-friend-or-homograph',
+ candidateEntityId,
+ candidateEntityIds: candidateEntityId ? [candidateEntityId] : [],
+ confidence: mention.confidence,
+ preservedLanguageTag: mention.language
+ };
+ }
+
+ if (aliasEntry && aliasEntry.kind === 'collision') {
+ return {
+ id: mention.id,
+ language: mention.language,
+ text: mention.text,
+ documentId: mention.documentId,
+ decision: 'hold-for-curator-review',
+ reason: 'alias-collision',
+ candidateEntityId: null,
+ candidateEntityIds: aliasEntry.entityIds,
+ confidence: mention.confidence,
+ preservedLanguageTag: mention.language
+ };
+ }
+
+ if (alias && mention.candidateEntityId && mention.candidateEntityId !== alias.entity.id) {
+ return {
+ id: mention.id,
+ language: mention.language,
+ text: mention.text,
+ documentId: mention.documentId,
+ decision: 'hold-for-curator-review',
+ reason: 'candidate-alias-conflict',
+ candidateEntityId: null,
+ candidateEntityIds: [alias.entity.id, mention.candidateEntityId].sort(),
+ confidence: mention.confidence,
+ preservedLanguageTag: mention.language
+ };
+ }
+
+ if (!alias || confidence === null || confidence < 0.8) {
+ return {
+ id: mention.id,
+ language: mention.language,
+ text: mention.text,
+ documentId: mention.documentId,
+ decision: 'suppress-recommendation',
+ reason: alias || candidateEntityId ? 'low-confidence-alias' : 'unknown-alias',
+ candidateEntityId,
+ candidateEntityIds: candidateEntityId ? [candidateEntityId] : [],
+ confidence: mention.confidence,
+ preservedLanguageTag: mention.language
+ };
+ }
+
+ return {
+ id: mention.id,
+ language: mention.language,
+ text: mention.text,
+ documentId: mention.documentId,
+ decision: 'accept-canonical-entity',
+ reason: 'trusted-translated-alias',
+ candidateEntityId: alias.entity.id,
+ candidateEntityIds: [alias.entity.id],
+ confidence: mention.confidence,
+ preservedLanguageTag: mention.language
+ };
+}
+
+function curatorActionForDecision(decision) {
+ if (decision.decision === 'accept-canonical-entity') {
+ return null;
+ }
+
+ return {
+ id: `curate-${decision.id}`,
+ mentionId: decision.id,
+ action:
+ decision.reason === 'alias-collision'
+ ? 'review-multilingual-alias-collision'
+ : decision.reason === 'candidate-alias-conflict'
+ ? 'review-multilingual-candidate-alias-conflict'
+ : decision.reason === 'script-confusable-alias'
+ ? 'review-multilingual-script-confusable'
+ : decision.reason === 'malformed-mention-text'
+ ? 'review-multilingual-malformed-mention'
+ : decision.reason === 'false-friend-or-homograph'
+ ? 'review-multilingual-homograph'
+ : 'verify-translated-alias-before-recommendation',
+ priority:
+ decision.reason === 'false-friend-or-homograph' ||
+ decision.reason === 'alias-collision' ||
+ decision.reason === 'candidate-alias-conflict' ||
+ decision.reason === 'script-confusable-alias' ||
+ decision.reason === 'malformed-mention-text'
+ ? 'high'
+ : 'normal',
+ language: decision.language,
+ text: decision.text,
+ candidateEntityId: decision.candidateEntityId,
+ candidateEntityIds: decision.candidateEntityIds,
+ reason: decision.reason
+ };
+}
+
+function buildEntityPackets(entities, decisions) {
+ return evidenceList(entities).map((entity) => {
+ const localizedNames = localizedNamesFor(entity);
+ const aliasEvidenceIssues = localizedNameIssuesFor(entity);
+ const accepted = decisions.filter(
+ (decision) =>
+ decision.decision === 'accept-canonical-entity' && decision.candidateEntityId === entity.id
+ );
+ const languages = Array.from(new Set(accepted.map((decision) => decision.language))).sort();
+
+ return {
+ id: entity.id,
+ canonicalName: entity.canonicalName,
+ ontology: entity.ontology,
+ identifier: entity.identifier,
+ languages,
+ aliasEvidenceIssues,
+ mentions: accepted.map((decision) => ({
+ id: decision.id,
+ text: decision.text,
+ language: decision.language,
+ documentId: decision.documentId,
+ confidence: decision.confidence
+ })),
+ localizedNames,
+ jsonLd: {
+ '@context': 'https://schema.org',
+ '@type': 'DefinedTerm',
+ name: entity.canonicalName,
+ identifier: `${entity.ontology}:${entity.identifier}`,
+ inDefinedTermSet: entity.ontology,
+ alternateName: Object.values(localizedNames).flat()
+ },
+ schemaOrg: {
+ '@type': 'ScholarlyArticle',
+ about: accepted.map((decision) => ({
+ '@type': 'DefinedTerm',
+ name: decision.text,
+ inLanguage: decision.language,
+ identifier: `${entity.ontology}:${entity.identifier}`
+ }))
+ }
+ };
+ });
+}
+
+function evaluateAliasGuard(corpus) {
+ const aliasIndex = buildAliasIndex(corpus.entities);
+ const mentionDecisions = evidenceList(corpus.mentions).map((mention) =>
+ mentionDecision(mention, aliasIndex, evidenceObject(corpus.homographs))
+ );
+ const curatorActions = mentionDecisions.map(curatorActionForDecision).filter(Boolean);
+ const entityPackets = buildEntityPackets(corpus.entities, mentionDecisions);
+ const suppressedMentionIds = mentionDecisions
+ .filter((decision) => decision.decision !== 'accept-canonical-entity')
+ .map((decision) => decision.id);
+
+ const summary = {
+ acceptedMentions: mentionDecisions.filter(
+ (decision) => decision.decision === 'accept-canonical-entity'
+ ).length,
+ heldMentions: mentionDecisions.filter(
+ (decision) => decision.decision === 'hold-for-curator-review'
+ ).length,
+ suppressedMentions: mentionDecisions.filter(
+ (decision) => decision.decision === 'suppress-recommendation'
+ ).length,
+ entityPackets: entityPackets.length
+ };
+
+ return {
+ corpusId: corpus.corpusId,
+ generatedAt: corpus.generatedAt,
+ mentionDecisions,
+ entityPackets,
+ curatorActions,
+ recommendationGuards: {
+ suppressedMentionIds,
+ safeEntityIds: entityPackets
+ .filter((packet) => packet.mentions.length > 0)
+ .map((packet) => packet.id)
+ },
+ summary,
+ auditDigest: digest({
+ corpusId: corpus.corpusId,
+ mentionDecisions,
+ entityPackets,
+ curatorActions,
+ summary
+ })
+ };
+}
+
+function buildSampleCorpus() {
+ return {
+ corpusId: 'kg-multilingual-upload-batch-17',
+ generatedAt: '2026-05-28T07:00:00Z',
+ entities: [
+ {
+ id: 'entity:mesh:D000077768',
+ canonicalName: 'CRISPR-Cas9',
+ ontology: 'MeSH',
+ identifier: 'D000077768',
+ localizedNames: {
+ en: ['CRISPR-Cas9'],
+ de: ['CRISPR-Cas9 Geneditierung'],
+ es: ['edicion genetica CRISPR-Cas9']
+ }
+ },
+ {
+ id: 'entity:mesh:D003920',
+ canonicalName: 'Diabetes Mellitus',
+ ontology: 'MeSH',
+ identifier: 'D003920',
+ localizedNames: {
+ en: ['diabetes mellitus'],
+ de: ['Diabetes mellitus'],
+ es: ['diabetes mellitus']
+ }
+ },
+ {
+ id: 'entity:stat:control-group',
+ canonicalName: 'Control Group',
+ ontology: 'SCIBASE-STAT',
+ identifier: 'control-group',
+ localizedNames: {
+ en: ['control group'],
+ es: ['grupo control'],
+ de: ['Kontrollgruppe']
+ }
+ }
+ ],
+ homographs: {
+ 'es:control': {
+ note: 'Spanish control may refer to monitoring or governance, not necessarily a statistical control group.'
+ }
+ },
+ mentions: [
+ {
+ id: 'mention-crispr-en',
+ documentId: 'paper-1',
+ text: 'CRISPR-Cas9',
+ language: 'en',
+ confidence: 0.97
+ },
+ {
+ id: 'mention-crispr-de',
+ documentId: 'paper-2',
+ text: 'CRISPR-Cas9 Geneditierung',
+ language: 'de',
+ confidence: 0.91
+ },
+ {
+ id: 'mention-crispr-es',
+ documentId: 'paper-3',
+ text: 'edicion genetica CRISPR-Cas9',
+ language: 'es',
+ confidence: 0.89
+ },
+ {
+ id: 'mention-diabetes-en',
+ documentId: 'paper-4',
+ text: 'diabetes mellitus',
+ language: 'en',
+ confidence: 0.94
+ },
+ {
+ id: 'mention-diabetes-de',
+ documentId: 'paper-5',
+ text: 'Diabetes mellitus',
+ language: 'de',
+ confidence: 0.95
+ },
+ {
+ id: 'mention-diabetes-es',
+ documentId: 'paper-6',
+ text: 'diabetes mellitus',
+ language: 'es',
+ confidence: 0.93
+ },
+ {
+ id: 'mention-control-es',
+ documentId: 'paper-7',
+ text: 'control',
+ language: 'es',
+ confidence: 0.88,
+ candidateEntityId: 'entity:stat:control-group'
+ },
+ {
+ id: 'mention-cellule-fr',
+ documentId: 'paper-8',
+ text: 'cellule',
+ language: 'fr',
+ confidence: 0.61,
+ candidateEntityId: 'entity:mesh:D002477'
+ },
+ {
+ id: 'mention-crispr-cyrillic-spoof',
+ documentId: 'paper-9',
+ text: '\u0421RISPR-Cas9',
+ language: 'en',
+ confidence: 0.97,
+ candidateEntityId: 'entity:mesh:D000077768'
+ },
+ {
+ id: 'mention-crispr-greek-alpha-spoof',
+ documentId: 'paper-10',
+ text: 'CRISPR-C\u03B1s9',
+ language: 'en',
+ confidence: 0.97,
+ candidateEntityId: 'entity:mesh:D000077768'
+ }
+ ]
+ };
+}
+
+module.exports = {
+ evaluateAliasGuard,
+ buildSampleCorpus,
+ digest
+};
diff --git a/multilingual-entity-alias-guard/make-demo-video.py b/multilingual-entity-alias-guard/make-demo-video.py
new file mode 100644
index 00000000..b6ffb874
--- /dev/null
+++ b/multilingual-entity-alias-guard/make-demo-video.py
@@ -0,0 +1,42 @@
+import subprocess
+from pathlib import Path
+
+
+ROOT = Path(__file__).resolve().parent
+REPORTS = ROOT / "reports"
+REPORTS.mkdir(exist_ok=True)
+OUTPUT = REPORTS / "demo.mp4"
+
+font = "C\\:/Windows/Fonts/arial.ttf"
+vf = ",".join(
+ [
+ "drawbox=x=52:y=58:w=1176:h=604:color=0x7bd88f@0.55:t=4",
+ "drawbox=x=62:y=68:w=1156:h=584:color=0x142f42@0.96:t=fill",
+ f"drawtext=fontfile='{font}':text='Scientific Knowledge Graph Alias Guard':x=92:y=126:fontsize=42:fontcolor=white",
+ f"drawtext=fontfile='{font}':text='Maps multilingual scientific terms to canonical entities':x=92:y=206:fontsize=30:fontcolor=0xd8f6df",
+ f"drawtext=fontfile='{font}':text='Preserves language tags for entity pages and JSON-LD':x=92:y=266:fontsize=30:fontcolor=0xd8f6df",
+ f"drawtext=fontfile='{font}':text='Holds homographs and false friends for curator review':x=92:y=326:fontsize=30:fontcolor=0xd8f6df",
+ f"drawtext=fontfile='{font}':text='Suppresses weak aliases before recommendations are shown':x=92:y=386:fontsize=30:fontcolor=0xd8f6df",
+ f"drawtext=fontfile='{font}':text='Handles malformed alias evidence without runtime failures':x=92:y=446:fontsize=30:fontcolor=0xd8f6df",
+ f"drawtext=fontfile='{font}':text='SCIBASE issue #17 multilingual KG integration slice':x=92:y=536:fontsize=28:fontcolor=0xffd37a",
+ ]
+)
+
+cmd = [
+ "ffmpeg",
+ "-y",
+ "-f",
+ "lavfi",
+ "-i",
+ "color=c=0x0c2130:s=1280x720:d=4:r=30",
+ "-vf",
+ vf,
+ "-pix_fmt",
+ "yuv420p",
+ "-movflags",
+ "+faststart",
+ str(OUTPUT),
+]
+
+subprocess.run(cmd, check=True)
+print(f"Wrote {OUTPUT.relative_to(ROOT)}")
diff --git a/multilingual-entity-alias-guard/package.json b/multilingual-entity-alias-guard/package.json
new file mode 100644
index 00000000..cd75e4fe
--- /dev/null
+++ b/multilingual-entity-alias-guard/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "multilingual-entity-alias-guard",
+ "version": "1.0.0",
+ "description": "Dependency-free multilingual entity alias guard for SCIBASE scientific knowledge graph integration.",
+ "main": "index.js",
+ "private": true,
+ "scripts": {
+ "test": "node test.js",
+ "demo": "node demo.js",
+ "video": "python make-demo-video.py",
+ "check": "npm test && npm run demo && npm run video"
+ }
+}
diff --git a/multilingual-entity-alias-guard/reports/alias-guard-packet.json b/multilingual-entity-alias-guard/reports/alias-guard-packet.json
new file mode 100644
index 00000000..44a38ddd
--- /dev/null
+++ b/multilingual-entity-alias-guard/reports/alias-guard-packet.json
@@ -0,0 +1,419 @@
+{
+ "corpusId": "kg-multilingual-upload-batch-17",
+ "generatedAt": "2026-05-28T07:00:00Z",
+ "mentionDecisions": [
+ {
+ "id": "mention-crispr-en",
+ "language": "en",
+ "text": "CRISPR-Cas9",
+ "documentId": "paper-1",
+ "decision": "accept-canonical-entity",
+ "reason": "trusted-translated-alias",
+ "candidateEntityId": "entity:mesh:D000077768",
+ "candidateEntityIds": [
+ "entity:mesh:D000077768"
+ ],
+ "confidence": 0.97,
+ "preservedLanguageTag": "en"
+ },
+ {
+ "id": "mention-crispr-de",
+ "language": "de",
+ "text": "CRISPR-Cas9 Geneditierung",
+ "documentId": "paper-2",
+ "decision": "accept-canonical-entity",
+ "reason": "trusted-translated-alias",
+ "candidateEntityId": "entity:mesh:D000077768",
+ "candidateEntityIds": [
+ "entity:mesh:D000077768"
+ ],
+ "confidence": 0.91,
+ "preservedLanguageTag": "de"
+ },
+ {
+ "id": "mention-crispr-es",
+ "language": "es",
+ "text": "edicion genetica CRISPR-Cas9",
+ "documentId": "paper-3",
+ "decision": "accept-canonical-entity",
+ "reason": "trusted-translated-alias",
+ "candidateEntityId": "entity:mesh:D000077768",
+ "candidateEntityIds": [
+ "entity:mesh:D000077768"
+ ],
+ "confidence": 0.89,
+ "preservedLanguageTag": "es"
+ },
+ {
+ "id": "mention-diabetes-en",
+ "language": "en",
+ "text": "diabetes mellitus",
+ "documentId": "paper-4",
+ "decision": "accept-canonical-entity",
+ "reason": "trusted-translated-alias",
+ "candidateEntityId": "entity:mesh:D003920",
+ "candidateEntityIds": [
+ "entity:mesh:D003920"
+ ],
+ "confidence": 0.94,
+ "preservedLanguageTag": "en"
+ },
+ {
+ "id": "mention-diabetes-de",
+ "language": "de",
+ "text": "Diabetes mellitus",
+ "documentId": "paper-5",
+ "decision": "accept-canonical-entity",
+ "reason": "trusted-translated-alias",
+ "candidateEntityId": "entity:mesh:D003920",
+ "candidateEntityIds": [
+ "entity:mesh:D003920"
+ ],
+ "confidence": 0.95,
+ "preservedLanguageTag": "de"
+ },
+ {
+ "id": "mention-diabetes-es",
+ "language": "es",
+ "text": "diabetes mellitus",
+ "documentId": "paper-6",
+ "decision": "accept-canonical-entity",
+ "reason": "trusted-translated-alias",
+ "candidateEntityId": "entity:mesh:D003920",
+ "candidateEntityIds": [
+ "entity:mesh:D003920"
+ ],
+ "confidence": 0.93,
+ "preservedLanguageTag": "es"
+ },
+ {
+ "id": "mention-control-es",
+ "language": "es",
+ "text": "control",
+ "documentId": "paper-7",
+ "decision": "hold-for-curator-review",
+ "reason": "false-friend-or-homograph",
+ "candidateEntityId": "entity:stat:control-group",
+ "candidateEntityIds": [
+ "entity:stat:control-group"
+ ],
+ "confidence": 0.88,
+ "preservedLanguageTag": "es"
+ },
+ {
+ "id": "mention-cellule-fr",
+ "language": "fr",
+ "text": "cellule",
+ "documentId": "paper-8",
+ "decision": "suppress-recommendation",
+ "reason": "low-confidence-alias",
+ "candidateEntityId": "entity:mesh:D002477",
+ "candidateEntityIds": [
+ "entity:mesh:D002477"
+ ],
+ "confidence": 0.61,
+ "preservedLanguageTag": "fr"
+ },
+ {
+ "id": "mention-crispr-cyrillic-spoof",
+ "language": "en",
+ "text": "СRISPR-Cas9",
+ "documentId": "paper-9",
+ "decision": "hold-for-curator-review",
+ "reason": "script-confusable-alias",
+ "candidateEntityId": "entity:mesh:D000077768",
+ "candidateEntityIds": [
+ "entity:mesh:D000077768"
+ ],
+ "confidence": 0.97,
+ "preservedLanguageTag": "en"
+ },
+ {
+ "id": "mention-crispr-greek-alpha-spoof",
+ "language": "en",
+ "text": "CRISPR-Cαs9",
+ "documentId": "paper-10",
+ "decision": "hold-for-curator-review",
+ "reason": "script-confusable-alias",
+ "candidateEntityId": "entity:mesh:D000077768",
+ "candidateEntityIds": [
+ "entity:mesh:D000077768"
+ ],
+ "confidence": 0.97,
+ "preservedLanguageTag": "en"
+ }
+ ],
+ "entityPackets": [
+ {
+ "id": "entity:mesh:D000077768",
+ "canonicalName": "CRISPR-Cas9",
+ "ontology": "MeSH",
+ "identifier": "D000077768",
+ "languages": [
+ "de",
+ "en",
+ "es"
+ ],
+ "aliasEvidenceIssues": [],
+ "mentions": [
+ {
+ "id": "mention-crispr-en",
+ "text": "CRISPR-Cas9",
+ "language": "en",
+ "documentId": "paper-1",
+ "confidence": 0.97
+ },
+ {
+ "id": "mention-crispr-de",
+ "text": "CRISPR-Cas9 Geneditierung",
+ "language": "de",
+ "documentId": "paper-2",
+ "confidence": 0.91
+ },
+ {
+ "id": "mention-crispr-es",
+ "text": "edicion genetica CRISPR-Cas9",
+ "language": "es",
+ "documentId": "paper-3",
+ "confidence": 0.89
+ }
+ ],
+ "localizedNames": {
+ "en": [
+ "CRISPR-Cas9"
+ ],
+ "de": [
+ "CRISPR-Cas9 Geneditierung"
+ ],
+ "es": [
+ "edicion genetica CRISPR-Cas9"
+ ]
+ },
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "CRISPR-Cas9",
+ "identifier": "MeSH:D000077768",
+ "inDefinedTermSet": "MeSH",
+ "alternateName": [
+ "CRISPR-Cas9",
+ "CRISPR-Cas9 Geneditierung",
+ "edicion genetica CRISPR-Cas9"
+ ]
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": [
+ {
+ "@type": "DefinedTerm",
+ "name": "CRISPR-Cas9",
+ "inLanguage": "en",
+ "identifier": "MeSH:D000077768"
+ },
+ {
+ "@type": "DefinedTerm",
+ "name": "CRISPR-Cas9 Geneditierung",
+ "inLanguage": "de",
+ "identifier": "MeSH:D000077768"
+ },
+ {
+ "@type": "DefinedTerm",
+ "name": "edicion genetica CRISPR-Cas9",
+ "inLanguage": "es",
+ "identifier": "MeSH:D000077768"
+ }
+ ]
+ }
+ },
+ {
+ "id": "entity:mesh:D003920",
+ "canonicalName": "Diabetes Mellitus",
+ "ontology": "MeSH",
+ "identifier": "D003920",
+ "languages": [
+ "de",
+ "en",
+ "es"
+ ],
+ "aliasEvidenceIssues": [],
+ "mentions": [
+ {
+ "id": "mention-diabetes-en",
+ "text": "diabetes mellitus",
+ "language": "en",
+ "documentId": "paper-4",
+ "confidence": 0.94
+ },
+ {
+ "id": "mention-diabetes-de",
+ "text": "Diabetes mellitus",
+ "language": "de",
+ "documentId": "paper-5",
+ "confidence": 0.95
+ },
+ {
+ "id": "mention-diabetes-es",
+ "text": "diabetes mellitus",
+ "language": "es",
+ "documentId": "paper-6",
+ "confidence": 0.93
+ }
+ ],
+ "localizedNames": {
+ "en": [
+ "diabetes mellitus"
+ ],
+ "de": [
+ "Diabetes mellitus"
+ ],
+ "es": [
+ "diabetes mellitus"
+ ]
+ },
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "Diabetes Mellitus",
+ "identifier": "MeSH:D003920",
+ "inDefinedTermSet": "MeSH",
+ "alternateName": [
+ "diabetes mellitus",
+ "Diabetes mellitus",
+ "diabetes mellitus"
+ ]
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": [
+ {
+ "@type": "DefinedTerm",
+ "name": "diabetes mellitus",
+ "inLanguage": "en",
+ "identifier": "MeSH:D003920"
+ },
+ {
+ "@type": "DefinedTerm",
+ "name": "Diabetes mellitus",
+ "inLanguage": "de",
+ "identifier": "MeSH:D003920"
+ },
+ {
+ "@type": "DefinedTerm",
+ "name": "diabetes mellitus",
+ "inLanguage": "es",
+ "identifier": "MeSH:D003920"
+ }
+ ]
+ }
+ },
+ {
+ "id": "entity:stat:control-group",
+ "canonicalName": "Control Group",
+ "ontology": "SCIBASE-STAT",
+ "identifier": "control-group",
+ "languages": [],
+ "aliasEvidenceIssues": [],
+ "mentions": [],
+ "localizedNames": {
+ "en": [
+ "control group"
+ ],
+ "es": [
+ "grupo control"
+ ],
+ "de": [
+ "Kontrollgruppe"
+ ]
+ },
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "Control Group",
+ "identifier": "SCIBASE-STAT:control-group",
+ "inDefinedTermSet": "SCIBASE-STAT",
+ "alternateName": [
+ "control group",
+ "grupo control",
+ "Kontrollgruppe"
+ ]
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": []
+ }
+ }
+ ],
+ "curatorActions": [
+ {
+ "id": "curate-mention-control-es",
+ "mentionId": "mention-control-es",
+ "action": "review-multilingual-homograph",
+ "priority": "high",
+ "language": "es",
+ "text": "control",
+ "candidateEntityId": "entity:stat:control-group",
+ "candidateEntityIds": [
+ "entity:stat:control-group"
+ ],
+ "reason": "false-friend-or-homograph"
+ },
+ {
+ "id": "curate-mention-cellule-fr",
+ "mentionId": "mention-cellule-fr",
+ "action": "verify-translated-alias-before-recommendation",
+ "priority": "normal",
+ "language": "fr",
+ "text": "cellule",
+ "candidateEntityId": "entity:mesh:D002477",
+ "candidateEntityIds": [
+ "entity:mesh:D002477"
+ ],
+ "reason": "low-confidence-alias"
+ },
+ {
+ "id": "curate-mention-crispr-cyrillic-spoof",
+ "mentionId": "mention-crispr-cyrillic-spoof",
+ "action": "review-multilingual-script-confusable",
+ "priority": "high",
+ "language": "en",
+ "text": "СRISPR-Cas9",
+ "candidateEntityId": "entity:mesh:D000077768",
+ "candidateEntityIds": [
+ "entity:mesh:D000077768"
+ ],
+ "reason": "script-confusable-alias"
+ },
+ {
+ "id": "curate-mention-crispr-greek-alpha-spoof",
+ "mentionId": "mention-crispr-greek-alpha-spoof",
+ "action": "review-multilingual-script-confusable",
+ "priority": "high",
+ "language": "en",
+ "text": "CRISPR-Cαs9",
+ "candidateEntityId": "entity:mesh:D000077768",
+ "candidateEntityIds": [
+ "entity:mesh:D000077768"
+ ],
+ "reason": "script-confusable-alias"
+ }
+ ],
+ "recommendationGuards": {
+ "suppressedMentionIds": [
+ "mention-control-es",
+ "mention-cellule-fr",
+ "mention-crispr-cyrillic-spoof",
+ "mention-crispr-greek-alpha-spoof"
+ ],
+ "safeEntityIds": [
+ "entity:mesh:D000077768",
+ "entity:mesh:D003920"
+ ]
+ },
+ "summary": {
+ "acceptedMentions": 6,
+ "heldMentions": 3,
+ "suppressedMentions": 1,
+ "entityPackets": 3
+ },
+ "auditDigest": "sha256:f11c08d8634f046b8382a175239964b368830acc24fe0f8c2ff1b92cdd02ef8f"
+}
diff --git a/multilingual-entity-alias-guard/reports/alias-guard-report.md b/multilingual-entity-alias-guard/reports/alias-guard-report.md
new file mode 100644
index 00000000..72b1afd5
--- /dev/null
+++ b/multilingual-entity-alias-guard/reports/alias-guard-report.md
@@ -0,0 +1,52 @@
+# Multilingual Entity Alias Guard
+
+Corpus: kg-multilingual-upload-batch-17
+Generated: 2026-05-28T07:00:00Z
+
+## Summary
+
+- Accepted mentions: 6
+- Held curator-review mentions: 3
+- Suppressed low-confidence mentions: 1
+- Entity packets emitted: 3
+- Audit digest: sha256:f11c08d8634f046b8382a175239964b368830acc24fe0f8c2ff1b92cdd02ef8f
+
+## Accepted Canonical Mappings
+
+- mention-crispr-en: CRISPR-Cas9 (en) -> entity:mesh:D000077768
+- mention-crispr-de: CRISPR-Cas9 Geneditierung (de) -> entity:mesh:D000077768
+- mention-crispr-es: edicion genetica CRISPR-Cas9 (es) -> entity:mesh:D000077768
+- mention-diabetes-en: diabetes mellitus (en) -> entity:mesh:D003920
+- mention-diabetes-de: Diabetes mellitus (de) -> entity:mesh:D003920
+- mention-diabetes-es: diabetes mellitus (es) -> entity:mesh:D003920
+
+## Curator Actions
+
+- curate-mention-control-es: review-multilingual-homograph (es:control)
+- curate-mention-cellule-fr: verify-translated-alias-before-recommendation (fr:cellule)
+- curate-mention-crispr-cyrillic-spoof: review-multilingual-script-confusable (en:СRISPR-Cas9)
+- curate-mention-crispr-greek-alpha-spoof: review-multilingual-script-confusable (en:CRISPR-Cαs9)
+
+## Recommendation Guard
+
+Held or suppressed mentions are not allowed to drive entity-page recommendations until a curator verifies the alias mapping.
+
+## Sparse Corpus Guard
+
+Sparse ontology or corpus exports that omit localized names, mention lists, or homograph policy still produce deterministic graph review evidence. The sparse fixture emitted 1 entity packet and 0 mention decisions.
+
+## Candidate Alias Conflict Guard
+
+Extractor candidates that disagree with trusted multilingual alias lookup are held for curator review instead of silently overriding the upstream candidate. The conflict fixture decision is hold-for-curator-review with reason candidate-alias-conflict.
+
+## Malformed Mention Text Guard
+
+Malformed mention text values are held for curator review instead of crashing alias normalization. The malformed fixture decision is hold-for-curator-review with reason malformed-mention-text, and it emits review-multilingual-malformed-mention.
+
+## Malformed Alias Evidence Guard
+
+Malformed localized-name evidence is omitted from alias lookup and JSON-LD alternate names instead of crashing ontology review. The malformed alias fixture records 1 alias evidence issue with reason malformed-localized-name.
+
+## Safety
+
+All fixtures are synthetic. The module does not call live ontologies, identity providers, external APIs, private corpora, search indexes, or recommendation systems.
diff --git a/multilingual-entity-alias-guard/reports/candidate-alias-conflict-packet.json b/multilingual-entity-alias-guard/reports/candidate-alias-conflict-packet.json
new file mode 100644
index 00000000..37881f7f
--- /dev/null
+++ b/multilingual-entity-alias-guard/reports/candidate-alias-conflict-packet.json
@@ -0,0 +1,160 @@
+{
+ "corpusId": "kg-candidate-alias-conflict-17",
+ "generatedAt": "2026-05-30T12:30:00Z",
+ "mentionDecisions": [
+ {
+ "id": "mention-diabetes-conflicting-candidate",
+ "language": "es",
+ "text": "diabetes mellitus",
+ "documentId": "paper-17",
+ "decision": "hold-for-curator-review",
+ "reason": "candidate-alias-conflict",
+ "candidateEntityId": null,
+ "candidateEntityIds": [
+ "entity:mesh:D003920",
+ "entity:stat:control-group"
+ ],
+ "confidence": 0.93,
+ "preservedLanguageTag": "es"
+ }
+ ],
+ "entityPackets": [
+ {
+ "id": "entity:mesh:D000077768",
+ "canonicalName": "CRISPR-Cas9",
+ "ontology": "MeSH",
+ "identifier": "D000077768",
+ "languages": [],
+ "aliasEvidenceIssues": [],
+ "mentions": [],
+ "localizedNames": {
+ "en": [
+ "CRISPR-Cas9"
+ ],
+ "de": [
+ "CRISPR-Cas9 Geneditierung"
+ ],
+ "es": [
+ "edicion genetica CRISPR-Cas9"
+ ]
+ },
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "CRISPR-Cas9",
+ "identifier": "MeSH:D000077768",
+ "inDefinedTermSet": "MeSH",
+ "alternateName": [
+ "CRISPR-Cas9",
+ "CRISPR-Cas9 Geneditierung",
+ "edicion genetica CRISPR-Cas9"
+ ]
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": []
+ }
+ },
+ {
+ "id": "entity:mesh:D003920",
+ "canonicalName": "Diabetes Mellitus",
+ "ontology": "MeSH",
+ "identifier": "D003920",
+ "languages": [],
+ "aliasEvidenceIssues": [],
+ "mentions": [],
+ "localizedNames": {
+ "en": [
+ "diabetes mellitus"
+ ],
+ "de": [
+ "Diabetes mellitus"
+ ],
+ "es": [
+ "diabetes mellitus"
+ ]
+ },
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "Diabetes Mellitus",
+ "identifier": "MeSH:D003920",
+ "inDefinedTermSet": "MeSH",
+ "alternateName": [
+ "diabetes mellitus",
+ "Diabetes mellitus",
+ "diabetes mellitus"
+ ]
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": []
+ }
+ },
+ {
+ "id": "entity:stat:control-group",
+ "canonicalName": "Control Group",
+ "ontology": "SCIBASE-STAT",
+ "identifier": "control-group",
+ "languages": [],
+ "aliasEvidenceIssues": [],
+ "mentions": [],
+ "localizedNames": {
+ "en": [
+ "control group"
+ ],
+ "es": [
+ "grupo control"
+ ],
+ "de": [
+ "Kontrollgruppe"
+ ]
+ },
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "Control Group",
+ "identifier": "SCIBASE-STAT:control-group",
+ "inDefinedTermSet": "SCIBASE-STAT",
+ "alternateName": [
+ "control group",
+ "grupo control",
+ "Kontrollgruppe"
+ ]
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": []
+ }
+ }
+ ],
+ "curatorActions": [
+ {
+ "id": "curate-mention-diabetes-conflicting-candidate",
+ "mentionId": "mention-diabetes-conflicting-candidate",
+ "action": "review-multilingual-candidate-alias-conflict",
+ "priority": "high",
+ "language": "es",
+ "text": "diabetes mellitus",
+ "candidateEntityId": null,
+ "candidateEntityIds": [
+ "entity:mesh:D003920",
+ "entity:stat:control-group"
+ ],
+ "reason": "candidate-alias-conflict"
+ }
+ ],
+ "recommendationGuards": {
+ "suppressedMentionIds": [
+ "mention-diabetes-conflicting-candidate"
+ ],
+ "safeEntityIds": []
+ },
+ "summary": {
+ "acceptedMentions": 0,
+ "heldMentions": 1,
+ "suppressedMentions": 0,
+ "entityPackets": 3
+ },
+ "auditDigest": "sha256:9c5561ec683bbf63505c6a252c7cae3559dca84172940c59941bfab62e23485e"
+}
diff --git a/multilingual-entity-alias-guard/reports/demo.mp4 b/multilingual-entity-alias-guard/reports/demo.mp4
new file mode 100644
index 00000000..dde86e70
Binary files /dev/null and b/multilingual-entity-alias-guard/reports/demo.mp4 differ
diff --git a/multilingual-entity-alias-guard/reports/malformed-alias-evidence-packet.json b/multilingual-entity-alias-guard/reports/malformed-alias-evidence-packet.json
new file mode 100644
index 00000000..abea381a
--- /dev/null
+++ b/multilingual-entity-alias-guard/reports/malformed-alias-evidence-packet.json
@@ -0,0 +1,87 @@
+{
+ "corpusId": "kg-malformed-localized-name-17",
+ "generatedAt": "2026-05-31T10:46:00Z",
+ "mentionDecisions": [
+ {
+ "id": "mention-diabetes-es",
+ "language": "es",
+ "text": "diabetes mellitus",
+ "documentId": "paper-19",
+ "decision": "accept-canonical-entity",
+ "reason": "trusted-translated-alias",
+ "candidateEntityId": "entity:mesh:D003920",
+ "candidateEntityIds": [
+ "entity:mesh:D003920"
+ ],
+ "confidence": 0.94,
+ "preservedLanguageTag": "es"
+ }
+ ],
+ "entityPackets": [
+ {
+ "id": "entity:mesh:D003920",
+ "canonicalName": "Diabetes Mellitus",
+ "ontology": "MeSH",
+ "identifier": "D003920",
+ "languages": [
+ "es"
+ ],
+ "aliasEvidenceIssues": [
+ {
+ "language": "es",
+ "reason": "malformed-localized-name",
+ "valueType": "object"
+ }
+ ],
+ "mentions": [
+ {
+ "id": "mention-diabetes-es",
+ "text": "diabetes mellitus",
+ "language": "es",
+ "documentId": "paper-19",
+ "confidence": 0.94
+ }
+ ],
+ "localizedNames": {
+ "es": [
+ "diabetes mellitus"
+ ]
+ },
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "Diabetes Mellitus",
+ "identifier": "MeSH:D003920",
+ "inDefinedTermSet": "MeSH",
+ "alternateName": [
+ "diabetes mellitus"
+ ]
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": [
+ {
+ "@type": "DefinedTerm",
+ "name": "diabetes mellitus",
+ "inLanguage": "es",
+ "identifier": "MeSH:D003920"
+ }
+ ]
+ }
+ }
+ ],
+ "curatorActions": [],
+ "recommendationGuards": {
+ "suppressedMentionIds": [],
+ "safeEntityIds": [
+ "entity:mesh:D003920"
+ ]
+ },
+ "summary": {
+ "acceptedMentions": 1,
+ "heldMentions": 0,
+ "suppressedMentions": 0,
+ "entityPackets": 1
+ },
+ "auditDigest": "sha256:e51b1670a58750de834139f8abe38ec170fe5eada627f224176759484411862e"
+}
diff --git a/multilingual-entity-alias-guard/reports/malformed-mention-text-packet.json b/multilingual-entity-alias-guard/reports/malformed-mention-text-packet.json
new file mode 100644
index 00000000..9f149fec
--- /dev/null
+++ b/multilingual-entity-alias-guard/reports/malformed-mention-text-packet.json
@@ -0,0 +1,162 @@
+{
+ "corpusId": "kg-malformed-mention-text-17",
+ "generatedAt": "2026-05-31T10:45:00Z",
+ "mentionDecisions": [
+ {
+ "id": "mention-malformed-text",
+ "language": "es",
+ "text": {
+ "value": "diabetes mellitus"
+ },
+ "documentId": "paper-18",
+ "decision": "hold-for-curator-review",
+ "reason": "malformed-mention-text",
+ "candidateEntityId": "entity:mesh:D003920",
+ "candidateEntityIds": [
+ "entity:mesh:D003920"
+ ],
+ "confidence": 0.94,
+ "preservedLanguageTag": "es"
+ }
+ ],
+ "entityPackets": [
+ {
+ "id": "entity:mesh:D000077768",
+ "canonicalName": "CRISPR-Cas9",
+ "ontology": "MeSH",
+ "identifier": "D000077768",
+ "languages": [],
+ "aliasEvidenceIssues": [],
+ "mentions": [],
+ "localizedNames": {
+ "en": [
+ "CRISPR-Cas9"
+ ],
+ "de": [
+ "CRISPR-Cas9 Geneditierung"
+ ],
+ "es": [
+ "edicion genetica CRISPR-Cas9"
+ ]
+ },
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "CRISPR-Cas9",
+ "identifier": "MeSH:D000077768",
+ "inDefinedTermSet": "MeSH",
+ "alternateName": [
+ "CRISPR-Cas9",
+ "CRISPR-Cas9 Geneditierung",
+ "edicion genetica CRISPR-Cas9"
+ ]
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": []
+ }
+ },
+ {
+ "id": "entity:mesh:D003920",
+ "canonicalName": "Diabetes Mellitus",
+ "ontology": "MeSH",
+ "identifier": "D003920",
+ "languages": [],
+ "aliasEvidenceIssues": [],
+ "mentions": [],
+ "localizedNames": {
+ "en": [
+ "diabetes mellitus"
+ ],
+ "de": [
+ "Diabetes mellitus"
+ ],
+ "es": [
+ "diabetes mellitus"
+ ]
+ },
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "Diabetes Mellitus",
+ "identifier": "MeSH:D003920",
+ "inDefinedTermSet": "MeSH",
+ "alternateName": [
+ "diabetes mellitus",
+ "Diabetes mellitus",
+ "diabetes mellitus"
+ ]
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": []
+ }
+ },
+ {
+ "id": "entity:stat:control-group",
+ "canonicalName": "Control Group",
+ "ontology": "SCIBASE-STAT",
+ "identifier": "control-group",
+ "languages": [],
+ "aliasEvidenceIssues": [],
+ "mentions": [],
+ "localizedNames": {
+ "en": [
+ "control group"
+ ],
+ "es": [
+ "grupo control"
+ ],
+ "de": [
+ "Kontrollgruppe"
+ ]
+ },
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "Control Group",
+ "identifier": "SCIBASE-STAT:control-group",
+ "inDefinedTermSet": "SCIBASE-STAT",
+ "alternateName": [
+ "control group",
+ "grupo control",
+ "Kontrollgruppe"
+ ]
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": []
+ }
+ }
+ ],
+ "curatorActions": [
+ {
+ "id": "curate-mention-malformed-text",
+ "mentionId": "mention-malformed-text",
+ "action": "review-multilingual-malformed-mention",
+ "priority": "high",
+ "language": "es",
+ "text": {
+ "value": "diabetes mellitus"
+ },
+ "candidateEntityId": "entity:mesh:D003920",
+ "candidateEntityIds": [
+ "entity:mesh:D003920"
+ ],
+ "reason": "malformed-mention-text"
+ }
+ ],
+ "recommendationGuards": {
+ "suppressedMentionIds": [
+ "mention-malformed-text"
+ ],
+ "safeEntityIds": []
+ },
+ "summary": {
+ "acceptedMentions": 0,
+ "heldMentions": 1,
+ "suppressedMentions": 0,
+ "entityPackets": 3
+ },
+ "auditDigest": "sha256:6fe14f9c776e3beb44509a8d8197586bb86757ccf617e0faa31cb20664fcf8ee"
+}
diff --git a/multilingual-entity-alias-guard/reports/sparse-alias-guard-packet.json b/multilingual-entity-alias-guard/reports/sparse-alias-guard-packet.json
new file mode 100644
index 00000000..46f4bbc4
--- /dev/null
+++ b/multilingual-entity-alias-guard/reports/sparse-alias-guard-packet.json
@@ -0,0 +1,41 @@
+{
+ "corpusId": "kg-sparse-ontology-export-17",
+ "generatedAt": "2026-05-30T12:00:00Z",
+ "mentionDecisions": [],
+ "entityPackets": [
+ {
+ "id": "entity:mesh:D012345",
+ "canonicalName": "Sparse Ontology Entity",
+ "ontology": "MeSH",
+ "identifier": "D012345",
+ "languages": [],
+ "aliasEvidenceIssues": [],
+ "mentions": [],
+ "localizedNames": {},
+ "jsonLd": {
+ "@context": "https://schema.org",
+ "@type": "DefinedTerm",
+ "name": "Sparse Ontology Entity",
+ "identifier": "MeSH:D012345",
+ "inDefinedTermSet": "MeSH",
+ "alternateName": []
+ },
+ "schemaOrg": {
+ "@type": "ScholarlyArticle",
+ "about": []
+ }
+ }
+ ],
+ "curatorActions": [],
+ "recommendationGuards": {
+ "suppressedMentionIds": [],
+ "safeEntityIds": []
+ },
+ "summary": {
+ "acceptedMentions": 0,
+ "heldMentions": 0,
+ "suppressedMentions": 0,
+ "entityPackets": 1
+ },
+ "auditDigest": "sha256:a6c361911c2ddc87ab437444d96ffd4422b6b5e2295ed41b25a7f60d0009192a"
+}
diff --git a/multilingual-entity-alias-guard/reports/summary.svg b/multilingual-entity-alias-guard/reports/summary.svg
new file mode 100644
index 00000000..0fe0bed8
--- /dev/null
+++ b/multilingual-entity-alias-guard/reports/summary.svg
@@ -0,0 +1,12 @@
+
diff --git a/multilingual-entity-alias-guard/requirements-map.md b/multilingual-entity-alias-guard/requirements-map.md
new file mode 100644
index 00000000..9388bdf0
--- /dev/null
+++ b/multilingual-entity-alias-guard/requirements-map.md
@@ -0,0 +1,32 @@
+# Requirements Map
+
+## Entity Extraction
+
+- Preserves language-tagged mentions from uploaded papers and datasets.
+- Maps trusted translated aliases to canonical ontology identifiers.
+- Normalizes language-tag casing plus hyphenated or underscored regional subtags for alias lookup while preserving the original tag on decisions.
+- Holds false friends and homographs before creating graph edges.
+- Holds same-language alias collisions when ontology entries reuse the same translated term.
+- Holds extractor-candidate and multilingual-alias conflicts before creating graph edges or recommendation inputs.
+- Holds malformed mention text values for curator review instead of crashing alias normalization or accepting unsafe graph evidence.
+- Holds Latin-language mentions with Cyrillic or Greek lookalike characters, including lowercase Greek confusables, for curator review before creating graph edges.
+- Treats omitted localized-name maps, malformed localized-name entries, mention lists, and homograph policies as sparse graph evidence instead of crashing corpus review.
+- Emits schema.org-style `DefinedTerm` JSON-LD packets for entity pages.
+
+## Knowledge Navigation
+
+- Keeps accepted multilingual aliases attached to canonical entity pages.
+- Produces curator actions for ambiguous terms that would pollute graph search.
+- Prevents unknown or low-confidence aliases from becoming discoverable graph nodes.
+
+## AI Research Recommendations
+
+- Suppresses low-confidence or missing-confidence mentions from recommendation inputs.
+- Exposes safe canonical entity IDs for graph recommendations.
+- Keeps multilingual evidence auditable with deterministic digests.
+
+## Safety And Scope
+
+- Synthetic data only.
+- No credentials, private corpora, live ontology calls, external APIs, or production recommendation systems.
+- This slice is distinct from ontology drift, synonym dedupe, generic entity disambiguation, temporal validity, geospatial provenance, and recommendation visibility/diversity guards.
diff --git a/multilingual-entity-alias-guard/test.js b/multilingual-entity-alias-guard/test.js
new file mode 100644
index 00000000..5f2f5cf1
--- /dev/null
+++ b/multilingual-entity-alias-guard/test.js
@@ -0,0 +1,460 @@
+const assert = require('assert');
+const {
+ evaluateAliasGuard,
+ buildSampleCorpus
+} = require('./index');
+
+function byId(items, id) {
+ return items.find((item) => item.id === id);
+}
+
+function testTrustedTranslatedAliasesBecomeCanonicalGraphNodes() {
+ const result = evaluateAliasGuard(buildSampleCorpus());
+ const crispr = byId(result.entityPackets, 'entity:mesh:D000077768');
+
+ assert.equal(crispr.canonicalName, 'CRISPR-Cas9');
+ assert.deepEqual(crispr.languages.sort(), ['de', 'en', 'es']);
+ assert.equal(crispr.mentions.length, 3);
+ assert.equal(crispr.jsonLd['@type'], 'DefinedTerm');
+ assert.equal(crispr.jsonLd.identifier, 'MeSH:D000077768');
+}
+
+function testFalseFriendMentionsAreHeldForCuratorReview() {
+ const result = evaluateAliasGuard(buildSampleCorpus());
+ const event = byId(result.mentionDecisions, 'mention-control-es');
+
+ assert.equal(event.decision, 'hold-for-curator-review');
+ assert.equal(event.reason, 'false-friend-or-homograph');
+ assert.equal(event.candidateEntityId, 'entity:stat:control-group');
+
+ const action = byId(result.curatorActions, 'curate-mention-control-es');
+ assert.equal(action.priority, 'high');
+ assert.equal(action.action, 'review-multilingual-homograph');
+}
+
+function testSameLanguageAliasCollisionsAreHeldForCuratorReview() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.entities.push({
+ id: 'entity:custom:diabetes-insipidus',
+ canonicalName: 'Diabetes Insipidus',
+ ontology: 'SCIBASE-MED',
+ identifier: 'diabetes-insipidus',
+ localizedNames: {
+ es: ['diabetes mellitus']
+ }
+ });
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-diabetes-es');
+
+ assert.equal(event.decision, 'hold-for-curator-review');
+ assert.equal(event.reason, 'alias-collision');
+ assert.deepEqual(event.candidateEntityIds, [
+ 'entity:custom:diabetes-insipidus',
+ 'entity:mesh:D003920'
+ ]);
+
+ const diabetes = byId(result.entityPackets, 'entity:mesh:D003920');
+ assert.equal(diabetes.mentions.length, 2);
+
+ const action = byId(result.curatorActions, 'curate-mention-diabetes-es');
+ assert.equal(action.priority, 'high');
+ assert.equal(action.action, 'review-multilingual-alias-collision');
+}
+
+function testConflictingExtractorCandidateAndAliasLookupAreHeld() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.mentions = [
+ {
+ id: 'mention-diabetes-conflicting-candidate',
+ documentId: 'paper-17',
+ text: 'diabetes mellitus',
+ language: 'es',
+ confidence: 0.93,
+ candidateEntityId: 'entity:stat:control-group'
+ }
+ ];
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-diabetes-conflicting-candidate');
+ const action = byId(result.curatorActions, 'curate-mention-diabetes-conflicting-candidate');
+ const diabetes = byId(result.entityPackets, 'entity:mesh:D003920');
+
+ assert.equal(event.decision, 'hold-for-curator-review');
+ assert.equal(event.reason, 'candidate-alias-conflict');
+ assert.deepEqual(event.candidateEntityIds, [
+ 'entity:mesh:D003920',
+ 'entity:stat:control-group'
+ ]);
+ assert.equal(action.action, 'review-multilingual-candidate-alias-conflict');
+ assert.equal(action.priority, 'high');
+ assert.equal(diabetes.mentions.length, 0);
+ assert.equal(result.recommendationGuards.safeEntityIds.includes('entity:mesh:D003920'), false);
+}
+
+function testUnicodeAndWhitespaceAliasesMatchCanonicalEntities() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.entities.push({
+ id: 'entity:mesh:D005260',
+ canonicalName: 'Gene Therapy',
+ ontology: 'MeSH',
+ identifier: 'D005260',
+ localizedNames: {
+ es: ['terapia ge\u0301nica']
+ }
+ });
+ corpus.mentions.push({
+ id: 'mention-gene-therapy-es',
+ documentId: 'paper-9',
+ text: ' terapia g\u00E9nica ',
+ language: 'es',
+ confidence: 0.9
+ });
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-gene-therapy-es');
+
+ assert.equal(event.decision, 'accept-canonical-entity');
+ assert.equal(event.reason, 'trusted-translated-alias');
+ assert.equal(event.candidateEntityId, 'entity:mesh:D005260');
+}
+
+function testLanguageTagCaseDoesNotSuppressTrustedAliases() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.mentions.push({
+ id: 'mention-diabetes-es-uppercase',
+ documentId: 'paper-10',
+ text: 'diabetes mellitus',
+ language: 'ES',
+ confidence: 0.92
+ });
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-diabetes-es-uppercase');
+
+ assert.equal(event.decision, 'accept-canonical-entity');
+ assert.equal(event.reason, 'trusted-translated-alias');
+ assert.equal(event.candidateEntityId, 'entity:mesh:D003920');
+ assert.equal(event.preservedLanguageTag, 'ES');
+}
+
+function testRegionalLanguageTagsUseBaseAliasLookup() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.mentions.push({
+ id: 'mention-diabetes-es-mx',
+ documentId: 'paper-11',
+ text: 'diabetes mellitus',
+ language: 'es-MX',
+ confidence: 0.92
+ });
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-diabetes-es-mx');
+
+ assert.equal(event.decision, 'accept-canonical-entity');
+ assert.equal(event.reason, 'trusted-translated-alias');
+ assert.equal(event.candidateEntityId, 'entity:mesh:D003920');
+ assert.equal(event.preservedLanguageTag, 'es-MX');
+}
+
+function testRegionalLanguageTagsStillUseBaseHomographHolds() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.mentions.push({
+ id: 'mention-control-es-mx',
+ documentId: 'paper-12',
+ text: 'control',
+ language: 'es-MX',
+ confidence: 0.88,
+ candidateEntityId: 'entity:stat:control-group'
+ });
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-control-es-mx');
+
+ assert.equal(event.decision, 'hold-for-curator-review');
+ assert.equal(event.reason, 'false-friend-or-homograph');
+ assert.equal(event.candidateEntityId, 'entity:stat:control-group');
+ assert.equal(event.preservedLanguageTag, 'es-MX');
+}
+
+function testUnderscoreRegionalLanguageTagsUseBaseAliasAndHomographRules() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.mentions.push(
+ {
+ id: 'mention-diabetes-es-mx-underscore',
+ documentId: 'paper-13',
+ text: 'diabetes mellitus',
+ language: 'es_MX',
+ confidence: 0.92
+ },
+ {
+ id: 'mention-control-es-mx-underscore',
+ documentId: 'paper-14',
+ text: 'control',
+ language: 'es_MX',
+ confidence: 0.88,
+ candidateEntityId: 'entity:stat:control-group'
+ }
+ );
+
+ const result = evaluateAliasGuard(corpus);
+ const aliasEvent = byId(result.mentionDecisions, 'mention-diabetes-es-mx-underscore');
+ const homographEvent = byId(result.mentionDecisions, 'mention-control-es-mx-underscore');
+
+ assert.equal(aliasEvent.decision, 'accept-canonical-entity');
+ assert.equal(aliasEvent.reason, 'trusted-translated-alias');
+ assert.equal(aliasEvent.candidateEntityId, 'entity:mesh:D003920');
+ assert.equal(aliasEvent.preservedLanguageTag, 'es_MX');
+ assert.equal(homographEvent.decision, 'hold-for-curator-review');
+ assert.equal(homographEvent.reason, 'false-friend-or-homograph');
+ assert.equal(homographEvent.candidateEntityId, 'entity:stat:control-group');
+}
+
+function testMixedScriptLatinLanguageMentionsAreHeldForCuratorReview() {
+ const result = evaluateAliasGuard(buildSampleCorpus());
+ const event = byId(result.mentionDecisions, 'mention-crispr-cyrillic-spoof');
+
+ assert.equal(event.decision, 'hold-for-curator-review');
+ assert.equal(event.reason, 'script-confusable-alias');
+ assert.equal(event.candidateEntityId, 'entity:mesh:D000077768');
+
+ const action = byId(result.curatorActions, 'curate-mention-crispr-cyrillic-spoof');
+ assert.equal(action.priority, 'high');
+ assert.equal(action.action, 'review-multilingual-script-confusable');
+}
+
+function testLowercaseGreekLookalikeLatinMentionsAreHeldForCuratorReview() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.mentions = [
+ {
+ id: 'mention-crispr-greek-alpha-spoof',
+ documentId: 'paper-15',
+ text: 'CRISPR-C\u03B1s9',
+ language: 'en',
+ confidence: 0.97,
+ candidateEntityId: 'entity:mesh:D000077768'
+ }
+ ];
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-crispr-greek-alpha-spoof');
+ const action = byId(result.curatorActions, 'curate-mention-crispr-greek-alpha-spoof');
+
+ assert.equal(event.decision, 'hold-for-curator-review');
+ assert.equal(event.reason, 'script-confusable-alias');
+ assert.equal(event.candidateEntityId, 'entity:mesh:D000077768');
+ assert.equal(action.priority, 'high');
+ assert.equal(action.action, 'review-multilingual-script-confusable');
+}
+
+function testLowConfidenceAliasesDoNotDriveRecommendations() {
+ const result = evaluateAliasGuard(buildSampleCorpus());
+ const event = byId(result.mentionDecisions, 'mention-cellule-fr');
+
+ assert.equal(event.decision, 'suppress-recommendation');
+ assert.equal(event.reason, 'low-confidence-alias');
+ assert.equal(result.recommendationGuards.suppressedMentionIds.includes('mention-cellule-fr'), true);
+}
+
+function testMissingConfidenceAliasesDoNotDriveRecommendations() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.mentions = [
+ {
+ id: 'mention-diabetes-missing-confidence',
+ documentId: 'paper-13',
+ text: 'diabetes mellitus',
+ language: 'es'
+ }
+ ];
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-diabetes-missing-confidence');
+ const diabetes = byId(result.entityPackets, 'entity:mesh:D003920');
+
+ assert.equal(event.decision, 'suppress-recommendation');
+ assert.equal(event.reason, 'low-confidence-alias');
+ assert.equal(event.candidateEntityId, 'entity:mesh:D003920');
+ assert.equal(result.recommendationGuards.suppressedMentionIds.includes('mention-diabetes-missing-confidence'), true);
+ assert.equal(result.recommendationGuards.safeEntityIds.includes('entity:mesh:D003920'), false);
+ assert.equal(diabetes.mentions.length, 0);
+}
+
+function testMissingLocalizedNamesEmitEmptyEntityAliasPacket() {
+ const corpus = {
+ corpusId: 'kg-sparse-ontology-export-17',
+ generatedAt: '2026-05-30T12:00:00Z',
+ entities: [
+ {
+ id: 'entity:mesh:D012345',
+ canonicalName: 'Sparse Ontology Entity',
+ ontology: 'MeSH',
+ identifier: 'D012345'
+ }
+ ],
+ homographs: {},
+ mentions: []
+ };
+
+ const result = evaluateAliasGuard(corpus);
+ const entity = byId(result.entityPackets, 'entity:mesh:D012345');
+
+ assert.deepEqual(entity.localizedNames, {});
+ assert.deepEqual(entity.jsonLd.alternateName, []);
+ assert.deepEqual(entity.mentions, []);
+ assert.equal(result.summary.entityPackets, 1);
+ assert.equal(result.summary.acceptedMentions, 0);
+ assert.ok(result.auditDigest.startsWith('sha256:'));
+}
+
+function testMalformedLocalizedNameTermsAreOmittedFromAliasEvidence() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.entities = [
+ {
+ id: 'entity:mesh:D003920',
+ canonicalName: 'Diabetes Mellitus',
+ ontology: 'MeSH',
+ identifier: 'D003920',
+ localizedNames: {
+ es: ['diabetes mellitus', { value: 'diabete mellitus' }]
+ }
+ }
+ ];
+ corpus.mentions = [
+ {
+ id: 'mention-diabetes-es',
+ documentId: 'paper-19',
+ text: 'diabetes mellitus',
+ language: 'es',
+ confidence: 0.94
+ }
+ ];
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-diabetes-es');
+ const diabetes = byId(result.entityPackets, 'entity:mesh:D003920');
+
+ assert.equal(event.decision, 'accept-canonical-entity');
+ assert.deepEqual(diabetes.localizedNames, { es: ['diabetes mellitus'] });
+ assert.deepEqual(diabetes.jsonLd.alternateName, ['diabetes mellitus']);
+ assert.deepEqual(diabetes.aliasEvidenceIssues, [
+ {
+ language: 'es',
+ reason: 'malformed-localized-name',
+ valueType: 'object'
+ }
+ ]);
+}
+
+function testMissingMentionListProducesEmptyAliasReview() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ delete corpus.mentions;
+
+ const result = evaluateAliasGuard(corpus);
+
+ assert.deepEqual(result.mentionDecisions, []);
+ assert.deepEqual(result.curatorActions, []);
+ assert.deepEqual(result.recommendationGuards.suppressedMentionIds, []);
+ assert.equal(result.summary.acceptedMentions, 0);
+ assert.equal(result.summary.heldMentions, 0);
+ assert.equal(result.summary.suppressedMentions, 0);
+ assert.equal(result.summary.entityPackets, corpus.entities.length);
+}
+
+function testMissingHomographPolicyDefaultsToEmptyPolicy() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ delete corpus.homographs;
+ corpus.mentions = [
+ {
+ id: 'mention-diabetes-without-homographs',
+ documentId: 'paper-16',
+ text: 'diabetes mellitus',
+ language: 'es',
+ confidence: 0.93
+ }
+ ];
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-diabetes-without-homographs');
+
+ assert.equal(event.decision, 'accept-canonical-entity');
+ assert.equal(event.reason, 'trusted-translated-alias');
+ assert.equal(event.candidateEntityId, 'entity:mesh:D003920');
+ assert.deepEqual(result.curatorActions, []);
+}
+
+function testMalformedMentionTextIsHeldForCuratorReview() {
+ const corpus = JSON.parse(JSON.stringify(buildSampleCorpus()));
+ corpus.mentions = [
+ {
+ id: 'mention-malformed-text',
+ documentId: 'paper-18',
+ text: { value: 'diabetes mellitus' },
+ language: 'es',
+ confidence: 0.94,
+ candidateEntityId: 'entity:mesh:D003920'
+ }
+ ];
+
+ const result = evaluateAliasGuard(corpus);
+ const event = byId(result.mentionDecisions, 'mention-malformed-text');
+ const action = byId(result.curatorActions, 'curate-mention-malformed-text');
+
+ assert.equal(event.decision, 'hold-for-curator-review');
+ assert.equal(event.reason, 'malformed-mention-text');
+ assert.equal(event.candidateEntityId, 'entity:mesh:D003920');
+ assert.deepEqual(event.candidateEntityIds, ['entity:mesh:D003920']);
+ assert.equal(action.priority, 'high');
+ assert.equal(action.action, 'review-multilingual-malformed-mention');
+ assert.equal(result.recommendationGuards.safeEntityIds.includes('entity:mesh:D003920'), false);
+}
+
+function testLanguageTaggedSynonymsArePreservedForEntityPages() {
+ const result = evaluateAliasGuard(buildSampleCorpus());
+ const diabetes = byId(result.entityPackets, 'entity:mesh:D003920');
+
+ assert.deepEqual(diabetes.localizedNames, {
+ en: ['diabetes mellitus'],
+ de: ['Diabetes mellitus'],
+ es: ['diabetes mellitus']
+ });
+ assert.equal(diabetes.schemaOrg.about.length, 3);
+}
+
+function testAuditDigestIsDeterministicAndPrivateFree() {
+ const result = evaluateAliasGuard(buildSampleCorpus());
+
+ assert.ok(result.auditDigest.startsWith('sha256:'));
+ assert.equal(result.summary.acceptedMentions, 6);
+ assert.equal(result.summary.heldMentions, 3);
+ assert.equal(result.summary.suppressedMentions, 1);
+ assert.ok(!JSON.stringify(result).includes('private@'));
+}
+
+const tests = [
+ testTrustedTranslatedAliasesBecomeCanonicalGraphNodes,
+ testFalseFriendMentionsAreHeldForCuratorReview,
+ testSameLanguageAliasCollisionsAreHeldForCuratorReview,
+ testConflictingExtractorCandidateAndAliasLookupAreHeld,
+ testUnicodeAndWhitespaceAliasesMatchCanonicalEntities,
+ testLanguageTagCaseDoesNotSuppressTrustedAliases,
+ testRegionalLanguageTagsUseBaseAliasLookup,
+ testRegionalLanguageTagsStillUseBaseHomographHolds,
+ testUnderscoreRegionalLanguageTagsUseBaseAliasAndHomographRules,
+ testMixedScriptLatinLanguageMentionsAreHeldForCuratorReview,
+ testLowercaseGreekLookalikeLatinMentionsAreHeldForCuratorReview,
+ testLowConfidenceAliasesDoNotDriveRecommendations,
+ testMissingConfidenceAliasesDoNotDriveRecommendations,
+ testMissingLocalizedNamesEmitEmptyEntityAliasPacket,
+ testMalformedLocalizedNameTermsAreOmittedFromAliasEvidence,
+ testMissingMentionListProducesEmptyAliasReview,
+ testMissingHomographPolicyDefaultsToEmptyPolicy,
+ testMalformedMentionTextIsHeldForCuratorReview,
+ testLanguageTaggedSynonymsArePreservedForEntityPages,
+ testAuditDigestIsDeterministicAndPrivateFree
+];
+
+for (const test of tests) {
+ test();
+}
+
+console.log(`${tests.length} multilingual entity alias guard tests passed`);