hummbl-bibliography 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.cascade/rules/hummbl-base120.md +107 -0
  2. package/.github/CODEOWNERS +17 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +24 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +10 -0
  5. package/.github/ISSUE_TEMPLATE/new-entry.md +79 -0
  6. package/.github/ISSUE_TEMPLATE/quality-improvement.md +71 -0
  7. package/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  8. package/.github/dependabot.yml +17 -0
  9. package/.github/workflows/ci.yml +98 -0
  10. package/.github/workflows/doi-enrichment.yml +77 -0
  11. package/.github/workflows/security-audit.yml +92 -0
  12. package/.github/workflows/stats-report.yml +59 -0
  13. package/.github/workflows/validate-models.yml +194 -0
  14. package/.github/workflows/validate.yml +152 -0
  15. package/.husky/pre-commit +15 -0
  16. package/.husky/validation-rules.json +11 -0
  17. package/CHANGELOG.md +228 -0
  18. package/CONTRIBUTING.md +110 -0
  19. package/CONTRIBUTORS.md +257 -0
  20. package/DEVELOPMENT.md +110 -0
  21. package/Day_1_Audit_Worksheet.md +64 -0
  22. package/LICENSE +21 -0
  23. package/README.md +213 -0
  24. package/SECURITY.md +16 -0
  25. package/SITREP.md +141 -0
  26. package/bibliography/T10_collaboration.bib +281 -0
  27. package/bibliography/T11_security.bib +311 -0
  28. package/bibliography/T12_complexity.bib +272 -0
  29. package/bibliography/T13_reasoning.bib +231 -0
  30. package/bibliography/T1_canonical.bib +236 -0
  31. package/bibliography/T2_empirical.bib +258 -0
  32. package/bibliography/T3_applied.bib +219 -0
  33. package/bibliography/T4_agentic.bib +281 -0
  34. package/bibliography/T5_engineering.bib +243 -0
  35. package/bibliography/T6_governance.bib +277 -0
  36. package/bibliography/T7_emerging.bib +228 -0
  37. package/bibliography/T8_cognition.bib +260 -0
  38. package/bibliography/T9_economics.bib +275 -0
  39. package/bibliography/hummbl-transformations.json +84 -0
  40. package/dist/unified-bibliography.json +5699 -0
  41. package/docs/CONTRIBUTING.md +240 -0
  42. package/docs/GAP_ANALYSIS.md +142 -0
  43. package/docs/MULTI_AGENT_COORDINATION_PROTOCOL.md +700 -0
  44. package/docs/QUALITY_AUDIT_REPORT.md +576 -0
  45. package/docs/QUALITY_STANDARDS.md +350 -0
  46. package/docs/TRANSFORMATION_GUIDE.md +337 -0
  47. package/docs/metrics/model-accuracy.md +150 -0
  48. package/governance/CAES_CANONICAL.sha256 +1 -0
  49. package/governance/CAES_SPEC.md +107 -0
  50. package/governance/CAES_VERSION +1 -0
  51. package/governance/lexicon/ALLOWLIST_POLICY.md +63 -0
  52. package/governance/lexicon/CANONICALIZATION.md +63 -0
  53. package/governance/lexicon/acronym.schema.json +153 -0
  54. package/governance/lexicon/acronym_allowlist.txt +237 -0
  55. package/governance/lexicon/acronyms.v0.2.json +2555 -0
  56. package/llms.txt +1105 -0
  57. package/mappings/arcana_citations.json +219 -0
  58. package/mappings/bki_evidence.json +384 -0
  59. package/package.json +25 -0
  60. package/reports/.gitkeep +0 -0
  61. package/reports/citation_graph.json +119335 -0
  62. package/scripts/add_nist_tags.py +437 -0
  63. package/scripts/annotate_dois.py +204 -0
  64. package/scripts/check_palace_aliases.py +200 -0
  65. package/scripts/ingest_to_open_brain.py +307 -0
  66. package/scripts/monthly-review.sh +166 -0
  67. package/scripts/setup-hooks.sh +107 -0
  68. package/scripts/test_check_palace_aliases.py +194 -0
  69. package/sources/bki.bib +57 -0
  70. package/sources/theoretical-foundations.bib +589 -0
  71. package/toolkit/README.md +360 -0
  72. package/toolkit/docs/generated/quick-reference.md +179 -0
  73. package/toolkit/package-lock.json +1140 -0
  74. package/toolkit/package.json +66 -0
  75. package/toolkit/scripts/check-memory-palace-aliases.js +230 -0
  76. package/toolkit/scripts/check-memory-palace-aliases.test.js +297 -0
  77. package/toolkit/scripts/generate-docs.js +223 -0
  78. package/toolkit/src/check-duplicates.js +225 -0
  79. package/toolkit/src/check-required-fields.js +138 -0
  80. package/toolkit/src/citation-graph.js +425 -0
  81. package/toolkit/src/extensions/beyondBase120Audit.ts +250 -0
  82. package/toolkit/src/extensions/memoryPalace.ts +438 -0
  83. package/toolkit/src/extract-keywords.js +190 -0
  84. package/toolkit/src/find-missing-dois.js +178 -0
  85. package/toolkit/src/fix-duplicates.js +140 -0
  86. package/toolkit/src/merge-entries.js +29 -0
  87. package/toolkit/src/query.js +281 -0
  88. package/toolkit/src/stats.js +244 -0
  89. package/toolkit/src/test-validation.js +117 -0
  90. package/toolkit/src/utils/modelRegistry.ts +193 -0
  91. package/toolkit/src/utils/monitorModels.ts +150 -0
  92. package/toolkit/src/utils/validateModelCode.ts +196 -0
  93. package/toolkit/src/validate.js +251 -0
  94. package/toolkit/src/watch.js +100 -0
  95. package/toolkit/tsconfig.json +25 -0
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import chalk from 'chalk';
7
+ import { Cite } from '@citation-js/core';
8
+ import '@citation-js/plugin-bibtex';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+ const args = process.argv.slice(2);
14
+ const bibDir = args[0] || '../bibliography';
15
+ const jsonOutput = args.includes('--json');
16
+
17
+ class BibStats {
18
+ constructor(bibDir) {
19
+ this.bibDir = path.resolve(bibDir);
20
+ this.stats = {
21
+ total: 0,
22
+ byTier: {},
23
+ byType: {},
24
+ transformations: { P: 0, IN: 0, CO: 0, DE: 0, RE: 0, SY: 0 },
25
+ quality: {
26
+ withDOI: 0,
27
+ withISBN: 0,
28
+ withAbstract: 0,
29
+ withKeywords: 0
30
+ }
31
+ };
32
+ }
33
+
34
+ parseBibTeXRaw(content) {
35
+ const rawEntries = {};
36
+ const entryRegex = /@\w+\{([^,]+),([^@]+?)(?=\n\})/gs;
37
+ let match;
38
+
39
+ while ((match = entryRegex.exec(content)) !== null) {
40
+ const key = match[1].trim();
41
+ const entryText = match[2];
42
+ const fields = {};
43
+
44
+ const lines = entryText.split('\n');
45
+ for (const line of lines) {
46
+ const fieldMatch = line.match(/^\s*(\w+)\s*=\s*\{(.+)\}\s*,?\s*$/);
47
+ if (fieldMatch) {
48
+ const [, fieldKey, value] = fieldMatch;
49
+ fields[fieldKey.toLowerCase()] = value.trim();
50
+ }
51
+ }
52
+
53
+ rawEntries[key] = fields;
54
+ }
55
+
56
+ return rawEntries;
57
+ }
58
+
59
+ extractTransformations(keywords) {
60
+ const transformations = new Set();
61
+ if (!keywords) return transformations;
62
+
63
+ const keywordStr = Array.isArray(keywords) ? keywords.join(', ') : keywords;
64
+ const matches = keywordStr.match(/HUMMBL:(P|IN|CO|DE|RE|SY)/g);
65
+
66
+ if (matches) {
67
+ matches.forEach(match => {
68
+ const trans = match.replace('HUMMBL:', '');
69
+ transformations.add(trans);
70
+ });
71
+ }
72
+
73
+ return transformations;
74
+ }
75
+
76
+ getTier(filename) {
77
+ const match = filename.match(/T(\d+)/);
78
+ if (match) return `T${match[1]}`;
79
+ return 'Unknown';
80
+ }
81
+
82
+ processEntry(entry, filename, rawEntry = {}) {
83
+ this.stats.total++;
84
+
85
+ // Count by tier
86
+ const tier = this.getTier(filename);
87
+ this.stats.byTier[tier] = (this.stats.byTier[tier] || 0) + 1;
88
+
89
+ // Count by type
90
+ const type = entry.type || 'unknown';
91
+ this.stats.byType[type] = (this.stats.byType[type] || 0) + 1;
92
+
93
+ // Count transformations from raw keywords
94
+ const keywords = rawEntry.keywords || '';
95
+ const transformations = this.extractTransformations(keywords);
96
+ transformations.forEach(trans => {
97
+ this.stats.transformations[trans]++;
98
+ });
99
+
100
+ // Quality metrics
101
+ if (entry.DOI) this.stats.quality.withDOI++;
102
+ if (entry.ISBN) this.stats.quality.withISBN++;
103
+
104
+ const abstract = rawEntry.abstract || '';
105
+ if (abstract && abstract.length >= 50) this.stats.quality.withAbstract++;
106
+ if (transformations.size > 0) this.stats.quality.withKeywords++;
107
+ }
108
+
109
+ async loadFile(filepath) {
110
+ const filename = path.basename(filepath);
111
+
112
+ try {
113
+ const content = fs.readFileSync(filepath, 'utf8');
114
+ const citation = new Cite(content, { forceType: '@bibtex/text' });
115
+ const entries = citation.data;
116
+
117
+ // Parse raw BibTeX for additional fields
118
+ const rawEntries = this.parseBibTeXRaw(content);
119
+
120
+ entries.forEach(entry => {
121
+ const rawEntry = rawEntries[entry.id] || {};
122
+ this.processEntry(entry, filename, rawEntry);
123
+ });
124
+
125
+ return entries.length;
126
+ } catch (err) {
127
+ if (!jsonOutput) {
128
+ console.error(chalk.red(`Error loading ${filename}: ${err.message}`));
129
+ }
130
+ return 0;
131
+ }
132
+ }
133
+
134
+ printReport() {
135
+ console.log('\n' + '='.repeat(60));
136
+ console.log(chalk.cyan.bold(' HUMMBL BIBLIOGRAPHY STATISTICS'));
137
+ console.log('='.repeat(60));
138
+
139
+ // Total entries
140
+ console.log(chalk.cyan('\n📊 OVERVIEW'));
141
+ console.log(chalk.white(`Total Entries: ${chalk.bold(this.stats.total)}`));
142
+
143
+ // By tier
144
+ console.log(chalk.cyan('\n📚 BY TIER'));
145
+ Object.entries(this.stats.byTier).forEach(([tier, count]) => {
146
+ const percentage = ((count / this.stats.total) * 100).toFixed(1);
147
+ const bar = '█'.repeat(Math.floor(count / 2));
148
+ console.log(chalk.white(` ${tier}: ${chalk.bold(count.toString().padStart(2))} (${percentage}%) ${chalk.blue(bar)}`));
149
+ });
150
+
151
+ // By type
152
+ console.log(chalk.cyan('\n📖 BY TYPE'));
153
+ Object.entries(this.stats.byType)
154
+ .sort((a, b) => b[1] - a[1])
155
+ .forEach(([type, count]) => {
156
+ const percentage = ((count / this.stats.total) * 100).toFixed(1);
157
+ console.log(chalk.white(` ${type.padEnd(15)}: ${chalk.bold(count.toString().padStart(2))} (${percentage}%)`));
158
+ });
159
+
160
+ // Transformations
161
+ console.log(chalk.cyan('\n🔄 HUMMBL TRANSFORMATIONS'));
162
+ const transNames = {
163
+ P: 'Perspective',
164
+ IN: 'Inversion',
165
+ CO: 'Composition',
166
+ DE: 'Decomposition',
167
+ RE: 'Recursion',
168
+ SY: 'Synthesis'
169
+ };
170
+
171
+ Object.entries(this.stats.transformations)
172
+ .sort((a, b) => b[1] - a[1])
173
+ .forEach(([trans, count]) => {
174
+ const bar = '█'.repeat(Math.floor(count / 2));
175
+ console.log(chalk.white(` ${trans.padEnd(2)} (${transNames[trans].padEnd(13)}): ${chalk.bold(count.toString().padStart(2))} ${chalk.green(bar)}`));
176
+ });
177
+
178
+ // Quality metrics
179
+ console.log(chalk.cyan('\n✨ QUALITY METRICS'));
180
+ const qualityMetrics = [
181
+ { label: 'Entries with DOI', count: this.stats.quality.withDOI },
182
+ { label: 'Entries with ISBN', count: this.stats.quality.withISBN },
183
+ { label: 'Entries with Abstract', count: this.stats.quality.withAbstract },
184
+ { label: 'Entries with HUMMBL Keywords', count: this.stats.quality.withKeywords }
185
+ ];
186
+
187
+ qualityMetrics.forEach(metric => {
188
+ const percentage = ((metric.count / this.stats.total) * 100).toFixed(1);
189
+ const color = percentage >= 80 ? 'green' : percentage >= 50 ? 'yellow' : 'red';
190
+ console.log(chalk.white(` ${metric.label.padEnd(30)}: ${chalk.bold(metric.count.toString().padStart(2))} (${chalk[color](percentage + '%')})`));
191
+ });
192
+
193
+ // Gap analysis
194
+ console.log(chalk.cyan('\n📈 GAP ANALYSIS'));
195
+ const avgTransformations = Object.values(this.stats.transformations).reduce((a, b) => a + b, 0) / 6;
196
+ const gaps = Object.entries(this.stats.transformations)
197
+ .filter(([, count]) => count < avgTransformations)
198
+ .sort((a, b) => a[1] - b[1]);
199
+
200
+ if (gaps.length > 0) {
201
+ console.log(chalk.yellow(` Average coverage: ${avgTransformations.toFixed(1)} entries per transformation`));
202
+ console.log(chalk.yellow(` Need more entries for: ${gaps.map(([t]) => t).join(', ')}`));
203
+ } else {
204
+ console.log(chalk.green(' ✓ Balanced transformation coverage'));
205
+ }
206
+
207
+ console.log('\n' + '='.repeat(60) + '\n');
208
+ }
209
+
210
+ async run() {
211
+ // Find all .bib files
212
+ const bibFiles = fs.readdirSync(this.bibDir)
213
+ .filter(f => f.endsWith('.bib'))
214
+ .map(f => path.join(this.bibDir, f));
215
+
216
+ if (bibFiles.length === 0) {
217
+ if (!jsonOutput) {
218
+ console.log(chalk.red('\n❌ No .bib files found in ' + this.bibDir));
219
+ }
220
+ process.exit(1);
221
+ }
222
+
223
+ // Load all files
224
+ for (const filepath of bibFiles) {
225
+ await this.loadFile(filepath);
226
+ }
227
+
228
+ // Output results
229
+ if (jsonOutput) {
230
+ console.log(JSON.stringify(this.stats, null, 2));
231
+ } else {
232
+ this.printReport();
233
+ }
234
+ }
235
+ }
236
+
237
+ // Run stats
238
+ const stats = new BibStats(bibDir);
239
+ stats.run().catch(err => {
240
+ if (!jsonOutput) {
241
+ console.error(chalk.red('Fatal error:'), err);
242
+ }
243
+ process.exit(1);
244
+ });
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test script for HUMMBL Base120 model validation and Memory Palace audit
5
+ * Run with: node src/test-validation.js
6
+ */
7
+
8
+ import { validateModelCode } from './utils/validateModelCode.js';
9
+ import { monitorModelReferences } from './utils/monitorModels.js';
10
+ import { isMemoryPalaceModel, lookupMemoryPalace, auditRegistry } from './extensions/memoryPalace.js';
11
+ import { auditBeyondBase120, scanForExtendedModels } from './extensions/beyondBase120Audit.js';
12
+
13
+ console.log('🧪 Testing HUMMBL Base120 Model Validation\n');
14
+
15
+ // Test valid models
16
+ console.log('✅ Testing valid model codes:');
17
+ const validTests = ['P1', 'IN15', 'CO7', 'DE3', 'RE20', 'SY5'];
18
+
19
+ validTests.forEach(code => {
20
+ const result = validateModelCode(code);
21
+ if (result.isValid) {
22
+ console.log(` ${code} → "${result.name}" (${result.transformation}) ✅`);
23
+ } else {
24
+ console.log(` ${code} → ERROR: ${result.error} ❌`);
25
+ }
26
+ });
27
+
28
+ console.log('\n❌ Testing invalid model codes:');
29
+ const invalidTests = ['P21', 'XX1', 'P0', 'IN', 'CO99'];
30
+
31
+ invalidTests.forEach(code => {
32
+ const result = validateModelCode(code);
33
+ console.log(` ${code} → ${result.error} ✅ (correctly rejected)`);
34
+ });
35
+
36
+ console.log('\n📚 Testing Memory Palace registry:');
37
+ const memoryPalaceTests = [
38
+ { term: 'Antifragility', expected: true },
39
+ { term: 'Black Swan', expected: true },
40
+ { term: 'OODA Loop', expected: true },
41
+ { term: 'Skin in the Game', expected: true },
42
+ { term: 'Aurelius Lens', expected: true },
43
+ { term: 'Mimetic Desire', expected: true },
44
+ { term: 'Belonging Infrastructure', expected: true },
45
+ { term: 'RandomFakeModel', expected: false },
46
+ { term: 'SomethingNotRegistered', expected: false },
47
+ ];
48
+
49
+ memoryPalaceTests.forEach(({ term, expected }) => {
50
+ const found = isMemoryPalaceModel(term);
51
+ const pass = found === expected;
52
+ const status = pass ? '✅' : '❌';
53
+ if (found) {
54
+ const entry = lookupMemoryPalace(term);
55
+ console.log(` "${term}" → registered (${entry.room}, ${entry.source_type ?? 'unset'}) ${status}`);
56
+ } else {
57
+ console.log(` "${term}" → not registered ${status}`);
58
+ }
59
+ });
60
+
61
+ console.log('\n🏛️ Memory Palace registry health:');
62
+ const health = auditRegistry();
63
+ console.log(` Total entries: ${health.totalEntries}`);
64
+ Object.entries(health.byRoom).forEach(([room, count]) => {
65
+ console.log(` ${room}: ${count}`);
66
+ });
67
+ if (health.duplicateSlugs.length > 0) {
68
+ console.log(` ❌ Duplicate slugs: ${health.duplicateSlugs.join(', ')}`);
69
+ } else {
70
+ console.log(` ✅ No duplicate slugs`);
71
+ }
72
+ if (health.duplicateNames.length > 0) {
73
+ console.log(` ❌ Duplicate names: ${health.duplicateNames.join(', ')}`);
74
+ } else {
75
+ console.log(` ✅ No duplicate names`);
76
+ }
77
+ if (health.missingSourceTypes.length > 0) {
78
+ console.log(` ⚠️ Missing source_type: ${health.missingSourceTypes.join(', ')}`);
79
+ } else {
80
+ console.log(` ✅ All entries have source_type`);
81
+ }
82
+
83
+ console.log('\n🔭 Testing Beyond-Base120 audit:');
84
+ const governedText = `
85
+ This framework applies Antifragility principles when designing for uncertainty.
86
+ The Scapegoat Mechanism reveals how communities handle mimetic crises.
87
+ The Aurelius Lens suggests governors should maintain continuous self-audit.
88
+ We use the OODA Loop for rapid decision cycles in dynamic environments.
89
+ `;
90
+
91
+ const audit = auditBeyondBase120(governedText);
92
+ console.log(` Terms scanned: ${audit.stats.total_terms_scanned}`);
93
+ console.log(` Registered: ${audit.stats.registered}`);
94
+ console.log(` Unregistered: ${audit.stats.unregistered}`);
95
+ if (audit.findings.length === 0) {
96
+ console.log(` ✅ All extended model references are registered`);
97
+ } else {
98
+ audit.findings.forEach(f => {
99
+ const icon = f.severity === 'ERROR' ? '❌' : '⚠️';
100
+ console.log(` ${icon} [${f.code}] ${f.term}: ${f.message}`);
101
+ });
102
+ }
103
+
104
+ console.log('\n📊 Testing runtime monitoring:');
105
+ const testOutput = `
106
+ Let's apply P1 (First Principles Framing) to break this down.
107
+ Using DE3 (Modularization) will help organize the code.
108
+ Don't forget IN2 (Premortem Analysis) before deployment.
109
+ Antifragility thinking applies here too.
110
+ `;
111
+
112
+ const monitoring = monitorModelReferences(testOutput);
113
+ console.log(`Valid references: ${monitoring.valid.join(', ')}`);
114
+ console.log(`Invalid references: ${monitoring.invalid.join(', ') || 'none'}`);
115
+ console.log(`Hallucinations: ${monitoring.hallucinations.join(', ') || 'none (governed extension audit handles this now)'}`);
116
+
117
+ console.log('\n🎉 Validation system ready for production use!');
@@ -0,0 +1,193 @@
1
+ /**
2
+ * HUMMBL Base120 Model Registry Service
3
+ * Centralized validation and lookup service for mental models
4
+ */
5
+
6
+ import { validateModelCode, BASE120_MODELS, TransformationType, ModelCode } from './validateModelCode.js';
7
+
8
+ export interface ModelDefinition {
9
+ code: ModelCode;
10
+ name: string;
11
+ transformation: TransformationType;
12
+ description: string;
13
+ example?: string;
14
+ }
15
+
16
+ // Singleton Model Registry
17
+ export class ModelRegistry {
18
+ private static instance: ModelRegistry;
19
+ private models: Map<ModelCode, ModelDefinition>;
20
+
21
+ private constructor() {
22
+ this.models = this.loadBase120();
23
+ }
24
+
25
+ static getInstance(): ModelRegistry {
26
+ if (!ModelRegistry.instance) {
27
+ ModelRegistry.instance = new ModelRegistry();
28
+ }
29
+ return ModelRegistry.instance;
30
+ }
31
+
32
+ /**
33
+ * Load all Base120 models into registry
34
+ */
35
+ private loadBase120(): Map<ModelCode, ModelDefinition> {
36
+ const registry = new Map<ModelCode, ModelDefinition>();
37
+
38
+ Object.entries(BASE120_MODELS).forEach(([transformation, models]) => {
39
+ models.forEach((name, index) => {
40
+ const code = `${transformation}${index + 1}` as ModelCode;
41
+ registry.set(code, {
42
+ code,
43
+ name,
44
+ transformation: transformation as TransformationType,
45
+ description: this.getDefaultDescription(code),
46
+ example: this.getDefaultExample(code)
47
+ });
48
+ });
49
+ });
50
+
51
+ return registry;
52
+ }
53
+
54
+ /**
55
+ * Validate a model code
56
+ */
57
+ validate(code: string): ReturnType<typeof validateModelCode> {
58
+ return validateModelCode(code);
59
+ }
60
+
61
+ /**
62
+ * Get model definition by code
63
+ */
64
+ getModel(code: ModelCode): ModelDefinition | null {
65
+ return this.models.get(code) || null;
66
+ }
67
+
68
+ /**
69
+ * Search models by name or description
70
+ */
71
+ search(query: string): ModelCode[] {
72
+ const results: ModelCode[] = [];
73
+ const lowerQuery = query.toLowerCase();
74
+
75
+ this.models.forEach((def, code) => {
76
+ if (def.name.toLowerCase().includes(lowerQuery) ||
77
+ def.description.toLowerCase().includes(lowerQuery)) {
78
+ results.push(code);
79
+ }
80
+ });
81
+
82
+ return results;
83
+ }
84
+
85
+ /**
86
+ * Get all models for a transformation
87
+ */
88
+ getTransformationModels(transformation: TransformationType): ModelDefinition[] {
89
+ const models: ModelDefinition[] = [];
90
+ this.models.forEach(def => {
91
+ if (def.transformation === transformation) {
92
+ models.push(def);
93
+ }
94
+ });
95
+ return models;
96
+ }
97
+
98
+ /**
99
+ * Get all models
100
+ */
101
+ getAllModels(): ModelDefinition[] {
102
+ return Array.from(this.models.values());
103
+ }
104
+
105
+ /**
106
+ * Comprehensive audit of text
107
+ */
108
+ auditText(text: string): {
109
+ references: Array<{code: string, definition?: ModelDefinition, isValid: boolean}>;
110
+ hallucinations: string[];
111
+ validationErrors: string[];
112
+ } {
113
+ const references = this.extractReferences(text);
114
+ const hallucinations = this.detectHallucinations(text);
115
+ const validationErrors = this.findValidationErrors(text);
116
+
117
+ return { references, hallucinations, validationErrors };
118
+ }
119
+
120
+ /**
121
+ * Extract model references from text
122
+ */
123
+ private extractReferences(text: string): Array<{code: string, definition?: ModelDefinition, isValid: boolean}> {
124
+ const modelPattern = /(P|IN|CO|DE|RE|SY)(\d+)/g;
125
+ const matches = [...text.matchAll(modelPattern)];
126
+
127
+ return matches.map(([fullMatch, transformation, num]) => {
128
+ const code = `${transformation}${num}`;
129
+ const result = validateModelCode(code);
130
+
131
+ return {
132
+ code,
133
+ definition: result.isValid ? this.models.get(code as ModelCode) : undefined,
134
+ isValid: result.isValid
135
+ };
136
+ });
137
+ }
138
+
139
+ /**
140
+ * Hallucination detection removed — use beyondBase120Audit.ts (Memory Palace)
141
+ * for extended model drift detection. No-op retained for interface compat.
142
+ */
143
+ private detectHallucinations(_text: string): string[] {
144
+ return [];
145
+ }
146
+
147
+ /**
148
+ * Find validation errors
149
+ */
150
+ private findValidationErrors(text: string): string[] {
151
+ const errors: string[] = [];
152
+ const references = this.extractReferences(text);
153
+
154
+ references.forEach(ref => {
155
+ if (!ref.isValid) {
156
+ errors.push(`Invalid model code: ${ref.code}`);
157
+ }
158
+ });
159
+
160
+ return errors;
161
+ }
162
+
163
+ /**
164
+ * Get default description for a model (placeholder)
165
+ */
166
+ private getDefaultDescription(code: ModelCode): string {
167
+ // In a full implementation, this would have detailed descriptions
168
+ // For now, return a basic description
169
+ const result = validateModelCode(code);
170
+ return result.name ? `HUMMBL Base120 model: ${result.name}` : 'Unknown model';
171
+ }
172
+
173
+ /**
174
+ * Get default example for a model (placeholder)
175
+ */
176
+ private getDefaultExample(code: ModelCode): string {
177
+ // In a full implementation, this would have usage examples
178
+ return `Example usage of ${code}`;
179
+ }
180
+
181
+ /**
182
+ * Health check
183
+ */
184
+ healthCheck(): { status: 'healthy' | 'unhealthy', modelCount: number } {
185
+ const modelCount = this.models.size;
186
+ const expectedCount = 120; // 6 transformations × 20 models each
187
+
188
+ return {
189
+ status: modelCount === expectedCount ? 'healthy' : 'unhealthy',
190
+ modelCount
191
+ };
192
+ }
193
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Runtime monitoring for HUMMBL Base120 model references
3
+ * Prevents hallucination by monitoring agent outputs in real-time
4
+ */
5
+
6
+ import { validateModelCode, ModelCode } from './validateModelCode.js';
7
+
8
+ export interface AuditReport {
9
+ references: ModelReference[];
10
+ hallucinations: string[];
11
+ validationErrors: string[];
12
+ }
13
+
14
+ export interface ModelReference {
15
+ code: string;
16
+ name?: string;
17
+ isValid: boolean;
18
+ position: number;
19
+ }
20
+
21
+ /**
22
+ * Monitors agent output for mental model references and hallucinations
23
+ */
24
+ export function monitorModelReferences(output: string): {
25
+ valid: ModelCode[];
26
+ invalid: string[];
27
+ hallucinations: string[];
28
+ } {
29
+ const modelPattern = /(P|IN|CO|DE|RE|SY)(\d+)/g;
30
+ const matches = [...output.matchAll(modelPattern)];
31
+
32
+ const valid: ModelCode[] = [];
33
+ const invalid: string[] = [];
34
+
35
+ matches.forEach(([fullMatch, transformation, num]) => {
36
+ const code = `${transformation}${num}`;
37
+ const result = validateModelCode(code);
38
+
39
+ if (result.isValid) {
40
+ valid.push(code as ModelCode);
41
+ } else {
42
+ invalid.push(code);
43
+ }
44
+ });
45
+
46
+ // Check for hallucinated terms
47
+ const hallucinations = detectHallucinations(output);
48
+
49
+ return { valid, invalid, hallucinations };
50
+ }
51
+
52
+ /**
53
+ * Comprehensive audit of text for model compliance
54
+ */
55
+ export function auditText(text: string): AuditReport {
56
+ const references = extractReferences(text);
57
+ const hallucinations = detectHallucinations(text);
58
+ const validationErrors = findValidationErrors(text);
59
+
60
+ return {
61
+ references,
62
+ hallucinations,
63
+ validationErrors
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Extract all model references from text
69
+ */
70
+ function extractReferences(text: string): ModelReference[] {
71
+ const modelPattern = /(P|IN|CO|DE|RE|SY)(\d+)/g;
72
+ const matches = [...text.matchAll(modelPattern)];
73
+
74
+ return matches.map((match, index) => {
75
+ const code = `${match[1]}${match[2]}`;
76
+ const result = validateModelCode(code);
77
+
78
+ return {
79
+ code,
80
+ name: result.name,
81
+ isValid: result.isValid,
82
+ position: match.index || 0
83
+ };
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Detect hallucinated mental model references.
89
+ * Governed ARCANA models and BaseN extensions are excluded from hallucination
90
+ * detection — they are first-class governed models, not noise.
91
+ */
92
+ /**
93
+ * Hallucination detection removed — use beyondBase120Audit.ts (Memory Palace)
94
+ * for extended model drift detection. No-op retained for interface compat.
95
+ */
96
+ function detectHallucinations(_text: string): string[] {
97
+ return [];
98
+ }
99
+
100
+ /**
101
+ * Find validation errors in model references
102
+ */
103
+ function findValidationErrors(text: string): string[] {
104
+ const errors: string[] = [];
105
+ const references = extractReferences(text);
106
+
107
+ references.forEach(ref => {
108
+ if (!ref.isValid) {
109
+ errors.push(`Invalid model code: ${ref.code}`);
110
+ }
111
+ });
112
+
113
+ return errors;
114
+ }
115
+
116
+ /**
117
+ * Check if output passes model validation
118
+ */
119
+ export function passesValidation(output: string): boolean {
120
+ const { invalid, hallucinations } = monitorModelReferences(output);
121
+ return invalid.length === 0 && hallucinations.length === 0;
122
+ }
123
+
124
+ /**
125
+ * Generate validation report for logging
126
+ */
127
+ export function generateValidationReport(output: string): string {
128
+ const audit = auditText(output);
129
+ const { valid, invalid, hallucinations } = monitorModelReferences(output);
130
+
131
+ let report = 'MENTAL MODEL VALIDATION REPORT\n';
132
+ report += '='.repeat(40) + '\n\n';
133
+
134
+ report += `Valid references (${valid.length}): ${valid.join(', ') || 'None'}\n`;
135
+ report += `Invalid references (${invalid.length}): ${invalid.join(', ') || 'None'}\n`;
136
+ report += `Hallucinations detected (${hallucinations.length}): ${hallucinations.join(', ') || 'None'}\n\n`;
137
+
138
+ if (audit.validationErrors.length > 0) {
139
+ report += 'Validation Errors:\n';
140
+ audit.validationErrors.forEach(error => {
141
+ report += `- ${error}\n`;
142
+ });
143
+ report += '\n';
144
+ }
145
+
146
+ const overallStatus = passesValidation(output) ? '✅ PASS' : '❌ FAIL';
147
+ report += `Overall Status: ${overallStatus}\n`;
148
+
149
+ return report;
150
+ }