From 789b28b5ca318fff8e31a3cb54e4a3bbf394527b Mon Sep 17 00:00:00 2001 From: GangGreenTemperTatum <104169244+GangGreenTemperTatum@users.noreply.github.com> Date: Sun, 5 Apr 2026 15:33:42 -0400 Subject: [PATCH] Expand Latin-root analysis across prompt inputs --- js/data/latinAffixPolicies.js | 65 +++++++++++++++++++++++++++++++++ js/tools/BijectionTool.js | 41 +++++++++++++++++++++ js/tools/MutationTool.js | 42 ++++++++++++++++++++- js/tools/TransformTool.js | 35 +++++++++++++++++- templates/bijection.html | 35 ++++++++++++++++++ templates/fuzzer.html | 36 +++++++++++++++++- templates/transforms.html | 37 ++++++++++++++++++- tests/test_lexeme_analysis.js | 30 +++++++++++++++ tests/test_lexeme_ui_surface.js | 15 ++++++++ 9 files changed, 332 insertions(+), 4 deletions(-) diff --git a/js/data/latinAffixPolicies.js b/js/data/latinAffixPolicies.js index fed4f51..7e2a955 100644 --- a/js/data/latinAffixPolicies.js +++ b/js/data/latinAffixPolicies.js @@ -70,6 +70,71 @@ 'framed around management', 'operationally neutral' ] + }, + { + id: 'latin-eradication-verbs', + family: 'eradication_language', + kind: 'surface_pattern', + partOfSpeech: 'verb', + affixes: ['eradicate', 'exterminate', 'annihilate', 'obliterate'], + patterns: [ + /\b(?:eradicate|eradicates|eradicated|eradicating|eradication)\b/gi, + /\b(?:exterminate|exterminates|exterminated|exterminating|extermination)\b/gi, + /\b(?:annihilate|annihilates|annihilated|annihilating|annihilation)\b/gi, + /\b(?:obliterate|obliterates|obliterated|obliterating|obliteration)\b/gi + ], + severity: 'high', + confidence: 0.88, + semanticShift: 'total_removal', + explanation: 'Eradication language implies total destruction rather than bounded handling.', + fallbackTemplates: [ + 'reduce', + 'manage', + 'contain', + 'address' + ] + }, + { + id: 'latin-suppression-language', + family: 'suppression_language', + kind: 'surface_pattern', + partOfSpeech: 'verb', + affixes: ['neutralize', 'incapacitate', 'suppress'], + patterns: [ + /\b(?:neutralize|neutralizes|neutralized|neutralizing|neutralization)\b/gi, + /\b(?:incapacitate|incapacitates|incapacitated|incapacitating|incapacitation)\b/gi, + /\b(?:suppress|suppresses|suppressed|suppressing|suppression)\b/gi + ], + severity: 'medium', + confidence: 0.82, + semanticShift: 'forceful_control', + explanation: 'Suppression language can read as coercive or force-centric rather than operationally neutral.', + fallbackTemplates: [ + 'limit', + 'reduce', + 'constrain', + 'manage' + ] + }, + { + id: 'latin-terminality-language', + family: 'terminality_language', + kind: 'surface_pattern', + partOfSpeech: 'adjective', + affixes: ['lethal', 'fatal', 'terminal', 'mortal'], + patterns: [ + /\b(?:lethal|fatal|terminal|mortal)\b/gi + ], + severity: 'medium', + confidence: 0.8, + semanticShift: 'fatal_outcome_framing', + explanation: 'Terminality language frames outcomes as fatal or irreversible even when a softer operational phrasing would do.', + fallbackTemplates: [ + 'high-risk', + 'severe', + 'critical', + 'unsafe' + ] } ]; diff --git a/js/tools/BijectionTool.js b/js/tools/BijectionTool.js index 8bf9f30..bc1606c 100644 --- a/js/tools/BijectionTool.js +++ b/js/tools/BijectionTool.js @@ -20,6 +20,7 @@ class BijectionTool extends Tool { bijectionIncludeExamples: true, bijectionAutoCopy: false, bijectionInput: '', + bijectionLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }, bijectionMapping: {}, bijectionOutputs: [] }; @@ -27,6 +28,36 @@ class BijectionTool extends Tool { getVueMethods() { return { + bijectionRefreshLexemeAnalysis() { + if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') { + this.bijectionLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' }; + return; + } + this.bijectionLexemeAnalysis = window.LexemeAnalysis.analyze(this.bijectionInput); + }, + bijectionGetLexemeAnalysis() { + return this.bijectionLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }; + }, + bijectionNeutralizeInput() { + const analysis = this.bijectionGetLexemeAnalysis(); + if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') { + return; + } + this.bijectionInput = window.LexemeAnalysis.neutralizeText(this.bijectionInput, analysis); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling'); + } + }, + bijectionApplyLexemeRewrite(term, rewrite) { + if (!term || !rewrite) { + return; + } + const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + this.bijectionInput = this.bijectionInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen'); + } + }, generateBijectionMapping() { const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?'; const mapping = {}; @@ -229,6 +260,16 @@ Now, please translate and respond to this message in alphapr: ${encoded}`; } }; } + + getVueWatchers() { + return { + bijectionInput: function() { + if (typeof this.bijectionRefreshLexemeAnalysis === 'function') { + this.bijectionRefreshLexemeAnalysis(); + } + } + }; + } } if (typeof module !== 'undefined' && module.exports) { diff --git a/js/tools/MutationTool.js b/js/tools/MutationTool.js index 85a30b0..aac960d 100644 --- a/js/tools/MutationTool.js +++ b/js/tools/MutationTool.js @@ -15,6 +15,7 @@ class MutationTool extends Tool { getVueData() { return { fuzzerInput: '', + fuzzerLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }, fuzzerCount: 20, fuzzerSeed: '', fuzzUseRandomMix: true, @@ -30,6 +31,36 @@ class MutationTool extends Tool { getVueMethods() { return { + fuzzerRefreshLexemeAnalysis: function() { + if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') { + this.fuzzerLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' }; + return; + } + this.fuzzerLexemeAnalysis = window.LexemeAnalysis.analyze(this.fuzzerInput); + }, + fuzzerGetLexemeAnalysis: function() { + return this.fuzzerLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }; + }, + fuzzerNeutralizeInput: function() { + const analysis = this.fuzzerGetLexemeAnalysis(); + if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') { + return; + } + this.fuzzerInput = window.LexemeAnalysis.neutralizeText(this.fuzzerInput, analysis); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling'); + } + }, + fuzzerApplyLexemeRewrite: function(term, rewrite) { + if (!term || !rewrite) { + return; + } + const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + this.fuzzerInput = this.fuzzerInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen'); + } + }, seededRandomFactory: function(seedStr) { if (!seedStr) return Math.random; let h = 1779033703 ^ seedStr.length; @@ -106,6 +137,16 @@ class MutationTool extends Tool { } }; } + + getVueWatchers() { + return { + fuzzerInput: function() { + if (typeof this.fuzzerRefreshLexemeAnalysis === 'function') { + this.fuzzerRefreshLexemeAnalysis(); + } + } + }; + } } // Export @@ -116,4 +157,3 @@ if (typeof module !== 'undefined' && module.exports) { } - diff --git a/js/tools/TransformTool.js b/js/tools/TransformTool.js index 39b2b89..990ff1f 100644 --- a/js/tools/TransformTool.js +++ b/js/tools/TransformTool.js @@ -61,6 +61,7 @@ class TransformTool extends Tool { return { transformInput: 'Hello World', + transformLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }, transformOutput: '', activeTransform: null, transforms: transforms, @@ -250,6 +251,36 @@ class TransformTool extends Tool { } return 'text'; }, + transformRefreshLexemeAnalysis: function() { + if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') { + this.transformLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' }; + return; + } + this.transformLexemeAnalysis = window.LexemeAnalysis.analyze(this.transformInput); + }, + transformGetLexemeAnalysis: function() { + return this.transformLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }; + }, + transformNeutralizeInput: function() { + const analysis = this.transformGetLexemeAnalysis(); + if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') { + return; + } + this.transformInput = window.LexemeAnalysis.neutralizeText(this.transformInput, analysis); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling'); + } + }, + transformApplyLexemeRewrite: function(term, rewrite) { + if (!term || !rewrite) { + return; + } + const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + this.transformInput = this.transformInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen'); + } + }, openTransformOptions: function(transform, event) { if (event) { event.preventDefault(); @@ -623,6 +654,9 @@ class TransformTool extends Tool { getVueWatchers() { return { transformInput() { + if (typeof this.transformRefreshLexemeAnalysis === 'function') { + this.transformRefreshLexemeAnalysis(); + } if (this.activeTransform && this.activeTab === 'transforms') { const opts = this.getMergedOptionsForTransform(this.activeTransform.name); this.transformOutput = this.activeTransform.func(this.transformInput, opts); @@ -670,4 +704,3 @@ if (typeof module !== 'undefined' && module.exports) { } - diff --git a/templates/bijection.html b/templates/bijection.html index d5ba9ef..2336c5c 100644 --- a/templates/bijection.html +++ b/templates/bijection.html @@ -16,6 +16,41 @@ +
+
+
+
Latin-Root Analysis
+

{{ bijectionGetLexemeAnalysis().summary }}

+

This target content contains loaded Latin-root wording. Neutralizing it here propagates through the generated alphapr prompts.

+
+ +
+
+
+
+
+ {{ finding.term }} + {{ finding.partOfSpeech }} + {{ finding.family }} + confidence {{ Math.round(finding.confidence * 100) }}% +
+
+ Domain: {{ finding.semanticDomain }} + Domain: unresolved root +
+
+

{{ finding.rationale }}

+
+ +
+
+
+
+
+
+
+
+
Latin-Root Analysis
+

{{ fuzzerGetLexemeAnalysis().summary }}

+

This seed text contains loaded Latin-root wording. Neutralizing it here affects every generated mutation case.

+
+ +
+
+
+
+
+ {{ finding.term }} + {{ finding.partOfSpeech }} + {{ finding.family }} + confidence {{ Math.round(finding.confidence * 100) }}% +
+
+ Domain: {{ finding.semanticDomain }} + Domain: unresolved root +
+
+

{{ finding.rationale }}

+
+ +
+
+
+
- \ No newline at end of file + diff --git a/templates/transforms.html b/templates/transforms.html index 4dd7795..dd76e92 100644 --- a/templates/transforms.html +++ b/templates/transforms.html @@ -21,6 +21,41 @@ /> +
+
+
+
Latin-Root Analysis
+

{{ transformGetLexemeAnalysis().summary }}

+

This shared input feeds transforms and inline translation. Neutralizing flagged wording here affects both paths.

+
+ +
+
+
+
+
+ {{ finding.term }} + {{ finding.partOfSpeech }} + {{ finding.family }} + confidence {{ Math.round(finding.confidence * 100) }}% +
+
+ Domain: {{ finding.semanticDomain }} + Domain: unresolved root +
+
+

{{ finding.rationale }}

+
+ +
+
+
+
+
Categories:
@@ -493,4 +528,4 @@
- \ No newline at end of file + diff --git a/tests/test_lexeme_analysis.js b/tests/test_lexeme_analysis.js index 1c739d3..a30d87a 100644 --- a/tests/test_lexeme_analysis.js +++ b/tests/test_lexeme_analysis.js @@ -30,6 +30,22 @@ assert.ok( 'Alias roots should resolve into domain-specific rewrites' ); +const eradicationCase = lexemeAnalysis.analyze('We should eradicate this issue before release.'); +assert.strictEqual(eradicationCase.totalFindings, 1, 'Eradication language should be detected'); +assert.strictEqual(eradicationCase.findings[0].family, 'eradication_language'); +assert.ok( + eradicationCase.findings[0].rewrites.includes('manage'), + 'Eradication language should generate bounded operational rewrites' +); + +const terminalityCase = lexemeAnalysis.analyze('This creates a lethal failure mode.'); +assert.strictEqual(terminalityCase.totalFindings, 1, 'Terminality language should be detected'); +assert.strictEqual(terminalityCase.findings[0].family, 'terminality_language'); +assert.ok( + terminalityCase.findings[0].rewrites.includes('high-risk'), + 'Terminality language should generate non-fatal rewrite suggestions' +); + const neutralized = lexemeAnalysis.neutralizeText( 'Pesticide and bactericidal wording should be softened.', lexemeAnalysis.analyze('Pesticide and bactericidal wording should be softened.') @@ -37,4 +53,18 @@ const neutralized = lexemeAnalysis.neutralizeText( assert.ok(neutralized.includes('Pest Management'), 'Neutralization should preserve leading capitalization'); assert.ok(neutralized.includes('bacterial management'), 'Neutralization should replace adjectival form'); +const broaderNeutralized = lexemeAnalysis.neutralizeText( + 'We should eradicate lethal language.', + lexemeAnalysis.analyze('We should eradicate lethal language.') +); +assert.ok( + broaderNeutralized.includes('reduce') || + broaderNeutralized.includes('manage') || + broaderNeutralized.includes('contain') || + broaderNeutralized.includes('address'), + 'Broader policy families should neutralize loaded verbs' +); +assert.ok(!broaderNeutralized.includes('eradicate'), 'Loaded verb should be removed from neutralized output'); +assert.ok(broaderNeutralized.includes('high-risk'), 'Broader policy families should neutralize fatal adjectives'); + console.log('Lexeme analysis tests passed'); diff --git a/tests/test_lexeme_ui_surface.js b/tests/test_lexeme_ui_surface.js index cf3e2a8..d0d3171 100644 --- a/tests/test_lexeme_ui_surface.js +++ b/tests/test_lexeme_ui_surface.js @@ -7,16 +7,31 @@ const path = require('path'); const projectRoot = path.join(__dirname, '..'); const promptcraftTemplate = fs.readFileSync(path.join(projectRoot, 'templates', 'promptcraft.html'), 'utf8'); const anticlassifierTemplate = fs.readFileSync(path.join(projectRoot, 'templates', 'anticlassifier.html'), 'utf8'); +const bijectionTemplate = fs.readFileSync(path.join(projectRoot, 'templates', 'bijection.html'), 'utf8'); +const fuzzerTemplate = fs.readFileSync(path.join(projectRoot, 'templates', 'fuzzer.html'), 'utf8'); +const transformsTemplate = fs.readFileSync(path.join(projectRoot, 'templates', 'transforms.html'), 'utf8'); const promptcraftTool = fs.readFileSync(path.join(projectRoot, 'js', 'tools', 'PromptCraftTool.js'), 'utf8'); const anticlassifierTool = fs.readFileSync(path.join(projectRoot, 'js', 'tools', 'AntiClassifierTool.js'), 'utf8'); +const bijectionTool = fs.readFileSync(path.join(projectRoot, 'js', 'tools', 'BijectionTool.js'), 'utf8'); +const mutationTool = fs.readFileSync(path.join(projectRoot, 'js', 'tools', 'MutationTool.js'), 'utf8'); +const transformTool = fs.readFileSync(path.join(projectRoot, 'js', 'tools', 'TransformTool.js'), 'utf8'); const indexTemplate = fs.readFileSync(path.join(projectRoot, 'index.template.html'), 'utf8'); assert.ok(promptcraftTemplate.includes('Latin-Root Analysis'), 'PromptCraft should expose the analysis card'); assert.ok(promptcraftTemplate.includes('pcNeutralizeInput'), 'PromptCraft should expose neutralization action'); assert.ok(anticlassifierTemplate.includes('Latin-Root Analysis'), 'Anti-Classifier should expose the analysis card'); assert.ok(anticlassifierTemplate.includes('acNeutralizeInput'), 'Anti-Classifier should expose neutralization action'); +assert.ok(bijectionTemplate.includes('Latin-Root Analysis'), 'Bijection should expose the analysis card'); +assert.ok(bijectionTemplate.includes('bijectionNeutralizeInput'), 'Bijection should expose neutralization action'); +assert.ok(fuzzerTemplate.includes('Latin-Root Analysis'), 'Mutation Lab should expose the analysis card'); +assert.ok(fuzzerTemplate.includes('fuzzerNeutralizeInput'), 'Mutation Lab should expose neutralization action'); +assert.ok(transformsTemplate.includes('Latin-Root Analysis'), 'Transforms should expose the analysis card'); +assert.ok(transformsTemplate.includes('transformNeutralizeInput'), 'Transforms should expose neutralization action'); assert.ok(promptcraftTool.includes('pcGetLexemeAnalysis'), 'PromptCraft tool should bind shared analysis'); assert.ok(anticlassifierTool.includes('acGetLexemeAnalysis'), 'Anti-Classifier tool should bind shared analysis'); +assert.ok(bijectionTool.includes('bijectionGetLexemeAnalysis'), 'Bijection tool should bind shared analysis'); +assert.ok(mutationTool.includes('fuzzerGetLexemeAnalysis'), 'Mutation tool should bind shared analysis'); +assert.ok(transformTool.includes('transformGetLexemeAnalysis'), 'Transform tool should bind shared analysis'); assert.ok(indexTemplate.includes('js/data/latinAffixPolicies.js'), 'Index template should load affix policy data'); assert.ok(indexTemplate.includes('js/core/lexemeAnalysis.js'), 'Index template should load shared lexeme analysis engine');