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.
Files changed (60) hide show
  1. package/package.json +7 -7
  2. package/scripts/constants/language-articles.d.ts +4 -0
  3. package/scripts/constants/language-articles.js +20 -0
  4. package/scripts/constants/language-ui.d.ts +8 -8
  5. package/scripts/constants/language-ui.js +64 -64
  6. package/scripts/constants/languages.d.ts +2 -2
  7. package/scripts/constants/languages.js +2 -2
  8. package/scripts/generators/news-enhanced.js +13 -3
  9. package/scripts/generators/pipeline/analysis-classification.d.ts +49 -0
  10. package/scripts/generators/pipeline/analysis-classification.js +333 -0
  11. package/scripts/generators/pipeline/analysis-existing.d.ts +67 -0
  12. package/scripts/generators/pipeline/analysis-existing.js +547 -0
  13. package/scripts/generators/pipeline/analysis-helpers.d.ts +140 -0
  14. package/scripts/generators/pipeline/analysis-helpers.js +266 -0
  15. package/scripts/generators/pipeline/analysis-risk.d.ts +49 -0
  16. package/scripts/generators/pipeline/analysis-risk.js +417 -0
  17. package/scripts/generators/pipeline/analysis-stage.d.ts +19 -39
  18. package/scripts/generators/pipeline/analysis-stage.js +219 -1704
  19. package/scripts/generators/pipeline/analysis-threats.d.ts +41 -0
  20. package/scripts/generators/pipeline/analysis-threats.js +142 -0
  21. package/scripts/generators/pipeline/fetch-stage.d.ts +25 -15
  22. package/scripts/generators/pipeline/fetch-stage.js +293 -117
  23. package/scripts/generators/strategies/article-strategy.d.ts +126 -7
  24. package/scripts/generators/strategies/article-strategy.js +491 -1
  25. package/scripts/generators/strategies/breaking-news-strategy.js +98 -8
  26. package/scripts/generators/strategies/committee-reports-strategy.js +23 -2
  27. package/scripts/generators/strategies/month-ahead-strategy.js +23 -2
  28. package/scripts/generators/strategies/monthly-review-strategy.js +13 -1
  29. package/scripts/generators/strategies/motions-strategy.js +15 -1
  30. package/scripts/generators/strategies/propositions-strategy.js +15 -1
  31. package/scripts/generators/strategies/week-ahead-strategy.js +19 -1
  32. package/scripts/generators/strategies/weekly-review-strategy.js +17 -1
  33. package/scripts/generators/synthesis-summary.d.ts +93 -0
  34. package/scripts/generators/synthesis-summary.js +364 -0
  35. package/scripts/index.d.ts +5 -2
  36. package/scripts/index.js +6 -1
  37. package/scripts/mcp/ep-mcp-client.d.ts +34 -1
  38. package/scripts/mcp/ep-mcp-client.js +110 -2
  39. package/scripts/mcp/mcp-connection.d.ts +3 -1
  40. package/scripts/mcp/mcp-connection.js +35 -4
  41. package/scripts/templates/article-template.js +24 -22
  42. package/scripts/templates/section-builders.js +2 -5
  43. package/scripts/types/index.d.ts +2 -1
  44. package/scripts/types/mcp.d.ts +7 -0
  45. package/scripts/types/political-classification.d.ts +1 -1
  46. package/scripts/types/quality.d.ts +9 -6
  47. package/scripts/types/significance.d.ts +130 -0
  48. package/scripts/types/significance.js +4 -0
  49. package/scripts/utils/article-quality-scorer.d.ts +13 -11
  50. package/scripts/utils/article-quality-scorer.js +36 -23
  51. package/scripts/utils/file-utils.d.ts +2 -2
  52. package/scripts/utils/file-utils.js +2 -2
  53. package/scripts/utils/html-sanitize.d.ts +10 -0
  54. package/scripts/utils/html-sanitize.js +32 -0
  55. package/scripts/utils/political-classification.d.ts +8 -7
  56. package/scripts/utils/political-classification.js +8 -7
  57. package/scripts/utils/political-risk-assessment.d.ts +1 -1
  58. package/scripts/utils/political-risk-assessment.js +1 -1
  59. package/scripts/utils/significance-scoring.d.ts +97 -0
  60. 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
- * Derive a filesystem-safe slug from a list of article types.
108
+ * Canonical subdirectory for each analysis method group.
118
109
  *
119
- * Each agentic workflow runs a single article type (e.g. `week-ahead`).
120
- * The slug is used to scope analysis output to
121
- * `{outputDir}/{date}/{slug}/` so that concurrent workflows for different
122
- * article types never collide on the same files.
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
- * When multiple types are present the slug is the sorted, hyphen-joined list.
125
- * The result is sanitised to contain only lowercase alphanumeric characters
126
- * and hyphens, preventing path traversal or filesystem issues.
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. When {@link AnalysisStageOptions.articleTypeSlug}
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
- * Individual method failures are isolated other methods continue regardless.
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