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
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* @module Generators/Pipeline/AnalysisRisk
|
|
5
|
+
* @description Risk scoring analysis method builders for the analysis pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Contains markdown builders for the **Risk Scoring** analysis method group:
|
|
8
|
+
* - `risk-matrix` — political risk scoring matrix with heat map
|
|
9
|
+
* - `political-capital-risk` — political capital at risk assessment
|
|
10
|
+
* - `quantitative-swot` — full narrative SWOT analysis with scored items
|
|
11
|
+
* - `legislative-velocity-risk` — legislative processing speed risk
|
|
12
|
+
* - `agent-risk-workflow` — agent-driven risk assessment workflow
|
|
13
|
+
*/
|
|
14
|
+
import { ArticleCategory } from '../../types/index.js';
|
|
15
|
+
import { assessLegislativeVelocityRisk, runAgentRiskAssessment, generateRiskAssessmentMarkdown, calculatePoliticalRiskScore, buildQuantitativeSWOT, createScoredSWOTItem, createScoredOpportunityOrThreat, createRiskDriver, } from '../../utils/political-risk-assessment.js';
|
|
16
|
+
import { sanitizeCell, safeArr, buildMarkdownHeader, EMPTY_TABLE_ROW_6, } from './analysis-helpers.js';
|
|
17
|
+
// ─── SWOT item builder ──────────────────────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Build the data-driven SWOT items for the political SWOT analysis.
|
|
20
|
+
*
|
|
21
|
+
* Descriptions are derived purely from data metrics — no pre-written
|
|
22
|
+
* political conclusions. AI enrichment markers indicate where the agentic
|
|
23
|
+
* workflow should inject real political intelligence analysis.
|
|
24
|
+
*
|
|
25
|
+
* @param counts - Count of items per data category
|
|
26
|
+
* @param counts.procedures - Number of active legislative procedures
|
|
27
|
+
* @param counts.adoptedTexts - Number of adopted texts
|
|
28
|
+
* @param counts.documents - Number of published documents
|
|
29
|
+
* @param counts.votingRecords - Number of roll-call voting records
|
|
30
|
+
* @param counts.questions - Number of parliamentary questions
|
|
31
|
+
* @param counts.mepUpdates - Number of MEP activity updates
|
|
32
|
+
* @param counts.events - Number of scheduled events
|
|
33
|
+
* @param counts.coalitions - Number of coalition data points
|
|
34
|
+
* @returns Object with strengths, weaknesses, opportunities, and threats arrays
|
|
35
|
+
*/
|
|
36
|
+
function buildPoliticalSwotItems(counts) {
|
|
37
|
+
const strengths = [
|
|
38
|
+
createScoredSWOTItem(`${counts.procedures} procedures in active legislative pipeline`, Math.min(counts.procedures / 5, 5), [
|
|
39
|
+
`${counts.procedures} procedures tracked in current period`,
|
|
40
|
+
`${counts.adoptedTexts} texts adopted`,
|
|
41
|
+
`${counts.documents} documents published`,
|
|
42
|
+
], counts.procedures > 0 ? 'medium' : 'low', counts.procedures > 5 ? 'improving' : 'stable'),
|
|
43
|
+
createScoredSWOTItem(`${counts.votingRecords} roll-call votes recorded with ${counts.questions} questions`, Math.min(counts.votingRecords / 3, 5), [
|
|
44
|
+
`${counts.votingRecords} voting records available`,
|
|
45
|
+
`${counts.questions} parliamentary questions filed`,
|
|
46
|
+
`${counts.mepUpdates} MEP activity updates`,
|
|
47
|
+
], counts.votingRecords > 0 ? 'medium' : 'low', 'stable'),
|
|
48
|
+
];
|
|
49
|
+
const weaknesses = [
|
|
50
|
+
createScoredSWOTItem(`${counts.mepUpdates} MEP updates — data coverage gap assessment`, Math.max(2, 5 - counts.mepUpdates / 10), [
|
|
51
|
+
`${counts.mepUpdates} MEP updates in current period`,
|
|
52
|
+
`${counts.documents} documents vs ${counts.procedures} procedures ratio`,
|
|
53
|
+
`Data freshness depends on EP feed update frequency`,
|
|
54
|
+
], 'medium', 'stable'),
|
|
55
|
+
];
|
|
56
|
+
const opportunities = [
|
|
57
|
+
createScoredOpportunityOrThreat(`${counts.events} parliamentary events scheduled`, counts.events > 3 ? 'likely' : 'possible', counts.events > 5 ? 'major' : 'moderate', [
|
|
58
|
+
`${counts.events} events in analysis period`,
|
|
59
|
+
`${counts.adoptedTexts} texts adopted indicates legislative throughput`,
|
|
60
|
+
`${counts.procedures} procedures in various stages`,
|
|
61
|
+
], 'medium', counts.events > 3 ? 'improving' : 'stable'),
|
|
62
|
+
];
|
|
63
|
+
const threats = [
|
|
64
|
+
createScoredOpportunityOrThreat(`${counts.coalitions} coalition data points — cohesion monitoring`, counts.coalitions > 0 ? 'possible' : 'unlikely', 'moderate', [
|
|
65
|
+
`${counts.coalitions} coalition observations recorded`,
|
|
66
|
+
`Cross-reference with ${counts.votingRecords} voting records`,
|
|
67
|
+
`${counts.procedures} procedures may be affected by coalition shifts`,
|
|
68
|
+
], counts.coalitions > 0 ? 'medium' : 'low', 'stable'),
|
|
69
|
+
];
|
|
70
|
+
return { strengths, weaknesses, opportunities, threats };
|
|
71
|
+
}
|
|
72
|
+
// ─── Per-method markdown builders ────────────────────────────────────────────
|
|
73
|
+
/**
|
|
74
|
+
* Build markdown for the risk scoring matrix.
|
|
75
|
+
*
|
|
76
|
+
* @param fetchedData - Raw fetched EP data
|
|
77
|
+
* @param date - Analysis date
|
|
78
|
+
* @returns Markdown content string
|
|
79
|
+
*/
|
|
80
|
+
export function buildRiskMatrixMarkdown(fetchedData, date) {
|
|
81
|
+
const procedures = safeArr(fetchedData, 'procedures');
|
|
82
|
+
const risks = [];
|
|
83
|
+
if (procedures.length > 0) {
|
|
84
|
+
risks.push(calculatePoliticalRiskScore('possible', 'moderate', 'RISK-001', 'Legislative blockage risk from procedure backlog', [`${procedures.length} procedures in pipeline`], ['Established committee procedures'], 'medium'));
|
|
85
|
+
}
|
|
86
|
+
const coalitions = safeArr(fetchedData, 'coalitions');
|
|
87
|
+
if (coalitions.length > 0) {
|
|
88
|
+
risks.push(calculatePoliticalRiskScore('unlikely', 'major', 'RISK-002', 'Coalition instability risk', [`${coalitions.length} coalition data points`], ['Established political group structures'], 'medium'));
|
|
89
|
+
}
|
|
90
|
+
const anomalies = safeArr(fetchedData, 'anomalies');
|
|
91
|
+
if (anomalies.length > 0) {
|
|
92
|
+
risks.push(calculatePoliticalRiskScore('possible', 'moderate', 'RISK-003', 'Voting pattern anomaly risk', [`${anomalies.length} anomalies detected`], [], 'medium'));
|
|
93
|
+
}
|
|
94
|
+
const header = buildMarkdownHeader('risk-matrix', date, risks.length > 0 ? 'medium' : 'low');
|
|
95
|
+
const riskRows = risks.length > 0
|
|
96
|
+
? risks
|
|
97
|
+
.map((r) => `| ${r.riskId} | ${r.description} | ${r.likelihood} | ${r.impact} | ${r.riskScore} | ${r.riskLevel} |`)
|
|
98
|
+
.join('\n')
|
|
99
|
+
: EMPTY_TABLE_ROW_6;
|
|
100
|
+
return (header +
|
|
101
|
+
`# Political Risk Scoring Matrix
|
|
102
|
+
|
|
103
|
+
## Overview
|
|
104
|
+
|
|
105
|
+
Quantitative risk scoring across ${risks.length} identified political dimensions.
|
|
106
|
+
This matrix uses a standardized likelihood × impact framework to quantify and
|
|
107
|
+
prioritize political risks affecting the European Parliament legislative process.
|
|
108
|
+
|
|
109
|
+
## Risk Heat Map
|
|
110
|
+
|
|
111
|
+
\`\`\`mermaid
|
|
112
|
+
quadrantChart
|
|
113
|
+
title Political Risk Heat Map — ${date}
|
|
114
|
+
x-axis Low Likelihood --> High Likelihood
|
|
115
|
+
y-axis Low Impact --> High Impact
|
|
116
|
+
quadrant-1 Critical Risk Zone
|
|
117
|
+
quadrant-2 High Impact / Low Likelihood
|
|
118
|
+
quadrant-3 Acceptable Risk Zone
|
|
119
|
+
quadrant-4 High Likelihood / Low Impact
|
|
120
|
+
${risks
|
|
121
|
+
.map((r) => {
|
|
122
|
+
const likelihoodMap = {
|
|
123
|
+
rare: 0.15,
|
|
124
|
+
unlikely: 0.3,
|
|
125
|
+
possible: 0.5,
|
|
126
|
+
likely: 0.7,
|
|
127
|
+
'almost certain': 0.9,
|
|
128
|
+
};
|
|
129
|
+
const impactMap = {
|
|
130
|
+
minor: 0.2,
|
|
131
|
+
moderate: 0.45,
|
|
132
|
+
major: 0.7,
|
|
133
|
+
critical: 0.9,
|
|
134
|
+
};
|
|
135
|
+
const lx = likelihoodMap[r.likelihood] ?? 0.5;
|
|
136
|
+
const ly = impactMap[r.impact] ?? 0.45;
|
|
137
|
+
return ` ${sanitizeCell(r.riskId)}: [${lx.toFixed(2)}, ${ly.toFixed(2)}]`;
|
|
138
|
+
})
|
|
139
|
+
.join('\n')}
|
|
140
|
+
\`\`\`
|
|
141
|
+
|
|
142
|
+
## Risk Matrix
|
|
143
|
+
|
|
144
|
+
| Risk ID | Description | Likelihood | Impact | Score | Level |
|
|
145
|
+
|---------|-------------|------------|--------|-------|-------|
|
|
146
|
+
${riskRows}
|
|
147
|
+
|
|
148
|
+
> **Risk Score** = Likelihood × Impact. **Levels**: 🟢 LOW (≤1.0), 🟡 MEDIUM (≤2.0), 🟠 HIGH (≤3.5), 🔴 CRITICAL (>3.5)
|
|
149
|
+
|
|
150
|
+
## Risk Assessment Details
|
|
151
|
+
|
|
152
|
+
${risks.length > 0
|
|
153
|
+
? risks
|
|
154
|
+
.map((r) => `### ${r.riskId}: ${r.description}
|
|
155
|
+
|
|
156
|
+
| Metric | Value |
|
|
157
|
+
|--------|-------|
|
|
158
|
+
| Risk Score | ${r.riskScore.toFixed(2)} |
|
|
159
|
+
| Risk Level | ${r.riskLevel.toUpperCase()} |
|
|
160
|
+
| Likelihood | ${r.likelihood} |
|
|
161
|
+
| Impact | ${r.impact} |
|
|
162
|
+
`)
|
|
163
|
+
.join('\n')
|
|
164
|
+
: '| — | — | — | — | — | — |'}
|
|
165
|
+
|
|
166
|
+
## Risk Mitigation Framework
|
|
167
|
+
|
|
168
|
+
| Risk Level | Count | Tolerance | Action Required |
|
|
169
|
+
|------------|-------|-----------|-----------------|
|
|
170
|
+
| 🔴 CRITICAL | ${risks.filter((r) => r.riskLevel === 'critical').length} | Zero tolerance | Immediate escalation |
|
|
171
|
+
| 🟠 HIGH | ${risks.filter((r) => r.riskLevel === 'high').length} | Low tolerance | Active mitigation |
|
|
172
|
+
| 🟡 MEDIUM | ${risks.filter((r) => r.riskLevel === 'medium').length} | Moderate | Enhanced monitoring |
|
|
173
|
+
| 🟢 LOW | ${risks.filter((r) => r.riskLevel === 'low').length} | Acceptable | Routine tracking |
|
|
174
|
+
|
|
175
|
+
## Date: ${date}
|
|
176
|
+
`);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Build markdown for political capital at risk analysis.
|
|
180
|
+
*
|
|
181
|
+
* @param fetchedData - Raw fetched EP data
|
|
182
|
+
* @param date - Analysis date
|
|
183
|
+
* @returns Markdown content string
|
|
184
|
+
*/
|
|
185
|
+
export function buildPoliticalCapitalRiskMarkdown(fetchedData, date) {
|
|
186
|
+
const header = buildMarkdownHeader('political-capital-risk', date, 'medium');
|
|
187
|
+
const coalitions = safeArr(fetchedData, 'coalitions');
|
|
188
|
+
const votingRecords = safeArr(fetchedData, 'votingRecords');
|
|
189
|
+
const patterns = safeArr(fetchedData, 'patterns');
|
|
190
|
+
const procedures = safeArr(fetchedData, 'procedures');
|
|
191
|
+
return (header +
|
|
192
|
+
`# Political Capital at Risk
|
|
193
|
+
|
|
194
|
+
## Data Inventory for Capital Risk Assessment
|
|
195
|
+
| Data Source | Count | Relevance |
|
|
196
|
+
|-------------|-------|-----------|
|
|
197
|
+
| Coalition data points | ${coalitions.length} | Group cohesion indicators |
|
|
198
|
+
| Voting records | ${votingRecords.length} | Voting alignment metrics |
|
|
199
|
+
| Voting patterns | ${patterns.length} | Trend and anomaly data |
|
|
200
|
+
| Active procedures | ${procedures.length} | Legislative engagement |
|
|
201
|
+
|
|
202
|
+
## Date: ${date}
|
|
203
|
+
`);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Build markdown for the quantitative SWOT analysis.
|
|
207
|
+
*
|
|
208
|
+
* Produces a full narrative SWOT analysis modelled after the repository's
|
|
209
|
+
* SWOT.md — each quadrant item has a description, strategic value, evidence
|
|
210
|
+
* bullets, and a scored impact rating derived from actual fetched EP data.
|
|
211
|
+
*
|
|
212
|
+
* @param fetchedData - Raw fetched EP data
|
|
213
|
+
* @param date - Analysis date
|
|
214
|
+
* @returns Markdown content string
|
|
215
|
+
*/
|
|
216
|
+
export function buildQuantitativeSwotMarkdown(fetchedData, date) {
|
|
217
|
+
const header = buildMarkdownHeader('quantitative-swot', date, 'medium');
|
|
218
|
+
const events = safeArr(fetchedData, 'events');
|
|
219
|
+
const procedures = safeArr(fetchedData, 'procedures');
|
|
220
|
+
const adoptedTexts = safeArr(fetchedData, 'adoptedTexts');
|
|
221
|
+
const documents = safeArr(fetchedData, 'documents');
|
|
222
|
+
const votingRecords = safeArr(fetchedData, 'votingRecords');
|
|
223
|
+
const coalitions = safeArr(fetchedData, 'coalitions');
|
|
224
|
+
const questions = safeArr(fetchedData, 'questions');
|
|
225
|
+
const mepUpdates = safeArr(fetchedData, 'mepUpdates');
|
|
226
|
+
const counts = {
|
|
227
|
+
procedures: procedures.length,
|
|
228
|
+
adoptedTexts: adoptedTexts.length,
|
|
229
|
+
documents: documents.length,
|
|
230
|
+
votingRecords: votingRecords.length,
|
|
231
|
+
questions: questions.length,
|
|
232
|
+
mepUpdates: mepUpdates.length,
|
|
233
|
+
events: events.length,
|
|
234
|
+
coalitions: coalitions.length,
|
|
235
|
+
};
|
|
236
|
+
const { strengths, weaknesses, opportunities, threats } = buildPoliticalSwotItems(counts);
|
|
237
|
+
const swot = buildQuantitativeSWOT(`Political SWOT Assessment ${date}`, strengths, weaknesses, opportunities, threats);
|
|
238
|
+
const strengthsNarrative = swot.strengths
|
|
239
|
+
.map((s, i) => `### S${i + 1}: ${s.description}\n` +
|
|
240
|
+
`- **Score**: ${s.score.toFixed(1)}/5\n` +
|
|
241
|
+
`- **Confidence**: ${s.confidence}\n` +
|
|
242
|
+
`- **Trend**: ${s.trend}\n` +
|
|
243
|
+
`- **Evidence**:\n${s.evidence.map((e) => ` - ${e}`).join('\n')}`)
|
|
244
|
+
.join('\n\n');
|
|
245
|
+
const weaknessesNarrative = swot.weaknesses
|
|
246
|
+
.map((w, i) => `### W${i + 1}: ${w.description}\n` +
|
|
247
|
+
`- **Score**: ${w.score.toFixed(1)}/5\n` +
|
|
248
|
+
`- **Confidence**: ${w.confidence}\n` +
|
|
249
|
+
`- **Trend**: ${w.trend}\n` +
|
|
250
|
+
`- **Evidence**:\n${w.evidence.map((e) => ` - ${e}`).join('\n')}`)
|
|
251
|
+
.join('\n\n');
|
|
252
|
+
const opportunitiesNarrative = swot.opportunities
|
|
253
|
+
.map((o, i) => `### O${i + 1}: ${o.description}\n` +
|
|
254
|
+
`- **Score**: ${o.score.toFixed(1)}/5\n` +
|
|
255
|
+
`- **Confidence**: ${o.confidence}\n` +
|
|
256
|
+
`- **Trend**: ${o.trend}\n` +
|
|
257
|
+
`- **Evidence**:\n${o.evidence.map((e) => ` - ${e}`).join('\n')}`)
|
|
258
|
+
.join('\n\n');
|
|
259
|
+
const threatsNarrative = swot.threats
|
|
260
|
+
.map((t, i) => `### T${i + 1}: ${t.description}\n` +
|
|
261
|
+
`- **Score**: ${t.score.toFixed(1)}/5\n` +
|
|
262
|
+
`- **Confidence**: ${t.confidence}\n` +
|
|
263
|
+
`- **Trend**: ${t.trend}\n` +
|
|
264
|
+
`- **Evidence**:\n${t.evidence.map((e) => ` - ${e}`).join('\n')}`)
|
|
265
|
+
.join('\n\n');
|
|
266
|
+
return (header +
|
|
267
|
+
`# Full Political SWOT Analysis
|
|
268
|
+
|
|
269
|
+
## Executive Summary
|
|
270
|
+
|
|
271
|
+
**Strategic Position Score**: ${swot.strategicPositionScore.toFixed(1)}/10
|
|
272
|
+
**Overall Assessment**: ${swot.overallAssessment}
|
|
273
|
+
**Analysis Date**: ${date}
|
|
274
|
+
|
|
275
|
+
> This SWOT analysis is derived from ${procedures.length} procedures, ${events.length} events, ${adoptedTexts.length} adopted texts, ${documents.length} documents, ${votingRecords.length} voting records, and ${coalitions.length} coalition data points fetched from the European Parliament.
|
|
276
|
+
|
|
277
|
+
## SWOT Quadrant Chart
|
|
278
|
+
|
|
279
|
+
\`\`\`mermaid
|
|
280
|
+
quadrantChart
|
|
281
|
+
title Political SWOT — Strategic Position (${date})
|
|
282
|
+
x-axis Low Impact --> High Impact
|
|
283
|
+
y-axis Low Priority --> High Priority
|
|
284
|
+
quadrant-1 Opportunities
|
|
285
|
+
quadrant-2 Strengths
|
|
286
|
+
quadrant-3 Weaknesses
|
|
287
|
+
quadrant-4 Threats
|
|
288
|
+
${swot.strengths.map((s, i) => ` S${i + 1} ${sanitizeCell(s.description).slice(0, 25)}: [${Math.max(0.55, Math.min(0.95, 0.5 + s.score / 10)).toFixed(2)}, ${Math.max(0.55, Math.min(0.95, 0.5 + s.score / 10)).toFixed(2)}]`).join('\n')}
|
|
289
|
+
${swot.weaknesses.map((w, i) => ` W${i + 1} ${sanitizeCell(w.description).slice(0, 25)}: [${Math.max(0.05, Math.min(0.45, 0.5 - w.score / 10)).toFixed(2)}, ${Math.max(0.05, Math.min(0.45, 0.5 - w.score / 10)).toFixed(2)}]`).join('\n')}
|
|
290
|
+
${swot.opportunities.map((o, i) => ` O${i + 1} ${sanitizeCell(o.description).slice(0, 25)}: [${Math.max(0.55, Math.min(0.95, 0.5 + o.score / 10)).toFixed(2)}, ${Math.max(0.55, Math.min(0.95, 0.5 + o.score / 10)).toFixed(2)}]`).join('\n')}
|
|
291
|
+
${swot.threats.map((t, i) => ` T${i + 1} ${sanitizeCell(t.description).slice(0, 25)}: [${Math.max(0.55, Math.min(0.95, 0.5 + t.score / 10)).toFixed(2)}, ${Math.max(0.05, Math.min(0.45, 0.5 - t.score / 10)).toFixed(2)}]`).join('\n')}
|
|
292
|
+
\`\`\`
|
|
293
|
+
|
|
294
|
+
## SWOT Overview
|
|
295
|
+
|
|
296
|
+
| Category | Items | Avg Score | Trend |
|
|
297
|
+
|----------|-------|-----------|-------|
|
|
298
|
+
| 🟢 Strengths | ${swot.strengths.length} | ${swot.strengths.length > 0 ? (swot.strengths.reduce((s, i) => s + i.score, 0) / swot.strengths.length).toFixed(1) : '—'} | ${swot.strengths[0]?.trend ?? '—'} |
|
|
299
|
+
| 🔴 Weaknesses | ${swot.weaknesses.length} | ${swot.weaknesses.length > 0 ? (swot.weaknesses.reduce((s, i) => s + i.score, 0) / swot.weaknesses.length).toFixed(1) : '—'} | ${swot.weaknesses[0]?.trend ?? '—'} |
|
|
300
|
+
| 🔵 Opportunities | ${swot.opportunities.length} | ${swot.opportunities.length > 0 ? (swot.opportunities.reduce((s, i) => s + i.score, 0) / swot.opportunities.length).toFixed(1) : '—'} | ${swot.opportunities[0]?.trend ?? '—'} |
|
|
301
|
+
| 🟠 Threats | ${swot.threats.length} | ${swot.threats.length > 0 ? (swot.threats.reduce((s, i) => s + i.score, 0) / swot.threats.length).toFixed(1) : '—'} | ${swot.threats[0]?.trend ?? '—'} |
|
|
302
|
+
|
|
303
|
+
## 🟢 Strengths
|
|
304
|
+
|
|
305
|
+
${strengthsNarrative || '_No strengths identified from available data._'}
|
|
306
|
+
|
|
307
|
+
## 🔴 Weaknesses
|
|
308
|
+
|
|
309
|
+
${weaknessesNarrative || '_No weaknesses identified from available data._'}
|
|
310
|
+
|
|
311
|
+
## 🔵 Opportunities
|
|
312
|
+
|
|
313
|
+
${opportunitiesNarrative || '_No opportunities identified from available data._'}
|
|
314
|
+
|
|
315
|
+
## 🟠 Threats
|
|
316
|
+
|
|
317
|
+
${threatsNarrative || '_No threats identified from available data._'}
|
|
318
|
+
|
|
319
|
+
## Cross-Impact Matrix
|
|
320
|
+
|
|
321
|
+
${swot.crossImpactMatrix.length > 0
|
|
322
|
+
? '| Interaction | Net Effect | Rationale |\n|-------------|-----------|----------|\n' +
|
|
323
|
+
swot.crossImpactMatrix
|
|
324
|
+
.slice(0, 10)
|
|
325
|
+
.map((e) => `| ${e.swotType} #${e.swotIndex + 1} × threat #${e.threatIndex + 1} | ${e.netEffect.toFixed(2)} | ${sanitizeCell(e.rationale)} |`)
|
|
326
|
+
.join('\n')
|
|
327
|
+
: '- No cross-impacts identified from available data'}
|
|
328
|
+
|
|
329
|
+
## Strategic Priorities Matrix
|
|
330
|
+
|
|
331
|
+
## Data Summary
|
|
332
|
+
|
|
333
|
+
| Data Source | Count |
|
|
334
|
+
|-------------|-------|
|
|
335
|
+
| Procedures | ${procedures.length} |
|
|
336
|
+
| Events | ${events.length} |
|
|
337
|
+
| Documents | ${documents.length} |
|
|
338
|
+
| Voting Records | ${votingRecords.length} |
|
|
339
|
+
| Adopted Texts | ${adoptedTexts.length} |
|
|
340
|
+
| Coalitions | ${coalitions.length} |
|
|
341
|
+
| Questions | ${questions.length} |
|
|
342
|
+
| MEP Updates | ${mepUpdates.length} |
|
|
343
|
+
| **Total Data Points** | **${procedures.length + events.length + documents.length + votingRecords.length + adoptedTexts.length}** |
|
|
344
|
+
|
|
345
|
+
## Date: ${date}
|
|
346
|
+
`);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Build markdown for legislative velocity risk analysis.
|
|
350
|
+
*
|
|
351
|
+
* @param fetchedData - Raw fetched EP data
|
|
352
|
+
* @param date - Analysis date
|
|
353
|
+
* @returns Markdown content string
|
|
354
|
+
*/
|
|
355
|
+
export function buildLegislativeVelocityRiskMarkdown(fetchedData, date) {
|
|
356
|
+
const procedures = safeArr(fetchedData, 'procedures');
|
|
357
|
+
const velocityRisks = assessLegislativeVelocityRisk(procedures);
|
|
358
|
+
const header = buildMarkdownHeader('legislative-velocity-risk', date, velocityRisks.length > 0 ? 'medium' : 'low');
|
|
359
|
+
const riskRows = velocityRisks.length > 0
|
|
360
|
+
? velocityRisks
|
|
361
|
+
.slice(0, 10)
|
|
362
|
+
.map((r) => `| ${sanitizeCell(r.procedureId)} | ${sanitizeCell(r.title.slice(0, 40))} | ${sanitizeCell(r.currentStage)} | ${r.daysInCurrentStage}d / ${r.expectedDaysForStage}d | ${r.velocityRisk.riskScore.toFixed(2)} | ${sanitizeCell(r.velocityRisk.riskLevel)} |`)
|
|
363
|
+
.join('\n')
|
|
364
|
+
: EMPTY_TABLE_ROW_6;
|
|
365
|
+
return (header +
|
|
366
|
+
`# Legislative Velocity Risk
|
|
367
|
+
|
|
368
|
+
## Overview
|
|
369
|
+
Risk assessment based on legislative processing speed for ${procedures.length} procedures.
|
|
370
|
+
|
|
371
|
+
## Top Velocity Risks
|
|
372
|
+
| Procedure | Title | Stage | Days (actual/expected) | Risk Score | Level |
|
|
373
|
+
|-----------|-------|-------|----------------------|------------|-------|
|
|
374
|
+
${riskRows}
|
|
375
|
+
|
|
376
|
+
## Summary
|
|
377
|
+
- **Procedures analysed**: ${procedures.length}
|
|
378
|
+
- **High/Critical risks**: ${velocityRisks.filter((r) => r.velocityRisk.riskLevel === 'high' || r.velocityRisk.riskLevel === 'critical').length}
|
|
379
|
+
- **Date**: ${date}
|
|
380
|
+
`);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Build markdown for the agent risk assessment workflow.
|
|
384
|
+
*
|
|
385
|
+
* @param fetchedData - Raw fetched EP data
|
|
386
|
+
* @param date - Analysis date
|
|
387
|
+
* @returns Markdown content string
|
|
388
|
+
*/
|
|
389
|
+
export function buildAgentRiskWorkflowMarkdown(fetchedData, date) {
|
|
390
|
+
const procedures = safeArr(fetchedData, 'procedures');
|
|
391
|
+
const coalitions = safeArr(fetchedData, 'coalitions');
|
|
392
|
+
const identifiedRisks = [];
|
|
393
|
+
if (procedures.length > 0) {
|
|
394
|
+
identifiedRisks.push(calculatePoliticalRiskScore('possible', 'moderate', 'RISK-W01', 'Legislative backlog risk', [`${procedures.length} active procedures`], ['Committee oversight'], 'medium'));
|
|
395
|
+
}
|
|
396
|
+
if (coalitions.length > 0) {
|
|
397
|
+
identifiedRisks.push(calculatePoliticalRiskScore('unlikely', 'moderate', 'RISK-W02', 'Coalition cohesion risk', [`${coalitions.length} coalitions monitored`], ['Group discipline mechanisms'], 'medium'));
|
|
398
|
+
}
|
|
399
|
+
if (identifiedRisks.length === 0) {
|
|
400
|
+
identifiedRisks.push(calculatePoliticalRiskScore('rare', 'minor', 'RISK-W00', 'Baseline political risk', ['Routine parliamentary activity'], ['Stable institutional framework'], 'low'));
|
|
401
|
+
}
|
|
402
|
+
const riskDrivers = [
|
|
403
|
+
createRiskDriver('Legislative pipeline complexity', 'legislative_delay', Math.min(procedures.length * 2, 30), 'stable'),
|
|
404
|
+
createRiskDriver('Coalition dynamics', 'coalition_fracture', 15, 'stable'),
|
|
405
|
+
];
|
|
406
|
+
const workflow = runAgentRiskAssessment(`ASSESS-${date}`, date, ArticleCategory.WEEK_AHEAD, identifiedRisks, riskDrivers, ['Monitor legislative velocity indicators', 'Track coalition voting patterns']);
|
|
407
|
+
return generateRiskAssessmentMarkdown(workflow);
|
|
408
|
+
}
|
|
409
|
+
/** All risk scoring method builders keyed by their AnalysisMethod identifier */
|
|
410
|
+
export const RISK_BUILDERS = {
|
|
411
|
+
'risk-matrix': buildRiskMatrixMarkdown,
|
|
412
|
+
'political-capital-risk': buildPoliticalCapitalRiskMarkdown,
|
|
413
|
+
'quantitative-swot': buildQuantitativeSwotMarkdown,
|
|
414
|
+
'legislative-velocity-risk': buildLegislativeVelocityRiskMarkdown,
|
|
415
|
+
'agent-risk-workflow': buildAgentRiskWorkflowMarkdown,
|
|
416
|
+
};
|
|
417
|
+
//# sourceMappingURL=analysis-risk.js.map
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
import { ArticleCategory } from '../../types/index.js';
|
|
1
|
+
import type { ArticleCategory } from '../../types/index.js';
|
|
2
2
|
import type { ConfidenceLevel } from '../../types/index.js';
|
|
3
|
-
|
|
4
|
-
* Check whether the fetched data contains any substantive EP data.
|
|
5
|
-
*
|
|
6
|
-
* Returns `true` when at least one data category has non-empty arrays.
|
|
7
|
-
* Used to gate analysis execution — analysis should not run on empty data.
|
|
8
|
-
*
|
|
9
|
-
* @param data - Raw fetched data record
|
|
10
|
-
* @returns true if any substantive data is present
|
|
11
|
-
*/
|
|
12
|
-
export declare function hasSubstantiveData(data: Record<string, unknown>): boolean;
|
|
3
|
+
export { hasSubstantiveData } from './analysis-helpers.js';
|
|
13
4
|
/**
|
|
14
5
|
* All analysis methods supported by the analysis pipeline stage.
|
|
15
6
|
*
|
|
@@ -19,7 +10,7 @@ export declare function hasSubstantiveData(data: Record<string, unknown>): boole
|
|
|
19
10
|
* - Risk Scoring (#806): risk-matrix, political-capital-risk, quantitative-swot, legislative-velocity-risk, agent-risk-workflow
|
|
20
11
|
* - Existing: deep-analysis, stakeholder-analysis, coalition-analysis, voting-patterns, cross-session-intelligence
|
|
21
12
|
*/
|
|
22
|
-
export type AnalysisMethod = 'significance-classification' | 'impact-matrix' | 'actor-mapping' | 'forces-analysis' | 'political-threat-landscape' | 'actor-threat-profiling' | 'consequence-trees' | 'legislative-disruption' | 'risk-matrix' | 'political-capital-risk' | 'quantitative-swot' | 'legislative-velocity-risk' | 'agent-risk-workflow' | 'deep-analysis' | 'stakeholder-analysis' | 'coalition-analysis' | 'voting-patterns' | 'cross-session-intelligence' | 'document-analysis';
|
|
13
|
+
export type AnalysisMethod = 'significance-classification' | 'impact-matrix' | 'actor-mapping' | 'forces-analysis' | 'political-threat-landscape' | 'actor-threat-profiling' | 'consequence-trees' | 'legislative-disruption' | 'risk-matrix' | 'political-capital-risk' | 'quantitative-swot' | 'legislative-velocity-risk' | 'agent-risk-workflow' | 'deep-analysis' | 'stakeholder-analysis' | 'coalition-analysis' | 'voting-patterns' | 'cross-session-intelligence' | 'significance-scoring' | 'synthesis-summary' | 'document-analysis';
|
|
23
14
|
/** All analysis methods in default execution order */
|
|
24
15
|
export declare const ALL_ANALYSIS_METHODS: readonly AnalysisMethod[];
|
|
25
16
|
/**
|
|
@@ -35,7 +26,7 @@ export interface AnalysisStageOptions {
|
|
|
35
26
|
readonly articleTypes: readonly ArticleCategory[];
|
|
36
27
|
/** ISO date string (YYYY-MM-DD) for this analysis run */
|
|
37
28
|
readonly date: string;
|
|
38
|
-
/** Base output directory (e.g. 'analysis') */
|
|
29
|
+
/** Base output directory (e.g. 'analysis/daily') */
|
|
39
30
|
readonly outputDir: string;
|
|
40
31
|
/**
|
|
41
32
|
* Filesystem-safe slug identifying the article type for this run.
|
|
@@ -114,16 +105,21 @@ export interface AnalysisContext {
|
|
|
114
105
|
readonly manifest: AnalysisManifest;
|
|
115
106
|
}
|
|
116
107
|
/**
|
|
117
|
-
*
|
|
108
|
+
* Canonical subdirectory for each analysis method group.
|
|
118
109
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
|
|
122
|
-
|
|
110
|
+
* Exported so that agentic workflows and downstream consumers can
|
|
111
|
+
* construct paths that are guaranteed to match the pipeline output.
|
|
112
|
+
*/
|
|
113
|
+
export declare const ANALYSIS_METHOD_SUBDIRS: Readonly<Record<AnalysisMethod, string>>;
|
|
114
|
+
/**
|
|
115
|
+
* Canonical filename for each analysis method.
|
|
123
116
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
|
|
117
|
+
* Exported so that agentic workflows and downstream consumers can
|
|
118
|
+
* reference the exact file names the pipeline produces.
|
|
119
|
+
*/
|
|
120
|
+
export declare const ANALYSIS_METHOD_FILENAMES: Readonly<Record<AnalysisMethod, string>>;
|
|
121
|
+
/**
|
|
122
|
+
* Derive a filesystem-safe slug from a list of article types.
|
|
127
123
|
*
|
|
128
124
|
* @param articleTypes - One or more article category identifiers
|
|
129
125
|
* @returns Filesystem-safe slug (lowercase, alphanumeric + hyphens only)
|
|
@@ -140,27 +136,11 @@ export declare function deriveArticleTypeSlug(articleTypes: readonly (ArticleCat
|
|
|
140
136
|
* Run the full analysis pipeline stage.
|
|
141
137
|
*
|
|
142
138
|
* Executes all enabled analysis methods sequentially, writing markdown files
|
|
143
|
-
* and a `manifest.json` summary.
|
|
144
|
-
* is provided the output is scoped to `outputDir/{date}/{slug}/` — this prevents
|
|
145
|
-
* merge conflicts when multiple agentic workflows run on the same date.
|
|
139
|
+
* and a `manifest.json` summary.
|
|
146
140
|
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* @param fetchedData - Raw EP data fetched by the fetch stage (keyed by data type)
|
|
141
|
+
* @param fetchedData - Raw EP data fetched by the fetch stage
|
|
150
142
|
* @param options - Analysis stage configuration
|
|
151
143
|
* @returns Analysis context object for consumption by the generate stage
|
|
152
|
-
*
|
|
153
|
-
* @example
|
|
154
|
-
* ```ts
|
|
155
|
-
* const ctx = await runAnalysisStage(fetchedData, {
|
|
156
|
-
* articleTypes: [ArticleCategory.WEEK_AHEAD],
|
|
157
|
-
* date: '2026-03-26',
|
|
158
|
-
* outputDir: 'analysis',
|
|
159
|
-
* articleTypeSlug: 'week-ahead',
|
|
160
|
-
* skipCompleted: true,
|
|
161
|
-
* verbose: true,
|
|
162
|
-
* });
|
|
163
|
-
* ```
|
|
164
144
|
*/
|
|
165
145
|
export declare function runAnalysisStage(fetchedData: Record<string, unknown>, options: AnalysisStageOptions): Promise<AnalysisContext>;
|
|
166
146
|
//# sourceMappingURL=analysis-stage.d.ts.map
|