euparliamentmonitor 0.8.19 → 0.8.21
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.
- package/package.json +7 -7
- package/scripts/constants/language-articles.d.ts +4 -0
- package/scripts/constants/language-articles.js +20 -0
- package/scripts/constants/language-ui.d.ts +8 -8
- package/scripts/constants/language-ui.js +64 -64
- package/scripts/constants/languages.d.ts +2 -2
- package/scripts/constants/languages.js +2 -2
- package/scripts/generators/news-enhanced.js +13 -3
- package/scripts/generators/pipeline/analysis-classification.d.ts +49 -0
- package/scripts/generators/pipeline/analysis-classification.js +333 -0
- package/scripts/generators/pipeline/analysis-existing.d.ts +67 -0
- package/scripts/generators/pipeline/analysis-existing.js +547 -0
- package/scripts/generators/pipeline/analysis-helpers.d.ts +140 -0
- package/scripts/generators/pipeline/analysis-helpers.js +266 -0
- package/scripts/generators/pipeline/analysis-risk.d.ts +49 -0
- package/scripts/generators/pipeline/analysis-risk.js +417 -0
- package/scripts/generators/pipeline/analysis-stage.d.ts +19 -39
- package/scripts/generators/pipeline/analysis-stage.js +219 -1704
- package/scripts/generators/pipeline/analysis-threats.d.ts +41 -0
- package/scripts/generators/pipeline/analysis-threats.js +142 -0
- package/scripts/generators/pipeline/fetch-stage.d.ts +25 -15
- package/scripts/generators/pipeline/fetch-stage.js +293 -117
- package/scripts/generators/strategies/article-strategy.d.ts +126 -7
- package/scripts/generators/strategies/article-strategy.js +491 -1
- package/scripts/generators/strategies/breaking-news-strategy.js +98 -8
- package/scripts/generators/strategies/committee-reports-strategy.js +23 -2
- package/scripts/generators/strategies/month-ahead-strategy.js +23 -2
- package/scripts/generators/strategies/monthly-review-strategy.js +13 -1
- package/scripts/generators/strategies/motions-strategy.js +15 -1
- package/scripts/generators/strategies/propositions-strategy.js +15 -1
- package/scripts/generators/strategies/week-ahead-strategy.js +19 -1
- package/scripts/generators/strategies/weekly-review-strategy.js +17 -1
- package/scripts/generators/synthesis-summary.d.ts +93 -0
- package/scripts/generators/synthesis-summary.js +364 -0
- package/scripts/index.d.ts +5 -2
- package/scripts/index.js +6 -1
- package/scripts/mcp/ep-mcp-client.d.ts +34 -1
- package/scripts/mcp/ep-mcp-client.js +110 -2
- package/scripts/mcp/mcp-connection.d.ts +3 -1
- package/scripts/mcp/mcp-connection.js +35 -4
- package/scripts/templates/article-template.js +24 -22
- package/scripts/templates/section-builders.js +2 -5
- package/scripts/types/index.d.ts +2 -1
- package/scripts/types/mcp.d.ts +7 -0
- package/scripts/types/political-classification.d.ts +1 -1
- package/scripts/types/quality.d.ts +9 -6
- package/scripts/types/significance.d.ts +130 -0
- package/scripts/types/significance.js +4 -0
- package/scripts/utils/article-quality-scorer.d.ts +13 -11
- package/scripts/utils/article-quality-scorer.js +36 -23
- package/scripts/utils/file-utils.d.ts +2 -2
- package/scripts/utils/file-utils.js +2 -2
- package/scripts/utils/html-sanitize.d.ts +10 -0
- package/scripts/utils/html-sanitize.js +32 -0
- package/scripts/utils/political-classification.d.ts +8 -7
- package/scripts/utils/political-classification.js +8 -7
- package/scripts/utils/political-risk-assessment.d.ts +1 -1
- package/scripts/utils/political-risk-assessment.js +1 -1
- package/scripts/utils/significance-scoring.d.ts +97 -0
- package/scripts/utils/significance-scoring.js +190 -0
|
@@ -19,8 +19,22 @@ const BINARY_NAME = 'european-parliament-mcp-server';
|
|
|
19
19
|
const BINARY_FILE = process.platform === 'win32' ? `${BINARY_NAME}.cmd` : BINARY_NAME;
|
|
20
20
|
/** Default binary resolved from node_modules/.bin relative to this file's compiled location */
|
|
21
21
|
const DEFAULT_SERVER_BINARY = resolve(dirname(fileURLToPath(import.meta.url)), `../../node_modules/.bin/${BINARY_FILE}`);
|
|
22
|
-
/**
|
|
23
|
-
const
|
|
22
|
+
/** Default request timeout in milliseconds — EU Parliament API responses commonly take 30-120+ seconds for large datasets */
|
|
23
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 180_000;
|
|
24
|
+
/**
|
|
25
|
+
* Effective request timeout, configurable via `EP_REQUEST_TIMEOUT_MS` env var.
|
|
26
|
+
* This keeps the client-side timeout aligned with the MCP server timeout set
|
|
27
|
+
* in workflow configs and copilot-mcp.json.
|
|
28
|
+
*/
|
|
29
|
+
const REQUEST_TIMEOUT_MS = (() => {
|
|
30
|
+
const envVal = process.env['EP_REQUEST_TIMEOUT_MS'];
|
|
31
|
+
if (envVal) {
|
|
32
|
+
const parsed = Number(envVal);
|
|
33
|
+
if (!Number.isNaN(parsed) && parsed > 0)
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
return DEFAULT_REQUEST_TIMEOUT_MS;
|
|
37
|
+
})();
|
|
24
38
|
/** Connection startup delay in milliseconds */
|
|
25
39
|
const CONNECTION_STARTUP_DELAY_MS = 500;
|
|
26
40
|
/** Maximum reconnect back-off delay in milliseconds */
|
|
@@ -75,7 +89,8 @@ function parseRetryAfterMs(retryAfter) {
|
|
|
75
89
|
}
|
|
76
90
|
/**
|
|
77
91
|
* Returns true only for transient, retriable failures: request timeouts,
|
|
78
|
-
* network-level connection-closed/reset errors,
|
|
92
|
+
* network-level connection-closed/reset errors, "not connected" states,
|
|
93
|
+
* and transient HTTP gateway errors (502, 503, 504).
|
|
79
94
|
*
|
|
80
95
|
* Uses an allow-list of known transient error patterns so that unknown or
|
|
81
96
|
* server-level errors (e.g., tool runtime failures) are NOT retried:
|
|
@@ -83,6 +98,7 @@ function parseRetryAfterMs(retryAfter) {
|
|
|
83
98
|
* - connection closed / reset / refused — network-level transport failures
|
|
84
99
|
* - not connected — local "not yet connected" guard error
|
|
85
100
|
* - socket hang up — Node.js HTTP socket-level disconnection
|
|
101
|
+
* - gateway error 502/503/504 — transient upstream server errors
|
|
86
102
|
*
|
|
87
103
|
* Everything else (MCPSessionExpiredError, TypeError, rate-limit errors,
|
|
88
104
|
* unknown errors) returns false so `callToolWithRetry` surfaces them immediately.
|
|
@@ -108,7 +124,11 @@ export function isRetriableError(error) {
|
|
|
108
124
|
msg.includes('not connected') ||
|
|
109
125
|
msg.includes('econnreset') ||
|
|
110
126
|
msg.includes('econnrefused') ||
|
|
111
|
-
msg.includes('socket hang up')
|
|
127
|
+
msg.includes('socket hang up') ||
|
|
128
|
+
// Transient upstream gateway errors — safe to retry with backoff
|
|
129
|
+
msg.includes('gateway error 502') ||
|
|
130
|
+
msg.includes('gateway error 503') ||
|
|
131
|
+
msg.includes('gateway error 504'));
|
|
112
132
|
}
|
|
113
133
|
/**
|
|
114
134
|
* Parse a `Retry-After` or `X-Retry-After` header value (which may be either a
|
|
@@ -462,8 +482,16 @@ export class MCPConnection {
|
|
|
462
482
|
const isJavaScriptFile = this.serverPath.toLowerCase().endsWith('.js');
|
|
463
483
|
const command = isJavaScriptFile ? process.execPath : this.serverPath;
|
|
464
484
|
const args = isJavaScriptFile ? [this.serverPath] : [];
|
|
485
|
+
// Ensure EP_REQUEST_TIMEOUT_MS is propagated to the MCP server subprocess.
|
|
486
|
+
// The EP MCP server defaults to only 10 seconds; we need 90+ seconds for
|
|
487
|
+
// slow EP API feed endpoints (events, procedures, documents, etc.).
|
|
488
|
+
const childEnv = { ...process.env };
|
|
489
|
+
if (!childEnv['EP_REQUEST_TIMEOUT_MS']) {
|
|
490
|
+
childEnv['EP_REQUEST_TIMEOUT_MS'] = String(REQUEST_TIMEOUT_MS);
|
|
491
|
+
}
|
|
465
492
|
this.process = spawn(command, args, {
|
|
466
493
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
494
|
+
env: childEnv,
|
|
467
495
|
});
|
|
468
496
|
let buffer = '';
|
|
469
497
|
let startupError = null;
|
|
@@ -571,6 +599,9 @@ export class MCPConnection {
|
|
|
571
599
|
const statusText = response.statusText || 'Too Many Requests';
|
|
572
600
|
throw new MCPRateLimitError(0, `${RATE_LIMIT_MSG} (status ${response.status} ${statusText}; ${RETRY_AFTER_HEADER}/Retry-After header missing)`);
|
|
573
601
|
}
|
|
602
|
+
// Include the status code in the error message for classification by isRetriableError()
|
|
603
|
+
// and safeCallTool(). Diagnostic logging is intentionally omitted here because
|
|
604
|
+
// callToolWithRetry may retry 502/503/504 errors, and per-retry warnings would be noisy.
|
|
574
605
|
throw new Error(`Gateway error ${response.status}: ${response.statusText}`);
|
|
575
606
|
}
|
|
576
607
|
/**
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* @description Generates HTML templates for news articles with proper structure and metadata
|
|
6
6
|
*/
|
|
7
7
|
import { createHash } from 'crypto';
|
|
8
|
-
import { ALL_LANGUAGES, LANGUAGE_FLAGS, LANGUAGE_NAMES, ARTICLE_TYPE_LABELS, READ_TIME_LABELS, BACK_TO_NEWS_LABELS, ARTICLE_NAV_LABELS, SKIP_LINK_TEXTS, SOURCES_HEADING_LABELS, HEADER_SUBTITLE_LABELS, THEME_TOGGLE_LABELS, FOOTER_ABOUT_HEADING_LABELS, FOOTER_ABOUT_TEXT_LABELS, FOOTER_QUICK_LINKS_LABELS, FOOTER_BUILT_BY_LABELS, FOOTER_LANGUAGES_LABELS, ANALYSIS_TRANSPARENCY_LABELS, ANALYSIS_SUMMARY_LABELS, METHODOLOGY_LABELS, TRANSPARENCY_DISCLOSURE_LABELS, CLASSIFICATION_ANALYSIS_LABELS, THREAT_ASSESSMENT_LABELS, RISK_SCORING_LABELS, DEEP_ANALYSIS_LABELS, VIEW_SOURCE_LABELS, OPEN_SOURCE_NOTE_LABELS, AI_ANALYSIS_GUIDE_LABELS, SWOT_FRAMEWORK_LABELS, RISK_METHODOLOGY_LABELS, THREAT_FRAMEWORK_LABELS, CLASSIFICATION_GUIDE_LABELS, STYLE_GUIDE_LABELS,
|
|
8
|
+
import { ALL_LANGUAGES, LANGUAGE_FLAGS, LANGUAGE_NAMES, ARTICLE_TYPE_LABELS, READ_TIME_LABELS, BACK_TO_NEWS_LABELS, ARTICLE_NAV_LABELS, SKIP_LINK_TEXTS, SOURCES_HEADING_LABELS, HEADER_SUBTITLE_LABELS, THEME_TOGGLE_LABELS, FOOTER_ABOUT_HEADING_LABELS, FOOTER_ABOUT_TEXT_LABELS, FOOTER_QUICK_LINKS_LABELS, FOOTER_BUILT_BY_LABELS, FOOTER_LANGUAGES_LABELS, ANALYSIS_TRANSPARENCY_LABELS, ANALYSIS_SUMMARY_LABELS, METHODOLOGY_LABELS, TRANSPARENCY_DISCLOSURE_LABELS, CLASSIFICATION_ANALYSIS_LABELS, THREAT_ASSESSMENT_LABELS, RISK_SCORING_LABELS, DEEP_ANALYSIS_LABELS, VIEW_SOURCE_LABELS, OPEN_SOURCE_NOTE_LABELS, AI_ANALYSIS_GUIDE_LABELS, SWOT_FRAMEWORK_LABELS, RISK_METHODOLOGY_LABELS, THREAT_FRAMEWORK_LABELS, CLASSIFICATION_GUIDE_LABELS, STYLE_GUIDE_LABELS, SIGNIFICANCE_CLASSIFICATION_LABELS, ACTOR_MAPPING_LABELS, FORCES_ANALYSIS_LABELS, IMPACT_MATRIX_LABELS, POLITICAL_THREAT_LANDSCAPE_LABELS, ACTOR_THREAT_PROFILING_LABELS, CONSEQUENCE_TREES_LABELS, LEGISLATIVE_DISRUPTION_LABELS, RISK_MATRIX_LABELS, QUANTITATIVE_SWOT_LABELS, POLITICAL_CAPITAL_RISK_LABELS, LEGISLATIVE_VELOCITY_RISK_LABELS, AGENT_RISK_WORKFLOW_LABELS, STAKEHOLDER_IMPACT_LABELS, COALITION_DYNAMICS_LABELS, VOTING_PATTERNS_LABELS, CROSS_SESSION_INTELLIGENCE_LABELS, getLocalizedString, getTextDirection, } from '../constants/languages.js';
|
|
9
9
|
import { escapeHTML, isSafeURL } from '../utils/file-utils.js';
|
|
10
|
+
import { stripHtmlTags } from '../utils/html-sanitize.js';
|
|
10
11
|
import { APP_VERSION, createThemeToggleButton, THEME_TOGGLE_SCRIPT, THEME_TOGGLE_SCRIPT_CONTENT, } from '../constants/config.js';
|
|
11
12
|
/** Pattern for valid article dates (YYYY-MM-DD) */
|
|
12
13
|
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
|
|
@@ -16,6 +17,11 @@ const SLUG_PATTERN = /^[a-z0-9-]+$/u;
|
|
|
16
17
|
const SRI_HASH_PATTERN = /^sha(?:256|384|512)-[A-Za-z0-9+/]+={0,2}$/u;
|
|
17
18
|
/** Words per minute for read-time calculation */
|
|
18
19
|
const TEMPLATE_WORDS_PER_MINUTE = 250;
|
|
20
|
+
/**
|
|
21
|
+
* Base URL for the deployed site, constructed via the URL API so that CodeQL
|
|
22
|
+
* recognises it as a validated URL rather than a potential regex pattern.
|
|
23
|
+
*/
|
|
24
|
+
const SITE_BASE_URL = new URL('/euparliamentmonitor', 'https://hack23.github.io').href;
|
|
19
25
|
/**
|
|
20
26
|
* BCP47 / Open Graph locale mapping for og:locale meta tag.
|
|
21
27
|
* Maps our 2-letter language codes to proper BCP47 locale strings.
|
|
@@ -116,11 +122,7 @@ export function generateArticleHTML(options) {
|
|
|
116
122
|
const categoryLabel = categoryLabels[category] ?? category;
|
|
117
123
|
const readTimeFormatter = getLocalizedString(READ_TIME_LABELS, lang);
|
|
118
124
|
// Auto-compute read-time from content word count if not explicitly set
|
|
119
|
-
const contentWordCount = content
|
|
120
|
-
.replace(/<[^>]+>/gu, ' ')
|
|
121
|
-
.replace(/\s+/gu, ' ')
|
|
122
|
-
.trim()
|
|
123
|
-
.split(' ').length;
|
|
125
|
+
const contentWordCount = stripHtmlTags(content).replace(/\s+/gu, ' ').trim().split(' ').length;
|
|
124
126
|
const computedReadTime = Math.max(1, Math.ceil(contentWordCount / TEMPLATE_WORDS_PER_MINUTE));
|
|
125
127
|
const effectiveReadTime = readTime > 0 ? readTime : computedReadTime;
|
|
126
128
|
const readTimeLabel = readTimeFormatter(effectiveReadTime);
|
|
@@ -156,12 +158,12 @@ export function generateArticleHTML(options) {
|
|
|
156
158
|
publisher: {
|
|
157
159
|
'@type': 'Organization',
|
|
158
160
|
name: 'EU Parliament Monitor',
|
|
159
|
-
url:
|
|
161
|
+
url: SITE_BASE_URL,
|
|
160
162
|
},
|
|
161
163
|
keywords: keywords.join(', '),
|
|
162
164
|
mainEntityOfPage: {
|
|
163
165
|
'@type': 'WebPage',
|
|
164
|
-
'@id':
|
|
166
|
+
'@id': `${SITE_BASE_URL}/news/${date}-${slug}-${lang}.html`,
|
|
165
167
|
},
|
|
166
168
|
}, null, 4);
|
|
167
169
|
// Validate and escape stylesHash — only allow valid SRI hash format
|
|
@@ -220,10 +222,10 @@ export function generateArticleHTML(options) {
|
|
|
220
222
|
<meta property="og:type" content="article">
|
|
221
223
|
<meta property="og:title" content="${safeTitle}">
|
|
222
224
|
<meta property="og:description" content="${safeSubtitle}">
|
|
223
|
-
<meta property="og:url" content="
|
|
225
|
+
<meta property="og:url" content="${SITE_BASE_URL}/news/${date}-${slug}-${lang}.html">
|
|
224
226
|
<meta property="og:site_name" content="EU Parliament Monitor">
|
|
225
227
|
<meta property="og:locale" content="${OG_LOCALE_MAP[lang] ?? lang}">
|
|
226
|
-
<meta property="og:image" content="
|
|
228
|
+
<meta property="og:image" content="${SITE_BASE_URL}/images/og-image.jpg">
|
|
227
229
|
<meta property="og:image:width" content="1200">
|
|
228
230
|
<meta property="og:image:height" content="630">
|
|
229
231
|
<meta property="og:image:alt" content="EU Parliament Monitor — AI-Disrupted Parliamentary Intelligence">
|
|
@@ -232,10 +234,10 @@ export function generateArticleHTML(options) {
|
|
|
232
234
|
<meta name="twitter:card" content="summary_large_image">
|
|
233
235
|
<meta name="twitter:title" content="${safeTitle}">
|
|
234
236
|
<meta name="twitter:description" content="${safeSubtitle}">
|
|
235
|
-
<meta name="twitter:image" content="
|
|
237
|
+
<meta name="twitter:image" content="${SITE_BASE_URL}/images/og-image.jpg">
|
|
236
238
|
<meta name="twitter:image:alt" content="EU Parliament Monitor — AI-Disrupted Parliamentary Intelligence">
|
|
237
239
|
|
|
238
|
-
<link rel="canonical" href="
|
|
240
|
+
<link rel="canonical" href="${SITE_BASE_URL}/news/${date}-${slug}-${lang}.html">
|
|
239
241
|
<link rel="stylesheet" href="../styles.css"${safeSriAttrs}>
|
|
240
242
|
|
|
241
243
|
<!-- Schema.org structured data -->
|
|
@@ -415,16 +417,16 @@ function renderAnalysisTransparencySection(date, slug, lang, analysisDir) {
|
|
|
415
417
|
const styleGuideLabel = escapeHTML(getLocalizedString(STYLE_GUIDE_LABELS, lang));
|
|
416
418
|
const repoBase = 'https://github.com/Hack23/euparliamentmonitor/blob/main';
|
|
417
419
|
const treeDirBase = 'https://github.com/Hack23/euparliamentmonitor/tree/main';
|
|
418
|
-
const analysisDirUrl = `${treeDirBase}/analysis/${safeDate}/${safeAnalysisDirName}`;
|
|
419
|
-
const analysisFileBase = `${repoBase}/analysis/${safeDate}/${safeAnalysisDirName}`;
|
|
420
|
+
const analysisDirUrl = `${treeDirBase}/analysis/daily/${safeDate}/${safeAnalysisDirName}`;
|
|
421
|
+
const analysisFileBase = `${repoBase}/analysis/daily/${safeDate}/${safeAnalysisDirName}`;
|
|
420
422
|
const methodologyDir = `${repoBase}/analysis/methodologies`;
|
|
421
423
|
// Per-file localized link labels
|
|
422
|
-
const significanceLabel = escapeHTML(getLocalizedString(
|
|
424
|
+
const significanceLabel = escapeHTML(getLocalizedString(SIGNIFICANCE_CLASSIFICATION_LABELS, lang));
|
|
423
425
|
const actorMappingLabel = escapeHTML(getLocalizedString(ACTOR_MAPPING_LABELS, lang));
|
|
424
426
|
const forcesLabel = escapeHTML(getLocalizedString(FORCES_ANALYSIS_LABELS, lang));
|
|
425
427
|
const impactMatrixLabel = escapeHTML(getLocalizedString(IMPACT_MATRIX_LABELS, lang));
|
|
426
428
|
const threatLandscapeLabel = escapeHTML(getLocalizedString(POLITICAL_THREAT_LANDSCAPE_LABELS, lang));
|
|
427
|
-
const
|
|
429
|
+
const actorThreatProfilingLabel = escapeHTML(getLocalizedString(ACTOR_THREAT_PROFILING_LABELS, lang));
|
|
428
430
|
const consequenceLabel = escapeHTML(getLocalizedString(CONSEQUENCE_TREES_LABELS, lang));
|
|
429
431
|
const disruptionLabel = escapeHTML(getLocalizedString(LEGISLATIVE_DISRUPTION_LABELS, lang));
|
|
430
432
|
const riskMatrixLabel = escapeHTML(getLocalizedString(RISK_MATRIX_LABELS, lang));
|
|
@@ -433,8 +435,8 @@ function renderAnalysisTransparencySection(date, slug, lang, analysisDir) {
|
|
|
433
435
|
const legVelocityLabel = escapeHTML(getLocalizedString(LEGISLATIVE_VELOCITY_RISK_LABELS, lang));
|
|
434
436
|
const agentRiskLabel = escapeHTML(getLocalizedString(AGENT_RISK_WORKFLOW_LABELS, lang));
|
|
435
437
|
const deepAnalysisFileLabel = escapeHTML(getLocalizedString(DEEP_ANALYSIS_LABELS, lang));
|
|
436
|
-
const stakeholderLabel = escapeHTML(getLocalizedString(
|
|
437
|
-
const coalitionLabel = escapeHTML(getLocalizedString(
|
|
438
|
+
const stakeholderLabel = escapeHTML(getLocalizedString(STAKEHOLDER_IMPACT_LABELS, lang));
|
|
439
|
+
const coalitionLabel = escapeHTML(getLocalizedString(COALITION_DYNAMICS_LABELS, lang));
|
|
438
440
|
const votingPatternsLabel = escapeHTML(getLocalizedString(VOTING_PATTERNS_LABELS, lang));
|
|
439
441
|
const crossSessionLabel = escapeHTML(getLocalizedString(CROSS_SESSION_INTELLIGENCE_LABELS, lang));
|
|
440
442
|
return `
|
|
@@ -449,7 +451,7 @@ function renderAnalysisTransparencySection(date, slug, lang, analysisDir) {
|
|
|
449
451
|
</ul>
|
|
450
452
|
<h3><span aria-hidden="true">🏷️</span> ${classificationLabel}</h3>
|
|
451
453
|
<ul>
|
|
452
|
-
<li><a href="${analysisFileBase}/classification/significance-
|
|
454
|
+
<li><a href="${analysisFileBase}/classification/significance-classification.md" target="_blank" rel="noopener noreferrer">${significanceLabel}</a></li>
|
|
453
455
|
<li><a href="${analysisFileBase}/classification/actor-mapping.md" target="_blank" rel="noopener noreferrer">${actorMappingLabel}</a></li>
|
|
454
456
|
<li><a href="${analysisFileBase}/classification/forces-analysis.md" target="_blank" rel="noopener noreferrer">${forcesLabel}</a></li>
|
|
455
457
|
<li><a href="${analysisFileBase}/classification/impact-matrix.md" target="_blank" rel="noopener noreferrer">${impactMatrixLabel}</a></li>
|
|
@@ -457,7 +459,7 @@ function renderAnalysisTransparencySection(date, slug, lang, analysisDir) {
|
|
|
457
459
|
<h3><span aria-hidden="true">🛡️</span> ${threatLabel}</h3>
|
|
458
460
|
<ul>
|
|
459
461
|
<li><a href="${analysisFileBase}/threat-assessment/political-threat-landscape.md" target="_blank" rel="noopener noreferrer">${threatLandscapeLabel}</a></li>
|
|
460
|
-
<li><a href="${analysisFileBase}/threat-assessment/actor-threat-
|
|
462
|
+
<li><a href="${analysisFileBase}/threat-assessment/actor-threat-profiling.md" target="_blank" rel="noopener noreferrer">${actorThreatProfilingLabel}</a></li>
|
|
461
463
|
<li><a href="${analysisFileBase}/threat-assessment/consequence-trees.md" target="_blank" rel="noopener noreferrer">${consequenceLabel}</a></li>
|
|
462
464
|
<li><a href="${analysisFileBase}/threat-assessment/legislative-disruption.md" target="_blank" rel="noopener noreferrer">${disruptionLabel}</a></li>
|
|
463
465
|
</ul>
|
|
@@ -472,8 +474,8 @@ function renderAnalysisTransparencySection(date, slug, lang, analysisDir) {
|
|
|
472
474
|
<h3><span aria-hidden="true">🔍</span> ${deepLabel}</h3>
|
|
473
475
|
<ul>
|
|
474
476
|
<li><a href="${analysisFileBase}/existing/deep-analysis.md" target="_blank" rel="noopener noreferrer">${deepAnalysisFileLabel}</a></li>
|
|
475
|
-
<li><a href="${analysisFileBase}/existing/stakeholder-
|
|
476
|
-
<li><a href="${analysisFileBase}/existing/coalition-
|
|
477
|
+
<li><a href="${analysisFileBase}/existing/stakeholder-impact.md" target="_blank" rel="noopener noreferrer">${stakeholderLabel}</a></li>
|
|
478
|
+
<li><a href="${analysisFileBase}/existing/coalition-dynamics.md" target="_blank" rel="noopener noreferrer">${coalitionLabel}</a></li>
|
|
477
479
|
<li><a href="${analysisFileBase}/existing/voting-patterns.md" target="_blank" rel="noopener noreferrer">${votingPatternsLabel}</a></li>
|
|
478
480
|
<li><a href="${analysisFileBase}/existing/cross-session-intelligence.md" target="_blank" rel="noopener noreferrer">${crossSessionLabel}</a></li>
|
|
479
481
|
</ul>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { escapeHTML } from '../utils/file-utils.js';
|
|
9
9
|
import { getLocalizedString, TOC_ARIA_LABELS } from '../constants/languages.js';
|
|
10
|
-
import { stripScriptBlocks } from '../utils/html-sanitize.js';
|
|
10
|
+
import { stripScriptBlocks, stripHtmlTags } from '../utils/html-sanitize.js';
|
|
11
11
|
/**
|
|
12
12
|
* Count occurrences of a regex pattern in a string.
|
|
13
13
|
*
|
|
@@ -52,10 +52,7 @@ export function computeArticleQualityScore(content) {
|
|
|
52
52
|
// Uses iterative scanning instead of regex to avoid CodeQL js/bad-tag-filter.
|
|
53
53
|
const noScripts = stripScriptBlocks(content);
|
|
54
54
|
// Strip HTML tags to get plain text, then count words
|
|
55
|
-
const plainText = noScripts
|
|
56
|
-
.replace(/<[^>]*>/g, ' ')
|
|
57
|
-
.replace(/\s+/g, ' ')
|
|
58
|
-
.trim();
|
|
55
|
+
const plainText = stripHtmlTags(noScripts).replace(/\s+/g, ' ').trim();
|
|
59
56
|
const wordCount = plainText.length > 0 ? plainText.split(' ').filter((w) => w.length > 0).length : 0;
|
|
60
57
|
// All further counting uses script-stripped HTML to avoid false positives
|
|
61
58
|
// from embedded JSON-LD or interactive script blocks.
|
package/scripts/types/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export { type LanguageCode, type RTLLanguageCode, type AnyLanguageCode, ArticleCategory, ArticlePerspective, TimePeriod, AnalysisPerspective, CATEGORY_PERSPECTIVE, CATEGORY_TIME_PERIOD, type LanguagePreset, type LanguageMap, type ArticleCategoryLabels, type LangTitleSubtitle, type PropositionsStrings, type EditorialStrings, type MotionsStrings, type WeekAheadStrings, type WeekAheadStakeholderStrings, type StakeholderImpactRow, type StakeholderImpactSection, type PoliticalTemperatureBand, type PoliticalTemperature, type BreakingStrings, type ActionConsequence, type StakeholderOutcome, type PoliticalMistake, type DeepAnalysis, type DeepAnalysisStrings, type CommitteeAnalysisContentStrings, } from './common.js';
|
|
13
13
|
export type { ParliamentEvent, CommitteeMeeting, LegislativeDocument, LegislativeProcedure, ParliamentaryQuestion, WeekAheadData, CommitteeDocument, CommitteeData, VotingRecord, VotingPattern, VotingAnomaly, MotionsQuestion, VotingAnomalyIntelligence, CoalitionIntelligence, MEPInfluenceScore, LegislativeVelocity, EPFeedItem, AdoptedTextFeedItem, EventFeedItem, ProcedureFeedItem, MEPFeedItem, DocumentFeedItem, QuestionFeedItem, DeclarationFeedItem, CorporateBodyFeedItem, BreakingNewsFeedData, EPFeedData, VotingIntensity, CoalitionShift, PolarizationIndex, PoliticalSignificanceScore, VotingTrend, CoalitionStabilityReport, LegislativeVelocityReport, } from './parliament.js';
|
|
14
14
|
export type { ParsedArticle, ArticleSource, ArticleOptions, SitemapUrl, ArticleMetadataEntry, NewsMetadataDatabase, DateRange, GenerationStats, GenerationResult, ArticleQualityScore, TOCEntry, } from './generation.js';
|
|
15
|
-
export { type MCPClientOptions, type MCPContentItem, type MCPToolResult, type JSONRPCRequest, type JSONRPCResponse, type PendingRequest, type GetMEPsOptions, type GetPlenarySessionsOptions, type SearchDocumentsOptions, type GetParliamentaryQuestionsOptions, type GetCommitteeInfoOptions, type MonitorLegislativePipelineOptions, type AssessMEPInfluenceOptions, type AnalyzeCoalitionDynamicsOptions, type DetectVotingAnomaliesOptions, type ComparePoliticalGroupsOptions, type AnalyzeLegislativeEffectivenessOptions, type VotingRecordsOptions, type VotingPatternsOptions, type ReportType, type GenerateReportOptions, type AnalyzeCommitteeActivityOptions, type TrackMEPAttendanceOptions, type AnalyzeCountryDelegationOptions, type GeneratePoliticalLandscapeOptions, type GetCurrentMEPsOptions, type GetSpeechesOptions, type GetProceduresOptions, type GetAdoptedTextsOptions, type GetEventsOptions, type GetMeetingActivitiesOptions, type GetMeetingDecisionsOptions, type GetMEPDeclarationsOptions, type GetIncomingMEPsOptions, type GetOutgoingMEPsOptions, type GetHomonymMEPsOptions, type GetPlenaryDocumentsOptions, type GetCommitteeDocumentsOptions, type GetPlenarySessionDocumentsOptions, type GetPlenarySessionDocumentItemsOptions, type GetControlledVocabulariesOptions, type GetExternalDocumentsOptions, type GetMeetingForeseenActivitiesOptions, type GetProcedureEventsOptions, type GetMeetingPlenarySessionDocumentsOptions, type GetMeetingPlenarySessionDocumentItemsOptions, type NetworkAnalysisOptions, type SentimentTrackerOptions, type EarlyWarningSystemOptions, type ComparativeIntelligenceOptions, type CorrelateIntelligenceOptions, type GeneratedStatsCategory, type GetAllGeneratedStatsOptions, type FeedTimeframe, type FeedBaseOptions, type GetMEPsFeedOptions, type GetEventsFeedOptions, type GetProceduresFeedOptions, type GetAdoptedTextsFeedOptions, type GetMEPDeclarationsFeedOptions, type GetDocumentsFeedOptions, type GetPlenaryDocumentsFeedOptions, type GetCommitteeDocumentsFeedOptions, type GetPlenarySessionDocumentsFeedOptions, type GetExternalDocumentsFeedOptions, type GetParliamentaryQuestionsFeedOptions, type GetCorporateBodiesFeedOptions, type GetControlledVocabulariesFeedOptions, } from './mcp.js';
|
|
15
|
+
export { type MCPClientOptions, type MCPContentItem, type MCPToolResult, type JSONRPCRequest, type JSONRPCResponse, type PendingRequest, type GetMEPsOptions, type GetPlenarySessionsOptions, type SearchDocumentsOptions, type GetParliamentaryQuestionsOptions, type GetCommitteeInfoOptions, type MonitorLegislativePipelineOptions, type AssessMEPInfluenceOptions, type AnalyzeCoalitionDynamicsOptions, type DetectVotingAnomaliesOptions, type ComparePoliticalGroupsOptions, type AnalyzeLegislativeEffectivenessOptions, type VotingRecordsOptions, type VotingPatternsOptions, type ReportType, type GenerateReportOptions, type AnalyzeCommitteeActivityOptions, type TrackMEPAttendanceOptions, type AnalyzeCountryDelegationOptions, type GeneratePoliticalLandscapeOptions, type GetCurrentMEPsOptions, type GetSpeechesOptions, type GetProceduresOptions, type GetAdoptedTextsOptions, type GetEventsOptions, type GetMeetingActivitiesOptions, type GetMeetingDecisionsOptions, type GetMEPDeclarationsOptions, type GetIncomingMEPsOptions, type GetOutgoingMEPsOptions, type GetHomonymMEPsOptions, type GetPlenaryDocumentsOptions, type GetCommitteeDocumentsOptions, type GetPlenarySessionDocumentsOptions, type GetPlenarySessionDocumentItemsOptions, type GetControlledVocabulariesOptions, type GetExternalDocumentsOptions, type GetMeetingForeseenActivitiesOptions, type GetProcedureEventsOptions, type GetMeetingPlenarySessionDocumentsOptions, type GetMeetingPlenarySessionDocumentItemsOptions, type NetworkAnalysisOptions, type SentimentTrackerOptions, type EarlyWarningSystemOptions, type ComparativeIntelligenceOptions, type CorrelateIntelligenceOptions, type GeneratedStatsCategory, type GetAllGeneratedStatsOptions, type FeedTimeframe, type FeedBaseOptions, type GetMEPsFeedOptions, type GetEventsFeedOptions, type GetProceduresFeedOptions, type GetAdoptedTextsFeedOptions, type GetMEPDeclarationsFeedOptions, type GetDocumentsFeedOptions, type GetPlenaryDocumentsFeedOptions, type GetCommitteeDocumentsFeedOptions, type GetPlenarySessionDocumentsFeedOptions, type GetExternalDocumentsFeedOptions, type GetParliamentaryQuestionsFeedOptions, type GetCorporateBodiesFeedOptions, type GetControlledVocabulariesFeedOptions, type GetProcedureEventByIdOptions, } from './mcp.js';
|
|
16
16
|
export type { WBMCPClientOptions, WorldBankIndicator, WorldBankCountry, EconomicContext, EconomicIndicatorSummary, EUCountryCodeMap, PolicyRelevantIndicators, } from './world-bank.js';
|
|
17
17
|
export type { SwotItem, SwotAnalysis, SwotStrings, SwotBuilderStrings, SwotDimensionName, StakeholderType, SwotDimension, SwotCrossReference, TemporalSwotAssessment, MultiDimensionalSwot, MultiDimensionalSwotStrings, DashboardMetric, ChartDataset, ChartData, ChartConfig, DashboardPanel, DashboardConfig, DashboardStrings, DashboardBuilderStrings, MindmapNodeCategory, PolicyConnectionType, PolicyConnectionStrength, ActorType, MindmapBranchColor, MindmapNode, MindmapLayer, PolicyConnection, ActorNode, InfluenceWeight, IntelligenceMindmap, VotingBloc, VoteHighlight, CoalitionMetrics, LegislativePipeline, TrendMetric, TrendAnalytics, StakeholderMetric, } from './visualization.js';
|
|
18
18
|
export type { ArticleGrade, AnalysisDepthScore, StakeholderCoverage, VisualizationQuality, ArticleQualityReport, } from './quality.js';
|
|
@@ -25,4 +25,5 @@ export type { PoliticalActorThreatProfile, ConsequenceNode, PoliticalConsequence
|
|
|
25
25
|
export type { PoliticalRiskLikelihood, PoliticalRiskImpact, PoliticalRiskLevel, PoliticalRiskScore, PoliticalThreatCategory, PoliticalRiskDriver, PoliticalCapitalAtRisk, LegislativeStage, LegislativeVelocityRisk, SwotItemTrend, ScoredSWOTItem, CrossImpactEntry, QuantitativeSWOT, RiskAssessmentStep, AgentRiskAssessmentWorkflow, RiskLevelCounts, PoliticalRiskSummary, } from './political-risk.js';
|
|
26
26
|
export type { PoliticalSignificance, ImpactLevel, PoliticalImpactAssessment, ClassificationConfidence, PoliticalActorType, PoliticalActorClassification, ForceAssessment, PoliticalForcesAnalysis, ClassificationMethod, AnalysisRunManifest, AnalysisFrontmatter, ClassificationInput, } from './political-classification.js';
|
|
27
27
|
export { SIGNIFICANCE_ORDER, IMPACT_ORDER } from './political-classification.js';
|
|
28
|
+
export type { PublicationDecision, SignificanceDimension, SignificanceScore, SignificanceScoringInput, SignificanceBatchResult, SynthesisFinding, AggregatedSWOT, RiskOverview, SynthesisSummary, } from './significance.js';
|
|
28
29
|
//# sourceMappingURL=index.d.ts.map
|
package/scripts/types/mcp.d.ts
CHANGED
|
@@ -415,4 +415,11 @@ export interface GetCorporateBodiesFeedOptions extends FeedBaseOptions {
|
|
|
415
415
|
/** Options for getControlledVocabulariesFeed */
|
|
416
416
|
export interface GetControlledVocabulariesFeedOptions extends FeedBaseOptions {
|
|
417
417
|
}
|
|
418
|
+
/** Options for getProcedureEventById */
|
|
419
|
+
export interface GetProcedureEventByIdOptions {
|
|
420
|
+
/** Procedure process ID (required) */
|
|
421
|
+
processId: string;
|
|
422
|
+
/** Event identifier (required) */
|
|
423
|
+
eventId: string;
|
|
424
|
+
}
|
|
418
425
|
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -136,7 +136,7 @@ export interface PoliticalForcesAnalysis {
|
|
|
136
136
|
*/
|
|
137
137
|
export type ClassificationMethod = 'impact-matrix' | 'actor-mapping' | 'forces-analysis' | 'significance-assessment';
|
|
138
138
|
/**
|
|
139
|
-
* Metadata record written to `analysis/{date}/{article-type}/manifest.json`.
|
|
139
|
+
* Metadata record written to `analysis/daily/{date}/{article-type}/manifest.json`.
|
|
140
140
|
* Describes a single analysis run: when it ran, which article types were
|
|
141
141
|
* analysed, and which analytical methods were applied.
|
|
142
142
|
*/
|
|
@@ -68,13 +68,16 @@ export interface VisualizationQuality {
|
|
|
68
68
|
/**
|
|
69
69
|
* Letter grade assigned to an article based on its overall quality score.
|
|
70
70
|
*
|
|
71
|
+
* Grade boundaries are calibrated so that the quality gate (Grade C floor)
|
|
72
|
+
* aligns with the methodology minimum of 7.0/10 (≡ 70/100).
|
|
73
|
+
*
|
|
71
74
|
* | Grade | Score range |
|
|
72
75
|
* |-------|-------------|
|
|
73
|
-
* | A | ≥
|
|
74
|
-
* | B | ≥
|
|
75
|
-
* | C | ≥
|
|
76
|
-
* | D | ≥
|
|
77
|
-
* | F | <
|
|
76
|
+
* | A | ≥ 90 |
|
|
77
|
+
* | B | ≥ 80 |
|
|
78
|
+
* | C | ≥ 70 |
|
|
79
|
+
* | D | ≥ 50 |
|
|
80
|
+
* | F | < 50 |
|
|
78
81
|
*/
|
|
79
82
|
export type ArticleGrade = 'A' | 'B' | 'C' | 'D' | 'F';
|
|
80
83
|
/**
|
|
@@ -108,7 +111,7 @@ export interface ArticleQualityReport {
|
|
|
108
111
|
readonly grade: ArticleGrade;
|
|
109
112
|
/** Actionable improvement recommendations */
|
|
110
113
|
readonly recommendations: readonly string[];
|
|
111
|
-
/** true when overallScore ≥
|
|
114
|
+
/** true when overallScore ≥ 70 (Grade C or better, per methodology minimum 7.0/10) */
|
|
112
115
|
readonly passesQualityGate: boolean;
|
|
113
116
|
}
|
|
114
117
|
//# sourceMappingURL=quality.d.ts.map
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Types/Significance
|
|
3
|
+
* @description Type definitions for the significance scoring engine and
|
|
4
|
+
* synthesis summary generator.
|
|
5
|
+
*
|
|
6
|
+
* The significance scoring engine produces a 5-dimension composite score
|
|
7
|
+
* (0–10) used for publication prioritisation decisions. Dimensions and
|
|
8
|
+
* weights follow the analysis template specification:
|
|
9
|
+
*
|
|
10
|
+
* | Dimension | Weight |
|
|
11
|
+
* |----------------------------|--------|
|
|
12
|
+
* | Parliamentary Significance | 0.25 |
|
|
13
|
+
* | Policy Impact | 0.25 |
|
|
14
|
+
* | Public Interest | 0.20 |
|
|
15
|
+
* | Temporal Urgency | 0.15 |
|
|
16
|
+
* | Institutional Relevance | 0.15 |
|
|
17
|
+
*
|
|
18
|
+
* The synthesis summary generator aggregates per-file analysis outputs
|
|
19
|
+
* into a single editorial briefing consumed by article generators.
|
|
20
|
+
*/
|
|
21
|
+
import type { ConfidenceLevel } from './analysis.js';
|
|
22
|
+
/** Publication decision produced by the scoring engine */
|
|
23
|
+
export type PublicationDecision = 'publish' | 'hold' | 'skip';
|
|
24
|
+
/** Names of the five scoring dimensions */
|
|
25
|
+
export type SignificanceDimension = 'parliamentarySignificance' | 'policyImpact' | 'publicInterest' | 'temporalUrgency' | 'institutionalRelevance';
|
|
26
|
+
/**
|
|
27
|
+
* Result of scoring a single EP event / document across five dimensions.
|
|
28
|
+
*
|
|
29
|
+
* Each dimension is scored 0–10. The `composite` value is the weighted
|
|
30
|
+
* average. The `decision` is derived from composite thresholds:
|
|
31
|
+
*
|
|
32
|
+
* | Score | Decision |
|
|
33
|
+
* |------------|-----------|
|
|
34
|
+
* | 0.0 – 3.9 | skip |
|
|
35
|
+
* | 4.0 – 5.9 | hold |
|
|
36
|
+
* | ≥ 6.0 | publish |
|
|
37
|
+
*/
|
|
38
|
+
export interface SignificanceScore {
|
|
39
|
+
/** Parliamentary significance dimension (0–10) */
|
|
40
|
+
readonly parliamentarySignificance: number;
|
|
41
|
+
/** Policy impact dimension (0–10) */
|
|
42
|
+
readonly policyImpact: number;
|
|
43
|
+
/** Public interest dimension (0–10) */
|
|
44
|
+
readonly publicInterest: number;
|
|
45
|
+
/** Temporal urgency dimension (0–10) */
|
|
46
|
+
readonly temporalUrgency: number;
|
|
47
|
+
/** Institutional / cross-group relevance dimension (0–10) */
|
|
48
|
+
readonly institutionalRelevance: number;
|
|
49
|
+
/** Weighted composite score (0–10) */
|
|
50
|
+
readonly composite: number;
|
|
51
|
+
/** Publication decision derived from composite thresholds */
|
|
52
|
+
readonly decision: PublicationDecision;
|
|
53
|
+
}
|
|
54
|
+
/** Input for a single event to be scored */
|
|
55
|
+
export interface SignificanceScoringInput {
|
|
56
|
+
/** Human-readable event title */
|
|
57
|
+
readonly title: string;
|
|
58
|
+
/** EP reference identifier (procedure ID, adopted text ref, etc.) */
|
|
59
|
+
readonly reference?: string;
|
|
60
|
+
/** Raw dimension scores provided by the caller (each 0–10) */
|
|
61
|
+
readonly parliamentarySignificance: number;
|
|
62
|
+
readonly policyImpact: number;
|
|
63
|
+
readonly publicInterest: number;
|
|
64
|
+
readonly temporalUrgency: number;
|
|
65
|
+
readonly institutionalRelevance: number;
|
|
66
|
+
}
|
|
67
|
+
/** Batch scoring result for multiple events */
|
|
68
|
+
export interface SignificanceBatchResult {
|
|
69
|
+
/** Individual event scores, ranked by composite descending */
|
|
70
|
+
readonly scores: readonly SignificanceScore[];
|
|
71
|
+
/** Count of events per decision category */
|
|
72
|
+
readonly summary: {
|
|
73
|
+
readonly publish: number;
|
|
74
|
+
readonly hold: number;
|
|
75
|
+
readonly skip: number;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** A single finding extracted from a per-file analysis */
|
|
79
|
+
export interface SynthesisFinding {
|
|
80
|
+
/** Source analysis method that produced this finding */
|
|
81
|
+
readonly method: string;
|
|
82
|
+
/** Source filename */
|
|
83
|
+
readonly file: string;
|
|
84
|
+
/** Confidence level from the analysis frontmatter */
|
|
85
|
+
readonly confidence: ConfidenceLevel;
|
|
86
|
+
/** One-line summary of the finding */
|
|
87
|
+
readonly summary: string;
|
|
88
|
+
}
|
|
89
|
+
/** Aggregated SWOT counts across all per-file analyses */
|
|
90
|
+
export interface AggregatedSWOT {
|
|
91
|
+
/** Number of strength entries found across all files */
|
|
92
|
+
readonly strengths: number;
|
|
93
|
+
/** Number of weakness entries found across all files */
|
|
94
|
+
readonly weaknesses: number;
|
|
95
|
+
/** Number of opportunity entries found across all files */
|
|
96
|
+
readonly opportunities: number;
|
|
97
|
+
/** Number of threat entries found across all files */
|
|
98
|
+
readonly threats: number;
|
|
99
|
+
}
|
|
100
|
+
/** Risk level distribution across all per-file analyses */
|
|
101
|
+
export interface RiskOverview {
|
|
102
|
+
/** Number of critical-level risk items */
|
|
103
|
+
readonly critical: number;
|
|
104
|
+
/** Number of high-level risk items */
|
|
105
|
+
readonly high: number;
|
|
106
|
+
/** Number of medium-level risk items */
|
|
107
|
+
readonly medium: number;
|
|
108
|
+
/** Number of low-level risk items */
|
|
109
|
+
readonly low: number;
|
|
110
|
+
}
|
|
111
|
+
/** Complete synthesis summary aggregating all per-file analyses */
|
|
112
|
+
export interface SynthesisSummary {
|
|
113
|
+
/** Unique synthesis identifier (SYN-YYYY-MM-DD-{8-char-UUID}) */
|
|
114
|
+
readonly synthesisId: string;
|
|
115
|
+
/** ISO date of the analysis */
|
|
116
|
+
readonly date: string;
|
|
117
|
+
/** Number of analysis files processed */
|
|
118
|
+
readonly documentsAnalyzed: number;
|
|
119
|
+
/** Aggregated confidence across all sources */
|
|
120
|
+
readonly overallConfidence: ConfidenceLevel;
|
|
121
|
+
/** Top findings ranked by confidence (high → medium → low) */
|
|
122
|
+
readonly topFindings: readonly SynthesisFinding[];
|
|
123
|
+
/** Aggregated SWOT summary */
|
|
124
|
+
readonly swot: AggregatedSWOT;
|
|
125
|
+
/** Risk level distribution */
|
|
126
|
+
readonly riskOverview: RiskOverview;
|
|
127
|
+
/** Editorial recommendations */
|
|
128
|
+
readonly editorialRecommendations: readonly string[];
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=significance.d.ts.map
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
* @module Utils/ArticleQualityScorer
|
|
3
3
|
* @description Comprehensive quality assessment engine for generated EU Parliament Monitor articles.
|
|
4
4
|
*
|
|
5
|
-
* Analyses HTML article content across
|
|
6
|
-
* -
|
|
7
|
-
* - **
|
|
8
|
-
* - **
|
|
9
|
-
* - **
|
|
5
|
+
* Analyses HTML article content across five weighted dimensions aligned with
|
|
6
|
+
* the AI-driven analysis methodology guide (`analysis/methodologies/ai-driven-analysis-guide.md`):
|
|
7
|
+
* - **Analysis depth** (25%) — political context, coalition dynamics, historical evidence, scenarios
|
|
8
|
+
* - **Evidence density** (25%) — document references, citations, data-backed claims
|
|
9
|
+
* - **Structural compliance** (20%) — SWOT, dashboard metrics, mindmap branches, deep-analysis
|
|
10
|
+
* - **Actionable intelligence** (15%) — article length via word count
|
|
11
|
+
* - **Stakeholder balance** (15%) — breadth of perspectives from MEPs, Commission, civil society, etc.
|
|
10
12
|
*
|
|
11
13
|
* Produces an {@link ArticleQualityReport} with a 0–100 overall score, letter grade (A–F),
|
|
12
|
-
* pass/fail quality gate, and actionable recommendations.
|
|
14
|
+
* pass/fail quality gate (≥ 70, per methodology minimum 7.0/10), and actionable recommendations.
|
|
13
15
|
*/
|
|
14
16
|
import type { ArticleQualityReport, AnalysisDepthScore, StakeholderCoverage, VisualizationQuality } from '../types/quality.js';
|
|
15
17
|
/**
|
|
@@ -46,12 +48,12 @@ export declare function assessVisualizationQuality(html: string): VisualizationQ
|
|
|
46
48
|
/**
|
|
47
49
|
* Compute the weighted overall quality score (0–100) from component scores.
|
|
48
50
|
*
|
|
49
|
-
* Weights:
|
|
51
|
+
* Weights (aligned with ai-driven-analysis-guide.md):
|
|
50
52
|
* - Analysis depth: 25 %
|
|
51
|
-
* -
|
|
52
|
-
* - Visualization:
|
|
53
|
-
* - Word count: 15 %
|
|
54
|
-
* -
|
|
53
|
+
* - Evidence references: 25 %
|
|
54
|
+
* - Visualization / structural compliance: 20 %
|
|
55
|
+
* - Word count / actionable intelligence: 15 %
|
|
56
|
+
* - Stakeholder balance / political neutrality: 15 %
|
|
55
57
|
*
|
|
56
58
|
* @param depth - Analysis depth score object
|
|
57
59
|
* @param coverage - Stakeholder coverage score object
|
|
@@ -2,32 +2,45 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { stripScriptBlocks } from './html-sanitize.js';
|
|
4
4
|
// ─── Scoring constants ────────────────────────────────────────────────────────
|
|
5
|
-
|
|
5
|
+
//
|
|
6
|
+
// Weights aligned with analysis/methodologies/ai-driven-analysis-guide.md §Score
|
|
7
|
+
// the Analysis:
|
|
8
|
+
// Evidence density → WEIGHT_EVIDENCE = 0.25
|
|
9
|
+
// Analytical depth → WEIGHT_ANALYSIS_DEPTH = 0.25
|
|
10
|
+
// Structural compliance → WEIGHT_VISUALIZATION = 0.20
|
|
11
|
+
// Actionable intel → WEIGHT_WORD_COUNT = 0.15
|
|
12
|
+
// Political neutrality → WEIGHT_STAKEHOLDER = 0.15
|
|
13
|
+
/** Weight applied to analysis depth score in overall calculation (methodology: Analytical depth 25%) */
|
|
6
14
|
const WEIGHT_ANALYSIS_DEPTH = 0.25;
|
|
7
|
-
/** Weight applied to stakeholder balance score in overall calculation */
|
|
8
|
-
const WEIGHT_STAKEHOLDER = 0.
|
|
9
|
-
/** Weight applied to visualization quality score in overall calculation */
|
|
10
|
-
const WEIGHT_VISUALIZATION = 0.
|
|
11
|
-
/** Weight applied to word-count score in overall calculation */
|
|
15
|
+
/** Weight applied to stakeholder balance score in overall calculation (methodology: Political neutrality 15%) */
|
|
16
|
+
const WEIGHT_STAKEHOLDER = 0.15;
|
|
17
|
+
/** Weight applied to visualization quality score in overall calculation (methodology: Structural compliance 20%) */
|
|
18
|
+
const WEIGHT_VISUALIZATION = 0.2;
|
|
19
|
+
/** Weight applied to word-count score in overall calculation (methodology: Actionable intelligence 15%) */
|
|
12
20
|
const WEIGHT_WORD_COUNT = 0.15;
|
|
13
|
-
/** Weight applied to evidence-reference score in overall calculation */
|
|
14
|
-
const WEIGHT_EVIDENCE = 0.
|
|
21
|
+
/** Weight applied to evidence-reference score in overall calculation (methodology: Evidence density 25%) */
|
|
22
|
+
const WEIGHT_EVIDENCE = 0.25;
|
|
15
23
|
/** Minimum word count to score 0 on the word-count dimension */
|
|
16
24
|
const WORD_COUNT_MIN = 0;
|
|
17
25
|
/** Word count that earns the maximum word-count dimension score */
|
|
18
26
|
const WORD_COUNT_MAX = 1500;
|
|
19
27
|
/** Evidence-reference count that earns the maximum evidence dimension score */
|
|
20
28
|
const EVIDENCE_MAX = 10;
|
|
21
|
-
/**
|
|
22
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Overall score threshold for passing the quality gate.
|
|
31
|
+
*
|
|
32
|
+
* Aligned with analysis/methodologies/ai-driven-analysis-guide.md which
|
|
33
|
+
* mandates a minimum 7.0/10 (≡ 70/100) quality score.
|
|
34
|
+
*/
|
|
35
|
+
const QUALITY_GATE_THRESHOLD = 70;
|
|
23
36
|
/** Grade boundary — score >= this earns an A */
|
|
24
|
-
const GRADE_A_MIN =
|
|
37
|
+
const GRADE_A_MIN = 90;
|
|
25
38
|
/** Grade boundary — score >= this earns a B */
|
|
26
|
-
const GRADE_B_MIN =
|
|
27
|
-
/** Grade boundary — score >= this earns a C */
|
|
28
|
-
const GRADE_C_MIN =
|
|
39
|
+
const GRADE_B_MIN = 80;
|
|
40
|
+
/** Grade boundary — score >= this earns a C (matches quality gate threshold) */
|
|
41
|
+
const GRADE_C_MIN = 70;
|
|
29
42
|
/** Grade boundary — score >= this earns a D */
|
|
30
|
-
const GRADE_D_MIN =
|
|
43
|
+
const GRADE_D_MIN = 50;
|
|
31
44
|
// ─── Analysis-depth keyword sets ─────────────────────────────────────────────
|
|
32
45
|
/** Keywords indicating political context discussion */
|
|
33
46
|
const POLITICAL_CONTEXT_KEYWORDS = [
|
|
@@ -784,12 +797,12 @@ function computeVisualizationScore(v) {
|
|
|
784
797
|
/**
|
|
785
798
|
* Compute the weighted overall quality score (0–100) from component scores.
|
|
786
799
|
*
|
|
787
|
-
* Weights:
|
|
800
|
+
* Weights (aligned with ai-driven-analysis-guide.md):
|
|
788
801
|
* - Analysis depth: 25 %
|
|
789
|
-
* -
|
|
790
|
-
* - Visualization:
|
|
791
|
-
* - Word count: 15 %
|
|
792
|
-
* -
|
|
802
|
+
* - Evidence references: 25 %
|
|
803
|
+
* - Visualization / structural compliance: 20 %
|
|
804
|
+
* - Word count / actionable intelligence: 15 %
|
|
805
|
+
* - Stakeholder balance / political neutrality: 15 %
|
|
793
806
|
*
|
|
794
807
|
* @param depth - Analysis depth score object
|
|
795
808
|
* @param coverage - Stakeholder coverage score object
|
|
@@ -898,10 +911,10 @@ export function generateRecommendations(report) {
|
|
|
898
911
|
*/
|
|
899
912
|
function addWordCountRecommendations(report, recs) {
|
|
900
913
|
if (report.wordCount < 500) {
|
|
901
|
-
recs.push('Expand article length to at least 500 words
|
|
914
|
+
recs.push('Expand article length to at least 500 words to improve word-count score');
|
|
902
915
|
}
|
|
903
916
|
else if (report.wordCount < WORD_COUNT_MAX) {
|
|
904
|
-
recs.push(`Increase article depth to ${WORD_COUNT_MAX} words for
|
|
917
|
+
recs.push(`Increase article depth to ${WORD_COUNT_MAX} words for maximum word-count score (currently ${report.wordCount})`);
|
|
905
918
|
}
|
|
906
919
|
}
|
|
907
920
|
/**
|
|
@@ -987,7 +1000,7 @@ function addEvidenceRecommendations(report, recs) {
|
|
|
987
1000
|
recs.push('Add at least 3 evidence references or EP document citations');
|
|
988
1001
|
}
|
|
989
1002
|
else if (report.evidenceReferences < 10) {
|
|
990
|
-
recs.push(`Increase evidence references to 10 for
|
|
1003
|
+
recs.push(`Increase evidence references to 10 for maximum evidence score (currently ${report.evidenceReferences})`);
|
|
991
1004
|
}
|
|
992
1005
|
}
|
|
993
1006
|
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
@@ -69,9 +69,9 @@ export declare function ensureDirectoryExists(dirPath: string): void;
|
|
|
69
69
|
* `mkdirSync`, preventing TOCTOU races when concurrent workflow runs
|
|
70
70
|
* attempt to claim the same candidate.
|
|
71
71
|
*
|
|
72
|
-
* @param baseDir - The preferred directory path (e.g. `analysis/2026-04-02/breaking`)
|
|
72
|
+
* @param baseDir - The preferred directory path (e.g. `analysis/daily/2026-04-02/breaking`)
|
|
73
73
|
* @returns The original `baseDir` when no completed run exists there, or a
|
|
74
|
-
* suffixed variant (e.g. `analysis/2026-04-02/breaking-2`) otherwise.
|
|
74
|
+
* suffixed variant (e.g. `analysis/daily/2026-04-02/breaking-2`) otherwise.
|
|
75
75
|
*/
|
|
76
76
|
export declare function resolveUniqueAnalysisDir(baseDir: string): string;
|
|
77
77
|
/**
|