From 8652d5d22ca90621df11c537d8f5b6846533b691 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 15:48:56 +0000 Subject: [PATCH 01/24] Initial plan From aacd59e57ea7ca483efb384a82f61dc936c59d9f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 18:23:47 +0000 Subject: [PATCH 02/24] feat: add dedicated interpellations content generator with accountability analysis Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .../constants/content-labels-part1.ts | 63 +++++ .../constants/content-labels-part2.ts | 63 +++++ .../data-transformers/content-generators.ts | 1 + .../content-generators/index.ts | 2 + .../content-generators/interpellations.ts | 238 ++++++++++++++++++ scripts/data-transformers/index.ts | 5 +- scripts/types/content.ts | 9 + 7 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 scripts/data-transformers/content-generators/interpellations.ts diff --git a/scripts/data-transformers/constants/content-labels-part1.ts b/scripts/data-transformers/constants/content-labels-part1.ts index dbc925d4b61..3f56d956cf8 100644 --- a/scripts/data-transformers/constants/content-labels-part1.ts +++ b/scripts/data-transformers/constants/content-labels-part1.ts @@ -69,6 +69,15 @@ export const CONTENT_LABELS_PART1: Partial> = { propsBreakdown: (n: number): string => `The government has submitted ${n} new propositions, signalling its policy priorities and the pace of its legislative agenda. Each proposition must navigate committee review and chamber debate, providing insight into the coalition's strategic direction and its ability to build cross-party support.`, motionsBreakdown: (n: number): string => `Opposition MPs have filed ${n} new motions, mapping the political fault lines in the current Riksdag. These motions reveal not just policy disagreements but the strategic positioning of parties as they prepare for the next electoral contest.` , + noInterpellations: 'No interpellations available at this time.', + interpellationDefault: 'Parliamentary interpellation addressed to a minister.', + interpellationsBreakdown: (n: number): string => `Opposition MPs have filed ${n} interpellations demanding ministerial accountability. Interpellations are formal parliamentary questions that ministers must answer in chamber debate, making them a key tool of parliamentary scrutiny.`, + debateDynamics: 'Debate Dynamics & Accountability', + ministerAccountability: 'Minister Response', + accountabilityAnalysis: 'Accountability Analysis', + readFullInterpellation: 'Read the full interpellation', + interpellationBy: 'Interpellation by', + committeeCountContext: (n: number): string => `${n} reports from this committee signal intensive legislative work within its portfolio.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Parliamentary committees have been active across ${committees} and ${extra} further policy domains.` : `Parliamentary committees have been active across ${committees}.`, committeeMomentumTakeaway: (n: number): string => `A total of ${n} reports demonstrates sustained legislative momentum and ongoing policy prioritisation.`, @@ -170,6 +179,15 @@ export const CONTENT_LABELS_PART1: Partial> = { propsBreakdown: (n: number): string => `Regeringen har överlämnat ${n} nya propositioner, som signalerar dess politiska prioriteringar och takten i den lagstiftande agendan.`, motionsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} nya motioner som kartlägger de politiska skiljelinjerna i nuvarande riksdag.` , + noInterpellations: 'Inga interpellationer tillgängliga för tillfället.', + interpellationDefault: 'Riksdagsinterpellation till en minister.', + interpellationsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} interpellationer som kräver ministeransvar. Interpellationer är formella parlamentariska frågor som ministrar måste besvara i kammaren, vilket gör dem till ett viktigt verktyg för parlamentarisk granskning.`, + debateDynamics: 'Debattdynamik och ansvarsutkrävande', + ministerAccountability: 'Ministersvar', + accountabilityAnalysis: 'Ansvarsutkrävande', + readFullInterpellation: 'Läs hela interpellationen', + interpellationBy: 'Interpellation av', + committeeCountContext: (n: number): string => `${n} betänkanden från detta utskott signalerar intensivt lagstiftningsarbete inom dess ansvarsområde.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Riksdagens utskott har varit aktiva inom ${committees} och ${extra} ytterligare områden.` : `Riksdagens utskott har varit aktiva inom ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Totalt ${n} betänkanden visar lagstiftande momentum och pågående politisk prioritering.`, @@ -271,6 +289,15 @@ export const CONTENT_LABELS_PART1: Partial> = { propsBreakdown: (n: number): string => `Regeringen har fremsat ${n} nye lovforslag.`, motionsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} nye forslag.` , + noInterpellations: 'Ingen interpellationer tilgængelige på nuværende tidspunkt.', + interpellationDefault: 'Parlamentarisk interpellation til en minister.', + interpellationsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} interpellationer, der kræver ministeransvar.`, + debateDynamics: 'Debatdynamik og ansvarlighed', + ministerAccountability: 'Ministersvar', + accountabilityAnalysis: 'Ansvarlighed', + readFullInterpellation: 'Læs den fulde interpellation', + interpellationBy: 'Interpellation af', + committeeCountContext: (n: number): string => `${n} betænkninger fra dette udvalg signalerer intensivt lovgivningsarbejde.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Parlamentets udvalg har været aktive inden for ${committees} og ${extra} yderligere politikområder.` : `Parlamentets udvalg har været aktive inden for ${committees}.`, committeeMomentumTakeaway: (n: number): string => `I alt ${n} betænkninger viser vedvarende lovgivningsmæssig fremdrift.`, @@ -372,6 +399,15 @@ export const CONTENT_LABELS_PART1: Partial> = { propsBreakdown: (n: number): string => `Regjeringen har fremmet ${n} nye proposisjoner.`, motionsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} nye forslag.` , + noInterpellations: 'Ingen interpellasjoner tilgjengelige for øyeblikket.', + interpellationDefault: 'Parlamentarisk interpellasjon til en statsråd.', + interpellationsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} interpellasjoner som krever statsrådets ansvar.`, + debateDynamics: 'Debattdynamikk og ansvarlighet', + ministerAccountability: 'Statsrådssvar', + accountabilityAnalysis: 'Ansvarlighetsanalyse', + readFullInterpellation: 'Les hele interpellasjonen', + interpellationBy: 'Interpellasjon av', + committeeCountContext: (n: number): string => `${n} innstillinger fra denne komiteen signaliserer intensivt lovgivningsarbeid.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Stortingets komiteer har vært aktive innen ${committees} og ${extra} ytterligere politikkområder.` : `Stortingets komiteer har vært aktive innen ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Totalt ${n} innstillinger viser vedvarende lovgivende fremdrift.`, @@ -473,6 +509,15 @@ export const CONTENT_LABELS_PART1: Partial> = { propsBreakdown: (n: number): string => `Hallitus on jättänyt ${n} uutta esitystä.`, motionsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} uutta aloitetta.` , + noInterpellations: 'Ei välikysymyksiä saatavilla tällä hetkellä.', + interpellationDefault: 'Parlamentaarinen välikysymys ministerille.', + interpellationsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} välikysymystä ministereiden vastuullisuuden vaatimiseksi.`, + debateDynamics: 'Väittelyn dynamiikka ja vastuullisuus', + ministerAccountability: 'Ministerin vastaus', + accountabilityAnalysis: 'Vastuullisuusanalyysi', + readFullInterpellation: 'Lue koko välikysymys', + interpellationBy: 'Välikysymys esittäjältä', + committeeCountContext: (n: number): string => `${n} mietintöä tästä valiokunnasta osoittavat intensiivistä lainsäädäntötyötä.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Eduskunnan valiokunnat ovat olleet aktiivisia aloilla ${committees} ja ${extra} muulla politiikka-alueella.` : `Eduskunnan valiokunnat ovat olleet aktiivisia aloilla ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Yhteensä ${n} mietintöä osoittaa jatkuvaa lainsäädännöllistä vauhtia.`, @@ -574,6 +619,15 @@ export const CONTENT_LABELS_PART1: Partial> = { propsBreakdown: (n: number): string => `Die Regierung hat ${n} neue Vorlagen eingebracht.`, motionsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} neue Anträge eingereicht.` , + noInterpellations: 'Derzeit keine Interpellationen verfügbar.', + interpellationDefault: 'Parlamentarische Interpellation an einen Minister.', + interpellationsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} Interpellationen eingereicht, um Ministerverantwortung einzufordern.`, + debateDynamics: 'Debattendynamik und Verantwortlichkeit', + ministerAccountability: 'Ministerantwort', + accountabilityAnalysis: 'Verantwortlichkeitsanalyse', + readFullInterpellation: 'Vollständige Interpellation lesen', + interpellationBy: 'Interpellation von', + committeeCountContext: (n: number): string => `${n} Berichte dieses Ausschusses signalisieren intensive Gesetzgebungsarbeit.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Die Parlamentsausschüsse waren in den Bereichen ${committees} und ${extra} weiteren Politikfeldern aktiv.` : `Die Parlamentsausschüsse waren in den Bereichen ${committees} aktiv.`, committeeMomentumTakeaway: (n: number): string => `Insgesamt ${n} Berichte zeugen von anhaltendem Gesetzgebungsmomentum.`, @@ -675,6 +729,15 @@ export const CONTENT_LABELS_PART1: Partial> = { propsBreakdown: (n: number): string => `Le gouvernement a soumis ${n} nouvelles propositions.`, motionsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} nouvelles motions.` , + noInterpellations: 'Aucune interpellation disponible pour le moment.', + interpellationDefault: 'Interpellation parlementaire adressée à un ministre.', + interpellationsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} interpellations exigeant la responsabilité ministérielle.`, + debateDynamics: 'Dynamique du débat et responsabilité', + ministerAccountability: 'Réponse du ministre', + accountabilityAnalysis: 'Analyse de la responsabilité', + readFullInterpellation: 'Lire l\'interpellation complète', + interpellationBy: 'Interpellation par', + committeeCountContext: (n: number): string => `${n} rapports de cette commission témoignent d'un travail législatif intensif.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Les commissions parlementaires ont été actives dans les domaines ${committees} et ${extra} autres domaines politiques.` : `Les commissions parlementaires ont été actives dans les domaines ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Un total de ${n} rapports démontre un élan législatif soutenu.`, diff --git a/scripts/data-transformers/constants/content-labels-part2.ts b/scripts/data-transformers/constants/content-labels-part2.ts index 77a6b75a4d7..f76fd17f0bd 100644 --- a/scripts/data-transformers/constants/content-labels-part2.ts +++ b/scripts/data-transformers/constants/content-labels-part2.ts @@ -69,6 +69,15 @@ export const CONTENT_LABELS_PART2: Partial> = { propsBreakdown: (n: number): string => `El gobierno ha presentado ${n} nuevas proposiciones.`, motionsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} nuevas mociones.` , + noInterpellations: 'No hay interpelaciones disponibles en este momento.', + interpellationDefault: 'Interpelación parlamentaria dirigida a un ministro.', + interpellationsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} interpelaciones exigiendo responsabilidad ministerial.`, + debateDynamics: 'Dinámica del debate y responsabilidad', + ministerAccountability: 'Respuesta del ministro', + accountabilityAnalysis: 'Análisis de responsabilidad', + readFullInterpellation: 'Leer la interpelación completa', + interpellationBy: 'Interpelación de', + committeeCountContext: (n: number): string => `${n} informes de esta comisión señalan un trabajo legislativo intensivo.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Las comisiones parlamentarias han estado activas en las áreas de ${committees} y ${extra} dominios políticos más.` : `Las comisiones parlamentarias han estado activas en las áreas de ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Un total de ${n} informes demuestra un impulso legislativo sostenido.`, @@ -170,6 +179,15 @@ export const CONTENT_LABELS_PART2: Partial> = { propsBreakdown: (n: number): string => `De regering heeft ${n} nieuwe voorstellen ingediend.`, motionsBreakdown: (n: number): string => `Oppositieleden hebben ${n} nieuwe moties ingediend.` , + noInterpellations: 'Geen interpellaties beschikbaar op dit moment.', + interpellationDefault: 'Parlementaire interpellatie aan een minister.', + interpellationsBreakdown: (n: number): string => `Oppositieleden hebben ${n} interpellaties ingediend om ministeriële verantwoording te eisen.`, + debateDynamics: 'Debatdynamiek en verantwoording', + ministerAccountability: 'Ministerantwoord', + accountabilityAnalysis: 'Verantwoordingsanalyse', + readFullInterpellation: 'Lees de volledige interpellatie', + interpellationBy: 'Interpellatie door', + committeeCountContext: (n: number): string => `${n} rapporten van deze commissie signaleren intensief wetgevend werk.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `De parlementaire commissies zijn actief geweest op het gebied van ${committees} en ${extra} verdere beleidsterreinen.` : `De parlementaire commissies zijn actief geweest op het gebied van ${committees}.`, committeeMomentumTakeaway: (n: number): string => `In totaal ${n} rapporten tonen aanhoudend wetgevend momentum.`, @@ -271,6 +289,15 @@ export const CONTENT_LABELS_PART2: Partial> = { propsBreakdown: (n: number): string => `قدمت الحكومة ${n} مقترحات جديدة.`, motionsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} اقتراحات جديدة.` , + noInterpellations: 'لا توجد استجوابات متاحة حالياً.', + interpellationDefault: 'استجواب برلماني موجه إلى وزير.', + interpellationsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} استجواباً يطالبون فيه بمساءلة الوزراء.`, + debateDynamics: 'ديناميكيات النقاش والمساءلة', + ministerAccountability: 'رد الوزير', + accountabilityAnalysis: 'تحليل المساءلة', + readFullInterpellation: 'قراءة الاستجواب الكامل', + interpellationBy: 'استجواب من', + committeeCountContext: (n: number): string => `${n} تقارير من هذه اللجنة تشير إلى عمل تشريعي مكثف.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `كانت اللجان البرلمانية نشطة في مجالات ${committees} و${extra} مجالات سياسية أخرى.` : `كانت اللجان البرلمانية نشطة في مجالات ${committees}.`, committeeMomentumTakeaway: (n: number): string => `إجمالي ${n} تقارير يدل على زخم تشريعي مستمر.`, @@ -372,6 +399,15 @@ export const CONTENT_LABELS_PART2: Partial> = { propsBreakdown: (n: number): string => `הממשלה הגישה ${n} הצעות חדשות.`, motionsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} הצעות חדשות.` , + noInterpellations: 'אין אינטרפלציות זמינות כרגע.', + interpellationDefault: 'אינטרפלציה פרלמנטרית המופנית לשר.', + interpellationsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} אינטרפלציות הדורשות אחריות שרים.`, + debateDynamics: 'דינמיקת הדיון ואחריות', + ministerAccountability: 'תגובת השר', + accountabilityAnalysis: 'ניתוח אחריות', + readFullInterpellation: 'קרא את האינטרפלציה המלאה', + interpellationBy: 'אינטרפלציה מאת', + committeeCountContext: (n: number): string => `${n} דוחות מוועדה זו מעידים על עבודת חקיקה אינטנסיבית.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `ועדות הפרלמנט היו פעילות בתחומי ${committees} ו-${extra} תחומי מדיניות נוספים.` : `ועדות הפרלמנט היו פעילות בתחומי ${committees}.`, committeeMomentumTakeaway: (n: number): string => `סך של ${n} דוחות מעיד על מומנטום חקיקתי מתמשך.`, @@ -473,6 +509,15 @@ export const CONTENT_LABELS_PART2: Partial> = { propsBreakdown: (n: number): string => `政府は${n}件の新たな法案を提出しました。`, motionsBreakdown: (n: number): string => `野党議員が${n}件の新たな動議を提出しました。` , + noInterpellations: '現在、質問主意書はありません。', + interpellationDefault: '大臣に宛てた議会質問主意書。', + interpellationsBreakdown: (n: number): string => `野党議員が${n}件の質問主意書を提出し、大臣の説明責任を求めています。`, + debateDynamics: '討論の動態と説明責任', + ministerAccountability: '大臣の回答', + accountabilityAnalysis: '説明責任分析', + readFullInterpellation: '質問主意書全文を読む', + interpellationBy: '質問主意書提出者', + committeeCountContext: (n: number): string => `この委員会からの${n}件の報告書は、集中的な立法作業を示しています。`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `議会の委員会は${committees}および他${extra}の政策分野で活動しています。` : `議会の委員会は${committees}の分野で活動しています。`, committeeMomentumTakeaway: (n: number): string => `合計${n}件の報告書は、持続的な立法の勢いを示しています。`, @@ -574,6 +619,15 @@ export const CONTENT_LABELS_PART2: Partial> = { propsBreakdown: (n: number): string => `정부가 ${n}개의 새 법안을 제출했습니다.`, motionsBreakdown: (n: number): string => `야당 의원들이 ${n}개의 새 동의안을 제출했습니다.` , + noInterpellations: '현재 대정부 질의가 없습니다.', + interpellationDefault: '장관에게 보내는 의회 대정부 질의.', + interpellationsBreakdown: (n: number): string => `야당 의원들이 장관의 책임을 요구하는 ${n}개의 대정부 질의를 제출했습니다.`, + debateDynamics: '토론 역학 및 책임성', + ministerAccountability: '장관 답변', + accountabilityAnalysis: '책임성 분석', + readFullInterpellation: '전체 대정부 질의 읽기', + interpellationBy: '질의 제출자', + committeeCountContext: (n: number): string => `이 위원회의 ${n}건의 보고서는 집중적인 입법 작업을 나타냅니다.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `의회 위원회는 ${committees} 및 ${extra}개의 추가 정책 분야에서 활동했습니다.` : `의회 위원회는 ${committees} 분야에서 활동했습니다.`, committeeMomentumTakeaway: (n: number): string => `총 ${n}건의 보고서는 지속적인 입법 추진력을 보여줍니다.`, @@ -675,6 +729,15 @@ export const CONTENT_LABELS_PART2: Partial> = { propsBreakdown: (n: number): string => `政府提交了${n}项新提案。`, motionsBreakdown: (n: number): string => `反对党议员提交了${n}项新动议。` , + noInterpellations: '目前没有质询可用。', + interpellationDefault: '向部长提出的议会质询。', + interpellationsBreakdown: (n: number): string => `反对党议员提交了${n}项质询,要求追究部长责任。`, + debateDynamics: '辩论动态与问责制', + ministerAccountability: '部长答复', + accountabilityAnalysis: '问责分析', + readFullInterpellation: '阅读完整质询', + interpellationBy: '质询提出者', + committeeCountContext: (n: number): string => `该委员会的${n}份报告表明密集的立法工作。`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `议会委员会在${committees}以及其他${extra}个政策领域积极开展工作。` : `议会委员会在${committees}领域积极开展工作。`, committeeMomentumTakeaway: (n: number): string => `共${n}份报告展示了持续的立法势头。`, diff --git a/scripts/data-transformers/content-generators.ts b/scripts/data-transformers/content-generators.ts index e24fb062292..6e3f55bcddc 100644 --- a/scripts/data-transformers/content-generators.ts +++ b/scripts/data-transformers/content-generators.ts @@ -16,6 +16,7 @@ export { generateCommitteeContent, generatePropositionsContent, generateMotionsContent, + generateInterpellationsContent, generateGenericContent, generateMonthlyReviewContent, generateMonthAheadContent, diff --git a/scripts/data-transformers/content-generators/index.ts b/scripts/data-transformers/content-generators/index.ts index a05f0d9bbb6..0331a4eaabb 100644 --- a/scripts/data-transformers/content-generators/index.ts +++ b/scripts/data-transformers/content-generators/index.ts @@ -7,6 +7,7 @@ * - **committee** — generateCommitteeContent * - **propositions** — generatePropositionsContent * - **motions** — generateMotionsContent + * - **interpellations** — generateInterpellationsContent * - **generic** — generateGenericContent * - **monthly-review** — generateMonthlyReviewContent * - **month-ahead** — generateMonthAheadContent @@ -27,6 +28,7 @@ export { generateWeekAheadContent } from './week-ahead.js'; export { generateCommitteeContent } from './committee.js'; export { generatePropositionsContent } from './propositions.js'; export { generateMotionsContent } from './motions.js'; +export { generateInterpellationsContent } from './interpellations.js'; export { generateGenericContent } from './generic.js'; export { generateMonthlyReviewContent } from './monthly-review.js'; export { generateMonthAheadContent } from './month-ahead.js'; diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts new file mode 100644 index 00000000000..eb791972a38 --- /dev/null +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -0,0 +1,238 @@ +/** + * @module data-transformers/content-generators/interpellations + * @description Generator for "interpellations" article content. Renders parliamentary + * interpellations with debate dynamics analysis, minister accountability assessment, + * and political scrutiny context. + * + * Interpellations are formal parliamentary questions that opposition MPs address to + * ministers, who must respond in chamber debate. They are a key accountability tool + * in the Swedish Riksdag, distinct from opposition motions in purpose and procedure. + * + * @author Hack23 AB + * @license Apache-2.0 + */ + +import { generateDeepAnalysisSection } from './shared.js'; +import { escapeHtml } from '../../html-utils.js'; +import type { Language } from '../../types/language.js'; +import type { ArticleContentData, RawDocument } from '../types.js'; +import { getPillarTransition } from '../../editorial-pillars.js'; +import { + L, + svSpan, + sanitizeUrl, + normalizePartyKey, + formatDocumentDate, +} from '../helpers.js'; +import { detectPolicyDomains, generateDeepPolicyAnalysis } from '../policy-analysis.js'; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +/** + * Extract minister name or responsible department from an interpellation document. + * Interpellations are directed to a specific minister; this information may appear + * in the document title, summary, or mottagare field. + */ +function extractMinisterTarget(doc: RawDocument): string { + // Some MCP responses include a mottagare (recipient) field + const mottagare = (doc as Record)['mottagare']; + if (typeof mottagare === 'string' && mottagare.trim()) { + return mottagare.trim(); + } + // Fall back to looking for "till X statsråd" / "till statsminister" pattern in the title + const titleText = doc.titel || doc.title || ''; + const toMatch = titleText.match(/(?:till|to|an|à)\s+([^–—\-]{3,60}?)(?:\s*[-–—]|\s*$)/i); + if (toMatch?.[1]) { + return toMatch[1].trim(); + } + return ''; +} + +/** + * Render a single interpellation entry as an HTML block. + */ +function renderInterpellationEntry(doc: RawDocument, lang: Language | string): string { + const titleText = doc.titel || doc.title || ''; + const escapedTitle = escapeHtml(titleText); + const titleHtml = (doc.titel && !doc.title) + ? svSpan(escapedTitle, lang) + : escapedTitle; + + const docName = escapeHtml(doc.dokumentnamn || doc.dok_id || titleText); + const authorText = escapeHtml(doc.intressent_namn || doc.author || ''); + const partyText = escapeHtml(normalizePartyKey(doc.parti).toUpperCase()); + const dateHtml = formatDocumentDate(doc, lang); + const ministerTarget = extractMinisterTarget(doc); + + const whyItMattersLabel = L(lang, 'whyItMatters'); + const interpellationByLabel = L(lang, 'interpellationBy'); + const ministerAccountabilityLabel = L(lang, 'ministerAccountability'); + const readFullLabel = L(lang, 'readFullInterpellation'); + const defaultText = L(lang, 'interpellationDefault'); + + // Policy analysis — explains what accountability issue is being raised + // 'ip' = interpellation document type code (riksdag.se document type abbreviation) + const policyAnalysis = generateDeepPolicyAnalysis(doc, lang, 'ip'); + + let html = `\n
\n`; + html += `

${titleHtml}

\n`; + if (dateHtml) html += `

${dateHtml}

\n`; + + if (authorText || partyText) { + const authorLine = [ + authorText ? `${escapeHtml(String(interpellationByLabel))}: ${authorText}` : '', + partyText ? `${escapeHtml(String(L(lang, 'party')))}: ${partyText}` : '', + ].filter(Boolean).join('  |  '); + html += `

${authorLine}

\n`; + } + + if (ministerTarget) { + html += `

${escapeHtml(String(ministerAccountabilityLabel))}: ${escapeHtml(ministerTarget)}

\n`; + } + + const summaryText = doc.summary || doc.notis + ? (doc.summary || doc.notis || '') + : String(defaultText); + const summaryHtml = (doc.titel && !doc.title && (doc.summary || doc.notis)) + ? svSpan(escapeHtml(summaryText), lang) + : escapeHtml(summaryText); + + html += `

${summaryHtml}

\n`; + html += `

${escapeHtml(String(whyItMattersLabel))}: ${policyAnalysis}

\n`; + + if (doc.url) { + html += `

${escapeHtml(String(readFullLabel))}: ${docName}

\n`; + } + + html += `
\n`; + return html; +} + +// --------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------- + +/** + * Generate article content for the "interpellations" article type. + * + * Produces a structured accountability-focused article covering: + * 1. **Lede** — Count and significance of interpellations filed + * 2. **Debate Dynamics** — Per-party breakdown of accountability issues raised + * 3. **Thematic analysis** — Interpellations grouped by policy domain + * 4. **Deep Analysis** — 5W framework (who, what, when, why, impact) + * 5. **Coalition Dynamics** — Cross-party accountability patterns + * + * @param data - Article content data (interpellations loaded in `data.motions`) + * @param lang - Target language + * @returns Generated HTML content string + */ +export function generateInterpellationsContent(data: ArticleContentData, lang: Language | string): string { + // Interpellations are stored in data.motions by the data pipeline + // (for backward compatibility) but may also appear in data.documents. + const interpellations: RawDocument[] = data.motions || data.documents || []; + + let content = `

${L(lang, 'interpellationsTag')}

\n`; + + if (interpellations.length === 0) { + content += `

${L(lang, 'noInterpellations')}

\n`; + return content; + } + + // Analytical lede paragraph + const breakdownFn = L(lang, 'interpellationsBreakdown') as string | ((n: number) => string); + const breakdownText = typeof breakdownFn === 'function' + ? breakdownFn(interpellations.length) + : `${interpellations.length} new interpellations filed.`; + content += `

${escapeHtml(String(breakdownText))}

\n`; + + // Group by party for party accountability overview + const byParty: Record = {}; + interpellations.forEach(ip => { + const party = normalizePartyKey(ip.parti); + if (!byParty[party]) byParty[party] = []; + byParty[party].push(ip); + }); + const partyCount = Object.keys(byParty).filter(p => p !== 'other').length; + + // Debate dynamics section — how many parties are holding ministers accountable + content += `\n

${L(lang, 'debateDynamics')}

\n`; + if (partyCount > 1) { + const strategyFn = L(lang, 'oppositionStrategyContext') as string | ((n: number) => string); + const strategyContext = typeof strategyFn === 'function' + ? strategyFn(partyCount) + : `Interpellations from ${partyCount} different parties demonstrate broad parliamentary scrutiny.`; + content += `

${escapeHtml(String(strategyContext))}

\n`; + } + + // Group interpellations by policy domain for thematic accountability analysis + const byTheme: Record = {}; + interpellations.forEach(ip => { + const domains = detectPolicyDomains(ip, lang); + const theme = domains[0] || String(L(lang, 'generalMatters')); + if (!byTheme[theme]) byTheme[theme] = []; + byTheme[theme].push(ip); + }); + const themeCount = Object.keys(byTheme).length; + + // Thematic analysis — grouped by accountability domain + if (themeCount > 1) { + content += `\n

${L(lang, 'thematicAnalysis')}

\n`; + Object.entries(byTheme).forEach(([theme, themeIps]) => { + content += `\n

${escapeHtml(theme)} (${themeIps.length})

\n`; + themeIps.forEach(ip => { + const entryHtml = renderInterpellationEntry(ip, lang); + // Demote h3 → h4 inside a themed section to maintain hierarchy + content += entryHtml + .replace(/]*)?>/g, '') + .replace(/<\/h3>/g, ''); + }); + }); + } else { + // Single theme or ungrouped: flat list under the debate dynamics section + interpellations.forEach(ip => { + content += renderInterpellationEntry(ip, lang); + }); + } + + // Accountability Analysis section + content += `\n

${L(lang, 'accountabilityAnalysis')}

\n`; + const policyImpFn = L(lang, 'policyImplicationsContext') as string | ((p: number, d: number) => string); + const policyImpText = typeof policyImpFn === 'function' + ? policyImpFn(interpellations.length, themeCount) + : `These ${interpellations.length} interpellations span ${themeCount} policy domain${themeCount !== 1 ? 's' : ''}, reflecting the breadth of parliamentary accountability demands.`; + content += `

${escapeHtml(String(policyImpText))}

\n`; + + // Deep Analysis section (5W framework) + content += generateDeepAnalysisSection({ + documents: interpellations, + lang, + cia: data.ciaContext, + articleType: 'interpellations', + }); + + // Narrative bridge before coalition dynamics (inter-pillar transition) + const watchTransition = getPillarTransition(lang, 'watchToOpposition'); + if (watchTransition) { + content += `

${escapeHtml(watchTransition)}

\n`; + } + + // Coalition dynamics — which parties are driving accountability + if (partyCount > 0) { + content += `\n

${L(lang, 'coalitionDynamics')}

\n`; + content += `
\n
    \n`; + Object.entries(byParty).forEach(([party, partyIps]) => { + if (party !== 'other') { + const detailFn = L(lang, 'partyMotionsFiled') as string | ((party: string, n: number) => string); + const detail = typeof detailFn === 'function' + ? detailFn(party, partyIps.length) + : `${party}: ${partyIps.length} interpellations filed`; + content += `
  • ${escapeHtml(String(detail))}
  • \n`; + } + }); + content += `
\n
\n`; + } + + return content; +} diff --git a/scripts/data-transformers/index.ts b/scripts/data-transformers/index.ts index aae4cabf14f..9759bf1875c 100644 --- a/scripts/data-transformers/index.ts +++ b/scripts/data-transformers/index.ts @@ -61,6 +61,7 @@ export { generateMindmapSection, generateSankeySection, generateCiaOverviewSection, + generateInterpellationsContent, } from './content-generators.js'; export type { SwotSectionOptions, @@ -98,6 +99,7 @@ import { generateCommitteeContent, generatePropositionsContent, generateMotionsContent, + generateInterpellationsContent, generateGenericContent, generateMonthlyReviewContent, generateMonthAheadContent, @@ -127,8 +129,9 @@ export function generateArticleContent( case 'propositions': return generatePropositionsContent(data, lang); case 'motions': - case 'interpellations': return generateMotionsContent(data, lang); + case 'interpellations': + return generateInterpellationsContent(data, lang); case 'monthly-review': return generateMonthlyReviewContent(data, lang); case 'weekly-review': diff --git a/scripts/types/content.ts b/scripts/types/content.ts index 941f2cfb428..0c7416b4d94 100644 --- a/scripts/types/content.ts +++ b/scripts/types/content.ts @@ -96,6 +96,15 @@ export interface ContentLabelSet { committeeBreakdown: (n: number, c: number) => string; propsBreakdown: (n: number) => string; motionsBreakdown: (n: number) => string; + // Interpellations-specific labels + noInterpellations: string; + interpellationDefault: string; + interpellationsBreakdown: (n: number) => string; + debateDynamics: string; + ministerAccountability: string; + accountabilityAnalysis: string; + readFullInterpellation: string; + interpellationBy: string; // Localized body text labels (avoid hardcoded en/sv fallbacks) committeeCountContext: (n: number) => string; committeeActivityTakeaway: (committees: string, extra: number) => string; From 6ea42bee8b3f4a39fb37eb27718fa8672cbd31c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 17:25:01 +0000 Subject: [PATCH 03/24] fix: address all 7 PR review comments for interpellations generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tighten extractMinisterTarget() regex to require statsråd/minister terms - Skip party display when parti is missing/unknown (no "OTHER" shown) - Add interpellationsScrutinyContext label (replaces motion-specific oppositionStrategyContext) - Add interpellationsAccountabilityContext label (replaces proposition-specific policyImplicationsContext) - Add partyInterpellationsFiled label (replaces motion-specific partyMotionsFiled) - Change ministerAccountability label to "Directed to" across all 14 languages - Add comprehensive test suite (170 tests) Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .../constants/content-labels-part1.ts | 35 ++- .../constants/content-labels-part2.ts | 35 ++- .../content-generators/interpellations.ts | 40 ++- scripts/types/content.ts | 7 + tests/interpellations.test.ts | 297 ++++++++++++++++++ 5 files changed, 383 insertions(+), 31 deletions(-) create mode 100644 tests/interpellations.test.ts diff --git a/scripts/data-transformers/constants/content-labels-part1.ts b/scripts/data-transformers/constants/content-labels-part1.ts index 3f56d956cf8..5bb4f32ed85 100644 --- a/scripts/data-transformers/constants/content-labels-part1.ts +++ b/scripts/data-transformers/constants/content-labels-part1.ts @@ -73,10 +73,13 @@ export const CONTENT_LABELS_PART1: Partial> = { interpellationDefault: 'Parliamentary interpellation addressed to a minister.', interpellationsBreakdown: (n: number): string => `Opposition MPs have filed ${n} interpellations demanding ministerial accountability. Interpellations are formal parliamentary questions that ministers must answer in chamber debate, making them a key tool of parliamentary scrutiny.`, debateDynamics: 'Debate Dynamics & Accountability', - ministerAccountability: 'Minister Response', + ministerAccountability: 'Directed to', accountabilityAnalysis: 'Accountability Analysis', readFullInterpellation: 'Read the full interpellation', interpellationBy: 'Interpellation by', + interpellationsScrutinyContext: (n: number): string => `Interpellations from ${n} different parties demonstrate broad parliamentary scrutiny of government ministers.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `These ${count} interpellations span ${domains} policy domain${domains > 1 ? 's' : ''}, reflecting the breadth of parliamentary accountability demands on the government.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellation${n > 1 ? 's' : ''} filed`, committeeCountContext: (n: number): string => `${n} reports from this committee signal intensive legislative work within its portfolio.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Parliamentary committees have been active across ${committees} and ${extra} further policy domains.` : `Parliamentary committees have been active across ${committees}.`, @@ -183,10 +186,13 @@ export const CONTENT_LABELS_PART1: Partial> = { interpellationDefault: 'Riksdagsinterpellation till en minister.', interpellationsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} interpellationer som kräver ministeransvar. Interpellationer är formella parlamentariska frågor som ministrar måste besvara i kammaren, vilket gör dem till ett viktigt verktyg för parlamentarisk granskning.`, debateDynamics: 'Debattdynamik och ansvarsutkrävande', - ministerAccountability: 'Ministersvar', + ministerAccountability: 'Riktat till', accountabilityAnalysis: 'Ansvarsutkrävande', readFullInterpellation: 'Läs hela interpellationen', interpellationBy: 'Interpellation av', + interpellationsScrutinyContext: (n: number): string => `Interpellationer från ${n} olika partier visar på bred parlamentarisk granskning av regeringens ministrar.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `Dessa ${count} interpellationer spänner över ${domains} politikområde${domains > 1 ? 'n' : ''}, vilket avspeglar bredden i riksdagens ansvarsutkrävande av regeringen.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellation${n > 1 ? 'er' : ''} inlämnade`, committeeCountContext: (n: number): string => `${n} betänkanden från detta utskott signalerar intensivt lagstiftningsarbete inom dess ansvarsområde.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Riksdagens utskott har varit aktiva inom ${committees} och ${extra} ytterligare områden.` : `Riksdagens utskott har varit aktiva inom ${committees}.`, @@ -293,10 +299,13 @@ export const CONTENT_LABELS_PART1: Partial> = { interpellationDefault: 'Parlamentarisk interpellation til en minister.', interpellationsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} interpellationer, der kræver ministeransvar.`, debateDynamics: 'Debatdynamik og ansvarlighed', - ministerAccountability: 'Ministersvar', + ministerAccountability: 'Rettet til', accountabilityAnalysis: 'Ansvarlighed', readFullInterpellation: 'Læs den fulde interpellation', interpellationBy: 'Interpellation af', + interpellationsScrutinyContext: (n: number): string => `Interpellationer fra ${n} forskellige partier viser en bred parlamentarisk granskning af regeringens ministre.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `Disse ${count} interpellationer dækker ${domains} politikområde${domains > 1 ? 'r' : ''}, der afspejler bredden i parlamentariske ansvarlighedskrav til regeringen.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellation${n > 1 ? 'er' : ''} indgivet`, committeeCountContext: (n: number): string => `${n} betænkninger fra dette udvalg signalerer intensivt lovgivningsarbejde.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Parlamentets udvalg har været aktive inden for ${committees} og ${extra} yderligere politikområder.` : `Parlamentets udvalg har været aktive inden for ${committees}.`, @@ -403,10 +412,13 @@ export const CONTENT_LABELS_PART1: Partial> = { interpellationDefault: 'Parlamentarisk interpellasjon til en statsråd.', interpellationsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} interpellasjoner som krever statsrådets ansvar.`, debateDynamics: 'Debattdynamikk og ansvarlighet', - ministerAccountability: 'Statsrådssvar', + ministerAccountability: 'Rettet til', accountabilityAnalysis: 'Ansvarlighetsanalyse', readFullInterpellation: 'Les hele interpellasjonen', interpellationBy: 'Interpellasjon av', + interpellationsScrutinyContext: (n: number): string => `Interpellasjoner fra ${n} ulike partier viser bred parlamentarisk gransking av regjeringens statsråder.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `Disse ${count} interpellasjonene spenner over ${domains} politikkområde${domains > 1 ? 'r' : ''}, noe som gjenspeiler bredden i parlamentariske ansvarlighetskrav til regjeringen.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellasjon${n > 1 ? 'er' : ''} innsendt`, committeeCountContext: (n: number): string => `${n} innstillinger fra denne komiteen signaliserer intensivt lovgivningsarbeid.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Stortingets komiteer har vært aktive innen ${committees} og ${extra} ytterligere politikkområder.` : `Stortingets komiteer har vært aktive innen ${committees}.`, @@ -513,10 +525,13 @@ export const CONTENT_LABELS_PART1: Partial> = { interpellationDefault: 'Parlamentaarinen välikysymys ministerille.', interpellationsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} välikysymystä ministereiden vastuullisuuden vaatimiseksi.`, debateDynamics: 'Väittelyn dynamiikka ja vastuullisuus', - ministerAccountability: 'Ministerin vastaus', + ministerAccountability: 'Osoitettu', accountabilityAnalysis: 'Vastuullisuusanalyysi', readFullInterpellation: 'Lue koko välikysymys', interpellationBy: 'Välikysymys esittäjältä', + interpellationsScrutinyContext: (n: number): string => `Välikysymykset ${n} eri puolueelta osoittavat laajaa parlamentaarista hallituksen ministereiden valvontaa.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `Nämä ${count} välikysymystä kattavat ${domains} politiikka-aluetta, mikä heijastaa eduskunnan vastuullisuusvaatimusten laajuutta.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} välikysymys${n > 1 ? 'tä' : ''} jätetty`, committeeCountContext: (n: number): string => `${n} mietintöä tästä valiokunnasta osoittavat intensiivistä lainsäädäntötyötä.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Eduskunnan valiokunnat ovat olleet aktiivisia aloilla ${committees} ja ${extra} muulla politiikka-alueella.` : `Eduskunnan valiokunnat ovat olleet aktiivisia aloilla ${committees}.`, @@ -623,10 +638,13 @@ export const CONTENT_LABELS_PART1: Partial> = { interpellationDefault: 'Parlamentarische Interpellation an einen Minister.', interpellationsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} Interpellationen eingereicht, um Ministerverantwortung einzufordern.`, debateDynamics: 'Debattendynamik und Verantwortlichkeit', - ministerAccountability: 'Ministerantwort', + ministerAccountability: 'Gerichtet an', accountabilityAnalysis: 'Verantwortlichkeitsanalyse', readFullInterpellation: 'Vollständige Interpellation lesen', interpellationBy: 'Interpellation von', + interpellationsScrutinyContext: (n: number): string => `Interpellationen aus ${n} verschiedenen Parteien zeigen eine breite parlamentarische Kontrolle der Regierungsminister.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `Diese ${count} Interpellationen erstrecken sich über ${domains} Politikbereich${domains > 1 ? 'e' : ''}, was die Breite der parlamentarischen Rechenschaftsforderungen an die Regierung widerspiegelt.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} Interpellation${n > 1 ? 'en' : ''} eingereicht`, committeeCountContext: (n: number): string => `${n} Berichte dieses Ausschusses signalisieren intensive Gesetzgebungsarbeit.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Die Parlamentsausschüsse waren in den Bereichen ${committees} und ${extra} weiteren Politikfeldern aktiv.` : `Die Parlamentsausschüsse waren in den Bereichen ${committees} aktiv.`, @@ -733,10 +751,13 @@ export const CONTENT_LABELS_PART1: Partial> = { interpellationDefault: 'Interpellation parlementaire adressée à un ministre.', interpellationsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} interpellations exigeant la responsabilité ministérielle.`, debateDynamics: 'Dynamique du débat et responsabilité', - ministerAccountability: 'Réponse du ministre', + ministerAccountability: 'Adressée à', accountabilityAnalysis: 'Analyse de la responsabilité', readFullInterpellation: 'Lire l\'interpellation complète', interpellationBy: 'Interpellation par', + interpellationsScrutinyContext: (n: number): string => `Des interpellations de ${n} partis différents démontrent un large contrôle parlementaire des ministres du gouvernement.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `Ces ${count} interpellations couvrent ${domains} domaine${domains > 1 ? 's' : ''} politique${domains > 1 ? 's' : ''}, reflétant l'ampleur des exigences de responsabilité parlementaire envers le gouvernement.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party} : ${n} interpellation${n > 1 ? 's' : ''} déposée${n > 1 ? 's' : ''}`, committeeCountContext: (n: number): string => `${n} rapports de cette commission témoignent d'un travail législatif intensif.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Les commissions parlementaires ont été actives dans les domaines ${committees} et ${extra} autres domaines politiques.` : `Les commissions parlementaires ont été actives dans les domaines ${committees}.`, diff --git a/scripts/data-transformers/constants/content-labels-part2.ts b/scripts/data-transformers/constants/content-labels-part2.ts index f76fd17f0bd..12d9bf679bd 100644 --- a/scripts/data-transformers/constants/content-labels-part2.ts +++ b/scripts/data-transformers/constants/content-labels-part2.ts @@ -73,10 +73,13 @@ export const CONTENT_LABELS_PART2: Partial> = { interpellationDefault: 'Interpelación parlamentaria dirigida a un ministro.', interpellationsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} interpelaciones exigiendo responsabilidad ministerial.`, debateDynamics: 'Dinámica del debate y responsabilidad', - ministerAccountability: 'Respuesta del ministro', + ministerAccountability: 'Dirigida a', accountabilityAnalysis: 'Análisis de responsabilidad', readFullInterpellation: 'Leer la interpelación completa', interpellationBy: 'Interpelación de', + interpellationsScrutinyContext: (n: number): string => `Interpelaciones de ${n} partidos diferentes demuestran un amplio escrutinio parlamentario de los ministros del gobierno.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `Estas ${count} interpelaciones abarcan ${domains} ámbito${domains > 1 ? 's' : ''} de política, reflejando la amplitud de las demandas parlamentarias de responsabilidad al gobierno.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpelación${n > 1 ? 'es' : ''} presentada${n > 1 ? 's' : ''}`, committeeCountContext: (n: number): string => `${n} informes de esta comisión señalan un trabajo legislativo intensivo.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Las comisiones parlamentarias han estado activas en las áreas de ${committees} y ${extra} dominios políticos más.` : `Las comisiones parlamentarias han estado activas en las áreas de ${committees}.`, @@ -183,10 +186,13 @@ export const CONTENT_LABELS_PART2: Partial> = { interpellationDefault: 'Parlementaire interpellatie aan een minister.', interpellationsBreakdown: (n: number): string => `Oppositieleden hebben ${n} interpellaties ingediend om ministeriële verantwoording te eisen.`, debateDynamics: 'Debatdynamiek en verantwoording', - ministerAccountability: 'Ministerantwoord', + ministerAccountability: 'Gericht aan', accountabilityAnalysis: 'Verantwoordingsanalyse', readFullInterpellation: 'Lees de volledige interpellatie', interpellationBy: 'Interpellatie door', + interpellationsScrutinyContext: (n: number): string => `Interpellaties van ${n} verschillende partijen tonen brede parlementaire controle op de ministers van de regering.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `Deze ${count} interpellaties bestrijken ${domains} beleidsterrein${domains > 1 ? 'en' : ''}, wat de breedte van parlementaire verantwoordingseisen aan de regering weerspiegelt.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellatie${n > 1 ? 's' : ''} ingediend`, committeeCountContext: (n: number): string => `${n} rapporten van deze commissie signaleren intensief wetgevend werk.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `De parlementaire commissies zijn actief geweest op het gebied van ${committees} en ${extra} verdere beleidsterreinen.` : `De parlementaire commissies zijn actief geweest op het gebied van ${committees}.`, @@ -293,10 +299,13 @@ export const CONTENT_LABELS_PART2: Partial> = { interpellationDefault: 'استجواب برلماني موجه إلى وزير.', interpellationsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} استجواباً يطالبون فيه بمساءلة الوزراء.`, debateDynamics: 'ديناميكيات النقاش والمساءلة', - ministerAccountability: 'رد الوزير', + ministerAccountability: 'موجه إلى', accountabilityAnalysis: 'تحليل المساءلة', readFullInterpellation: 'قراءة الاستجواب الكامل', interpellationBy: 'استجواب من', + interpellationsScrutinyContext: (n: number): string => `استجوابات من ${n} أحزاب مختلفة تُظهر رقابة برلمانية واسعة على وزراء الحكومة.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `تغطي هذه الاستجوابات الـ ${count} عدد ${domains} من مجالات السياسة، مما يعكس اتساع مطالب المساءلة البرلمانية تجاه الحكومة.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} استجواب مقدم`, committeeCountContext: (n: number): string => `${n} تقارير من هذه اللجنة تشير إلى عمل تشريعي مكثف.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `كانت اللجان البرلمانية نشطة في مجالات ${committees} و${extra} مجالات سياسية أخرى.` : `كانت اللجان البرلمانية نشطة في مجالات ${committees}.`, @@ -403,10 +412,13 @@ export const CONTENT_LABELS_PART2: Partial> = { interpellationDefault: 'אינטרפלציה פרלמנטרית המופנית לשר.', interpellationsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} אינטרפלציות הדורשות אחריות שרים.`, debateDynamics: 'דינמיקת הדיון ואחריות', - ministerAccountability: 'תגובת השר', + ministerAccountability: 'מופנית אל', accountabilityAnalysis: 'ניתוח אחריות', readFullInterpellation: 'קרא את האינטרפלציה המלאה', interpellationBy: 'אינטרפלציה מאת', + interpellationsScrutinyContext: (n: number): string => `אינטרפלציות מ-${n} מפלגות שונות מדגימות פיקוח פרלמנטרי רחב על שרי הממשלה.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `${count} אינטרפלציות אלה מכסות ${domains} תחומי מדיניות, המשקפים את רוחב דרישות האחריותיות הפרלמנטריות כלפי הממשלה.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} אינטרפלציות הוגשו`, committeeCountContext: (n: number): string => `${n} דוחות מוועדה זו מעידים על עבודת חקיקה אינטנסיבית.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `ועדות הפרלמנט היו פעילות בתחומי ${committees} ו-${extra} תחומי מדיניות נוספים.` : `ועדות הפרלמנט היו פעילות בתחומי ${committees}.`, @@ -513,10 +525,13 @@ export const CONTENT_LABELS_PART2: Partial> = { interpellationDefault: '大臣に宛てた議会質問主意書。', interpellationsBreakdown: (n: number): string => `野党議員が${n}件の質問主意書を提出し、大臣の説明責任を求めています。`, debateDynamics: '討論の動態と説明責任', - ministerAccountability: '大臣の回答', + ministerAccountability: '宛先', accountabilityAnalysis: '説明責任分析', readFullInterpellation: '質問主意書全文を読む', interpellationBy: '質問主意書提出者', + interpellationsScrutinyContext: (n: number): string => `${n}の異なる政党からの質問主意書は、政府大臣に対する広範な議会の監視を示しています。`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `これら${count}件の質問主意書は${domains}の政策分野にまたがり、政府に対する議会の説明責任要求の幅広さを反映しています。`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n}件の質問主意書を提出`, committeeCountContext: (n: number): string => `この委員会からの${n}件の報告書は、集中的な立法作業を示しています。`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `議会の委員会は${committees}および他${extra}の政策分野で活動しています。` : `議会の委員会は${committees}の分野で活動しています。`, @@ -623,10 +638,13 @@ export const CONTENT_LABELS_PART2: Partial> = { interpellationDefault: '장관에게 보내는 의회 대정부 질의.', interpellationsBreakdown: (n: number): string => `야당 의원들이 장관의 책임을 요구하는 ${n}개의 대정부 질의를 제출했습니다.`, debateDynamics: '토론 역학 및 책임성', - ministerAccountability: '장관 답변', + ministerAccountability: '수신자', accountabilityAnalysis: '책임성 분석', readFullInterpellation: '전체 대정부 질의 읽기', interpellationBy: '질의 제출자', + interpellationsScrutinyContext: (n: number): string => `${n}개의 서로 다른 정당의 대정부 질의는 정부 장관에 대한 광범위한 의회 감시를 보여줍니다.`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `이 ${count}건의 대정부 질의는 ${domains}개의 정책 분야에 걸쳐 있으며, 정부에 대한 의회 책임 요구의 폭을 반영합니다.`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n}건의 대정부 질의 제출`, committeeCountContext: (n: number): string => `이 위원회의 ${n}건의 보고서는 집중적인 입법 작업을 나타냅니다.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `의회 위원회는 ${committees} 및 ${extra}개의 추가 정책 분야에서 활동했습니다.` : `의회 위원회는 ${committees} 분야에서 활동했습니다.`, @@ -733,10 +751,13 @@ export const CONTENT_LABELS_PART2: Partial> = { interpellationDefault: '向部长提出的议会质询。', interpellationsBreakdown: (n: number): string => `反对党议员提交了${n}项质询,要求追究部长责任。`, debateDynamics: '辩论动态与问责制', - ministerAccountability: '部长答复', + ministerAccountability: '质询对象', accountabilityAnalysis: '问责分析', readFullInterpellation: '阅读完整质询', interpellationBy: '质询提出者', + interpellationsScrutinyContext: (n: number): string => `来自${n}个不同政党的质询展示了对政府部长的广泛议会监督。`, + interpellationsAccountabilityContext: (count: number, domains: number): string => `这${count}项质询涵盖${domains}个政策领域,反映了议会对政府问责要求的广度。`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}:提交了${n}项质询`, committeeCountContext: (n: number): string => `该委员会的${n}份报告表明密集的立法工作。`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `议会委员会在${committees}以及其他${extra}个政策领域积极开展工作。` : `议会委员会在${committees}领域积极开展工作。`, diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts index eb791972a38..92ec2fd9e2f 100644 --- a/scripts/data-transformers/content-generators/interpellations.ts +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -41,11 +41,14 @@ function extractMinisterTarget(doc: RawDocument): string { if (typeof mottagare === 'string' && mottagare.trim()) { return mottagare.trim(); } - // Fall back to looking for "till X statsråd" / "till statsminister" pattern in the title + // Fall back to looking for "till [name] statsråd|statsminister|minister" pattern in the title + // Only match when followed by a minister-related term to avoid false positives (e.g. "till Gaza") const titleText = doc.titel || doc.title || ''; - const toMatch = titleText.match(/(?:till|to|an|à)\s+([^–—\-]{3,60}?)(?:\s*[-–—]|\s*$)/i); - if (toMatch?.[1]) { - return toMatch[1].trim(); + const ministerMatch = titleText.match( + /(?:till|to)\s+(.{3,60}?)\s*(?:statsråd|statsminister|minister)/i + ); + if (ministerMatch?.[1]) { + return ministerMatch[1].trim(); } return ''; } @@ -62,7 +65,10 @@ function renderInterpellationEntry(doc: RawDocument, lang: Language | string): s const docName = escapeHtml(doc.dokumentnamn || doc.dok_id || titleText); const authorText = escapeHtml(doc.intressent_namn || doc.author || ''); - const partyText = escapeHtml(normalizePartyKey(doc.parti).toUpperCase()); + // Only show party when the raw value is present and not an "Unknown" sentinel + const rawParti = typeof doc.parti === 'string' ? doc.parti.trim() : ''; + const hasParty = rawParti !== '' && rawParti.toLowerCase() !== 'unknown'; + const partyText = hasParty ? escapeHtml(rawParti.toUpperCase()) : ''; const dateHtml = formatDocumentDate(doc, lang); const ministerTarget = extractMinisterTarget(doc); @@ -159,11 +165,11 @@ export function generateInterpellationsContent(data: ArticleContentData, lang: L // Debate dynamics section — how many parties are holding ministers accountable content += `\n

${L(lang, 'debateDynamics')}

\n`; if (partyCount > 1) { - const strategyFn = L(lang, 'oppositionStrategyContext') as string | ((n: number) => string); - const strategyContext = typeof strategyFn === 'function' - ? strategyFn(partyCount) - : `Interpellations from ${partyCount} different parties demonstrate broad parliamentary scrutiny.`; - content += `

${escapeHtml(String(strategyContext))}

\n`; + const scrutinyFn = L(lang, 'interpellationsScrutinyContext') as string | ((n: number) => string); + const scrutinyContext = typeof scrutinyFn === 'function' + ? scrutinyFn(partyCount) + : `Interpellations from ${partyCount} different parties demonstrate broad parliamentary scrutiny of government ministers.`; + content += `

${escapeHtml(String(scrutinyContext))}

\n`; } // Group interpellations by policy domain for thematic accountability analysis @@ -198,11 +204,11 @@ export function generateInterpellationsContent(data: ArticleContentData, lang: L // Accountability Analysis section content += `\n

${L(lang, 'accountabilityAnalysis')}

\n`; - const policyImpFn = L(lang, 'policyImplicationsContext') as string | ((p: number, d: number) => string); - const policyImpText = typeof policyImpFn === 'function' - ? policyImpFn(interpellations.length, themeCount) - : `These ${interpellations.length} interpellations span ${themeCount} policy domain${themeCount !== 1 ? 's' : ''}, reflecting the breadth of parliamentary accountability demands.`; - content += `

${escapeHtml(String(policyImpText))}

\n`; + const accountabilityFn = L(lang, 'interpellationsAccountabilityContext') as string | ((count: number, domains: number) => string); + const accountabilityText = typeof accountabilityFn === 'function' + ? accountabilityFn(interpellations.length, themeCount) + : `These ${interpellations.length} interpellations span ${themeCount} policy domain${themeCount !== 1 ? 's' : ''}, reflecting the breadth of parliamentary accountability demands on the government.`; + content += `

${escapeHtml(String(accountabilityText))}

\n`; // Deep Analysis section (5W framework) content += generateDeepAnalysisSection({ @@ -224,10 +230,10 @@ export function generateInterpellationsContent(data: ArticleContentData, lang: L content += `
\n
    \n`; Object.entries(byParty).forEach(([party, partyIps]) => { if (party !== 'other') { - const detailFn = L(lang, 'partyMotionsFiled') as string | ((party: string, n: number) => string); + const detailFn = L(lang, 'partyInterpellationsFiled') as string | ((party: string, n: number) => string); const detail = typeof detailFn === 'function' ? detailFn(party, partyIps.length) - : `${party}: ${partyIps.length} interpellations filed`; + : `${party}: ${partyIps.length} interpellation${partyIps.length > 1 ? 's' : ''} filed`; content += `
  • ${escapeHtml(String(detail))}
  • \n`; } }); diff --git a/scripts/types/content.ts b/scripts/types/content.ts index 0c7416b4d94..c41fa8fd8eb 100644 --- a/scripts/types/content.ts +++ b/scripts/types/content.ts @@ -101,10 +101,17 @@ export interface ContentLabelSet { interpellationDefault: string; interpellationsBreakdown: (n: number) => string; debateDynamics: string; + /** Label for the target minister (recipient), e.g. "Directed to" */ ministerAccountability: string; accountabilityAnalysis: string; readFullInterpellation: string; interpellationBy: string; + /** Debate dynamics context — interpellations-specific (replaces oppositionStrategyContext for interpellations) */ + interpellationsScrutinyContext: (n: number) => string; + /** Accountability analysis context — interpellations-specific (replaces policyImplicationsContext for interpellations) */ + interpellationsAccountabilityContext: (count: number, domains: number) => string; + /** Party interpellations filed — interpellations-specific (replaces partyMotionsFiled for interpellations) */ + partyInterpellationsFiled: (party: string, n: number) => string; // Localized body text labels (avoid hardcoded en/sv fallbacks) committeeCountContext: (n: number) => string; committeeActivityTakeaway: (committees: string, extra: number) => string; diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts new file mode 100644 index 00000000000..23cb7023218 --- /dev/null +++ b/tests/interpellations.test.ts @@ -0,0 +1,297 @@ +/** + * Unit Tests for Interpellations Content Generator + * Validates: + * - Output does not contain motion/proposition-specific phrasing + * - Minister target extraction does not misparse titles like "... till Gaza" + * - Interpellations label keys exist for all 14 languages + * - Party rendering skips missing/unknown parties (no "OTHER") + */ + +import { describe, it, expect, afterEach, vi } from 'vitest'; +import { + generateArticleContent, + CONTENT_LABELS, +} from '../scripts/data-transformers.js'; +import type { Language } from '../scripts/types/language.js'; + +const ALL_LANGUAGES: Language[] = ['en', 'sv', 'da', 'no', 'fi', 'de', 'fr', 'es', 'nl', 'ar', 'he', 'ja', 'ko', 'zh']; + +/** Mock interpellation document */ +interface MockInterpellation { + titel?: string; + title?: string; + url?: string; + parti?: string; + dokumentnamn?: string; + dok_id?: string; + intressent_namn?: string; + author?: string; + summary?: string; + datum?: string; + mottagare?: string; +} + +describe('Interpellations Content Generator', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + describe('Content labels', () => { + const INTERPELLATION_KEYS = [ + 'noInterpellations', + 'interpellationDefault', + 'interpellationsBreakdown', + 'debateDynamics', + 'ministerAccountability', + 'accountabilityAnalysis', + 'readFullInterpellation', + 'interpellationBy', + 'interpellationsScrutinyContext', + 'interpellationsAccountabilityContext', + 'partyInterpellationsFiled', + ] as const; + + ALL_LANGUAGES.forEach((lang) => { + INTERPELLATION_KEYS.forEach((key) => { + it(`should have "${key}" label for ${lang}`, () => { + const labels = CONTENT_LABELS[lang]; + expect(labels).toBeDefined(); + expect((labels as unknown as Record)[key]).toBeDefined(); + }); + }); + }); + + it('should have function-valued interpellationsBreakdown for en', () => { + const fn = CONTENT_LABELS.en.interpellationsBreakdown; + expect(typeof fn).toBe('function'); + expect(fn(3)).toContain('3'); + }); + + it('should have function-valued interpellationsScrutinyContext for en', () => { + const fn = CONTENT_LABELS.en.interpellationsScrutinyContext; + expect(typeof fn).toBe('function'); + const result = fn(4); + expect(result).toContain('4'); + expect(result).not.toContain('Motion'); + expect(result).not.toContain('motion'); + }); + + it('should have function-valued interpellationsAccountabilityContext for en', () => { + const fn = CONTENT_LABELS.en.interpellationsAccountabilityContext; + expect(typeof fn).toBe('function'); + const result = fn(5, 3); + expect(result).toContain('5'); + expect(result).toContain('3'); + expect(result).not.toContain('proposition'); + }); + + it('should have function-valued partyInterpellationsFiled for en', () => { + const fn = CONTENT_LABELS.en.partyInterpellationsFiled; + expect(typeof fn).toBe('function'); + const result = fn('S', 2); + expect(result).toContain('S'); + expect(result).toContain('2'); + expect(result).toContain('interpellation'); + expect(result).not.toContain('motion'); + }); + + it('ministerAccountability should NOT say "Minister Response" in en', () => { + expect(CONTENT_LABELS.en.ministerAccountability).not.toContain('Response'); + expect(CONTENT_LABELS.en.ministerAccountability).toContain('Directed'); + }); + + it('ministerAccountability should NOT say "Ministersvar" in sv', () => { + expect(CONTENT_LABELS.sv.ministerAccountability).not.toContain('Ministersvar'); + expect(CONTENT_LABELS.sv.ministerAccountability).toContain('Riktat'); + }); + }); + + describe('Output content', () => { + it('should generate interpellations content without motion phrasing', () => { + const data = { + motions: [ + { + titel: 'Om sjukvårdsförsörjning till statsrådet', + parti: 'V', + url: 'https://riksdagen.se/ip1', + dokumentnamn: 'IP 2025/26:1', + intressent_namn: 'Test Person', + mottagare: 'Socialministern', + }, + { + titel: 'Om klimatpolitik till statsrådet', + parti: 'S', + url: 'https://riksdagen.se/ip2', + dokumentnamn: 'IP 2025/26:2', + intressent_namn: 'Other Person', + mottagare: 'Miljöministern', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + + // Should contain interpellations-specific content + expect(result).toContain('Interpellation'); + expect(result).toContain('Debate Dynamics'); + expect(result).toContain('Accountability Analysis'); + + // Should NOT contain motion-specific phrasing + expect(result).not.toContain('Opposition Motions'); + expect(result).not.toContain('Read the full motion'); + expect(result).not.toContain('motion-entry'); + // Should not use the motions-specific "Motions from X parties" label + expect(result).not.toMatch(/Motions from \d+ different parties/); + }); + + it('should not contain proposition-specific phrasing in accountability section', () => { + const data = { + motions: [ + { + titel: 'Om försvaret till statsrådet', + parti: 'M', + url: 'https://riksdagen.se/ip3', + dokumentnamn: 'IP 2025/26:3', + intressent_namn: 'Defense Person', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + // Should not reference propositions in the accountability section + expect(result).not.toContain('propositions touch on'); + expect(result).not.toContain('legislative ambition'); + }); + + it('should use interpellations-specific party count label', () => { + const data = { + motions: [ + { titel: 'IP 1', parti: 'V', url: '#', dokumentnamn: 'IP1' }, + { titel: 'IP 2', parti: 'S', url: '#', dokumentnamn: 'IP2' }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + // Coalition dynamics should say "interpellations filed" not "motions filed" + expect(result).toContain('interpellation'); + expect(result).not.toMatch(/\d+ motion[s]? filed/); + }); + }); + + describe('Minister target extraction', () => { + it('should extract target from mottagare field', () => { + const data = { + motions: [ + { + titel: 'Fråga om humanitär hjälp till Gaza', + parti: 'V', + url: '#', + dokumentnamn: 'IP1', + mottagare: 'Utrikesministern', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).toContain('Utrikesministern'); + }); + + it('should NOT extract "Gaza" as minister from title "... till Gaza"', () => { + const data = { + motions: [ + { + titel: 'Humanitär hjälp till Gaza – en fråga om solidaritet', + parti: 'V', + url: '#', + dokumentnamn: 'IP1', + // No mottagare field + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + // Should NOT show "Gaza" as a minister target + expect(result).not.toContain('Directed to Gaza'); + expect(result).not.toContain('Directed to: Gaza'); + }); + + it('should extract target from "till X statsråd" pattern', () => { + const data = { + motions: [ + { + titel: 'Interpellation till justitie- och inrikesminister', + parti: 'SD', + url: '#', + dokumentnamn: 'IP1', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + // The regex should match "till justitie- och inrikes" before "minister" + expect(result).toContain('Directed to'); + }); + }); + + describe('Party rendering', () => { + it('should NOT display "OTHER" when party is missing', () => { + const data = { + motions: [ + { + titel: 'Test interpellation', + url: '#', + dokumentnamn: 'IP1', + intressent_namn: 'Some Author', + // parti is not set + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).not.toContain('OTHER'); + }); + + it('should NOT display "OTHER" when party is "Unknown"', () => { + const data = { + motions: [ + { + titel: 'Test interpellation', + url: '#', + dokumentnamn: 'IP1', + intressent_namn: 'Some Author', + parti: 'Unknown', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).not.toContain('OTHER'); + expect(result).not.toContain('UNKNOWN'); + }); + + it('should display actual party when present', () => { + const data = { + motions: [ + { + titel: 'Test interpellation', + url: '#', + dokumentnamn: 'IP1', + intressent_namn: 'Some Author', + parti: 'V', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).toContain('Party: V'); + }); + }); + + describe('Empty state', () => { + it('should show no-interpellations message when empty', () => { + const data = { motions: [] as MockInterpellation[] }; + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).toContain('No interpellations'); + }); + }); +}); From 297992961afb511d089861450eec32fccadc51f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 19:59:48 +0000 Subject: [PATCH 04/24] =?UTF-8?q?fix:=20address=20review=20round=202=20?= =?UTF-8?q?=E2=80=94=20filter=20"Unknown"=20author,=20type=20mottagare,=20?= =?UTF-8?q?fix=20docstring,=20restore=20CI=20patterns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Filter out "Unknown" sentinel values from author names (intressent_namn/author) so internal placeholders never leak to readers; added 3 tests. - Add `mottagare` as a typed optional field on RawDocument instead of casting through Record. - Fix extractMinisterTarget() docstring to accurately describe checked fields (mottagare and title, not summary). - Restore missing cross-referencing examples and date calculation helpers in news-evening-analysis.md to fix 4 pre-existing CI test failures (Example 1/2/3 patterns, .filter(), millisecond constants). Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .github/workflows/news-evening-analysis.md | 36 +++++++++++++ .../content-generators/interpellations.ts | 15 +++--- scripts/data-transformers/types.ts | 2 + tests/interpellations.test.ts | 53 +++++++++++++++++++ 4 files changed, 100 insertions(+), 6 deletions(-) diff --git a/.github/workflows/news-evening-analysis.md b/.github/workflows/news-evening-analysis.md index 86a87e6d558..883fb1226fd 100644 --- a/.github/workflows/news-evening-analysis.md +++ b/.github/workflows/news-evening-analysis.md @@ -202,6 +202,42 @@ Filter results to only include items with dates `>= fromDate`. Cross-reference related data sources for richer analysis (e.g., committee reports with voting records, government propositions with parliamentary motions). Filter all results by date to `>= fromDate`. +**Example 1: Committee Report Deep Dive** +```javascript +// 1. Fetch committee reports +const reports = get_betankanden({ rm: "", limit: 50 }); +// 2. Cross-reference with voting records +const votes = search_voteringar({ rm: "", bet: report.beteckning }); +// 3. Filter results by date +const fresh = reports.filter(r => new Date(r.publicerad) >= new Date(fromDate)); +``` + +**Example 2: Government Activity Analysis** +```javascript +// 1. Fetch government propositions +const props = get_propositioner({ rm: "", limit: 20 }); +// 2. Cross-reference with committee handling +const filtered = props.filter(p => new Date(p.publicerad) >= new Date(fromDate)); +``` + +**Example 3: Party Behavior Analysis** +```javascript +// 1. Fetch motions by party +const motions = get_motioner({ rm: "", limit: 100 }); +// 2. Cross-reference with speeches +const speeches = search_anforanden({ rm: "", parti: party }); +``` + +### Date Calculation Helpers + +```javascript +const now = new Date(); +const oneDayMs = 86400000; // 24 * 60 * 60 * 1000 +const oneHourMs = 3600000; // 60 * 60 * 1000 +const fromDate = new Date(now.getTime() - lookbackHours * oneHourMs).toISOString().slice(0, 10); +const fiveDaysAgo = new Date(now.getTime() - 5 * oneDayMs).toISOString().slice(0, 10); +``` + ### Saturday vs Weekday Mode - **Saturday** (day_of_week=6): Produce a **Weekly Parliamentary Review** looking back 5 days (Monday–Friday). Use `coverage_depth: comprehensive`. Title: "The Week in Swedish Politics: {key theme}". Article slug: `weekly-review`. diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts index 92ec2fd9e2f..dbb62ca3563 100644 --- a/scripts/data-transformers/content-generators/interpellations.ts +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -33,13 +33,12 @@ import { detectPolicyDomains, generateDeepPolicyAnalysis } from '../policy-analy /** * Extract minister name or responsible department from an interpellation document. * Interpellations are directed to a specific minister; this information may appear - * in the document title, summary, or mottagare field. + * in the `mottagare` field or in the document title (pattern: "till X statsråd/minister"). */ function extractMinisterTarget(doc: RawDocument): string { - // Some MCP responses include a mottagare (recipient) field - const mottagare = (doc as Record)['mottagare']; - if (typeof mottagare === 'string' && mottagare.trim()) { - return mottagare.trim(); + // Prefer the typed mottagare (recipient) field from the interpellations API + if (typeof doc.mottagare === 'string' && doc.mottagare.trim()) { + return doc.mottagare.trim(); } // Fall back to looking for "till [name] statsråd|statsminister|minister" pattern in the title // Only match when followed by a minister-related term to avoid false positives (e.g. "till Gaza") @@ -64,7 +63,11 @@ function renderInterpellationEntry(doc: RawDocument, lang: Language | string): s : escapedTitle; const docName = escapeHtml(doc.dokumentnamn || doc.dok_id || titleText); - const authorText = escapeHtml(doc.intressent_namn || doc.author || ''); + // Filter out "Unknown" sentinel values that leak from enrichDocumentsWithContent() + const rawAuthor = doc.intressent_namn || doc.author || ''; + const authorText = (rawAuthor && rawAuthor.toLowerCase() !== 'unknown') + ? escapeHtml(rawAuthor) + : ''; // Only show party when the raw value is present and not an "Unknown" sentinel const rawParti = typeof doc.parti === 'string' ? doc.parti.trim() : ''; const hasParty = rawParti !== '' && rawParti.toLowerCase() !== 'unknown'; diff --git a/scripts/data-transformers/types.ts b/scripts/data-transformers/types.ts index 09fa818ba76..b63dc2462fe 100644 --- a/scripts/data-transformers/types.ts +++ b/scripts/data-transformers/types.ts @@ -45,6 +45,8 @@ export interface RawDocument { intressent_namn?: string; author?: string; parti?: string; + /** Recipient minister for interpellations (mottagare field from the API) */ + mottagare?: string; /** Publication date from the MCP API (ISO format YYYY-MM-DD) */ datum?: string; /** Riksmöte (parliamentary session, e.g. "2025/26") */ diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts index 23cb7023218..e8e810cecb5 100644 --- a/tests/interpellations.test.ts +++ b/tests/interpellations.test.ts @@ -294,4 +294,57 @@ describe('Interpellations Content Generator', () => { expect(result).toContain('No interpellations'); }); }); + + describe('Author sentinel filtering', () => { + it('should NOT display "Unknown" as author name', () => { + const data = { + motions: [ + { + titel: 'Test interpellation', + url: '#', + dokumentnamn: 'IP1', + intressent_namn: 'Unknown', + parti: 'V', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + // Should not render the sentinel value "Unknown" to readers + expect(result).not.toContain('Interpellation by: Unknown'); + }); + + it('should NOT display "unknown" (lowercase) as author name', () => { + const data = { + motions: [ + { + titel: 'Test interpellation', + url: '#', + dokumentnamn: 'IP1', + author: 'unknown', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).not.toContain('Interpellation by: unknown'); + }); + + it('should display real author names', () => { + const data = { + motions: [ + { + titel: 'Test interpellation', + url: '#', + dokumentnamn: 'IP1', + intressent_namn: 'Anna Andersson', + parti: 'S', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).toContain('Interpellation by: Anna Andersson'); + }); + }); }); From ffe1b621242d1db27f33c587846c4ef3a50fe45c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 21:40:59 +0000 Subject: [PATCH 05/24] =?UTF-8?q?fix:=20address=20review=20round=203=20?= =?UTF-8?q?=E2=80=94=20compound=20minister=20regex,=20pillar=20transition?= =?UTF-8?q?=20guard,=20workflow=20examples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. extractMinisterTarget() regex now captures the full minister phrase including compound titles like "utrikesminister"/"finansministern" (not just the prefix). Also tolerates inflected forms like "statsrådet", "ministerns". 2. Pillar transition paragraph moved inside `partyCount > 0` guard to avoid dangling transition when Coalition Dynamics section is skipped. 3. Example 1 in news-evening-analysis.md fixed: `report.beteckning` → `reports[0].beteckning`. 4. Example 3 now includes `.filter()` with `new Date` date comparison for consistency. 5. Added 2 unit tests for compound minister title extraction. Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .github/workflows/news-evening-analysis.md | 4 ++- .../content-generators/interpellations.ts | 18 +++++----- tests/interpellations.test.ts | 33 +++++++++++++++++++ 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/.github/workflows/news-evening-analysis.md b/.github/workflows/news-evening-analysis.md index 883fb1226fd..c8b4c76e2ae 100644 --- a/.github/workflows/news-evening-analysis.md +++ b/.github/workflows/news-evening-analysis.md @@ -207,7 +207,7 @@ Cross-reference related data sources for richer analysis (e.g., committee report // 1. Fetch committee reports const reports = get_betankanden({ rm: "", limit: 50 }); // 2. Cross-reference with voting records -const votes = search_voteringar({ rm: "", bet: report.beteckning }); +const votes = search_voteringar({ rm: "", bet: reports[0].beteckning }); // 3. Filter results by date const fresh = reports.filter(r => new Date(r.publicerad) >= new Date(fromDate)); ``` @@ -226,6 +226,8 @@ const filtered = props.filter(p => new Date(p.publicerad) >= new Date(fromDate)) const motions = get_motioner({ rm: "", limit: 100 }); // 2. Cross-reference with speeches const speeches = search_anforanden({ rm: "", parti: party }); +// 3. Filter motions by date +const recentMotions = motions.filter(m => new Date(m.datum) >= new Date(fromDate)); ``` ### Date Calculation Helpers diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts index dbb62ca3563..b524069bf68 100644 --- a/scripts/data-transformers/content-generators/interpellations.ts +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -40,11 +40,12 @@ function extractMinisterTarget(doc: RawDocument): string { if (typeof doc.mottagare === 'string' && doc.mottagare.trim()) { return doc.mottagare.trim(); } - // Fall back to looking for "till [name] statsråd|statsminister|minister" pattern in the title - // Only match when followed by a minister-related term to avoid false positives (e.g. "till Gaza") + // Fall back to looking for minister-directed phrases in the title. + // Capture the full minister phrase including compound titles like "utrikesminister"/"finansminister". + // Only match when a minister-related term is present to avoid false positives (e.g. "till Gaza"). const titleText = doc.titel || doc.title || ''; const ministerMatch = titleText.match( - /(?:till|to)\s+(.{3,60}?)\s*(?:statsråd|statsminister|minister)/i + /(?:till|to)\s+(.{3,60}?\s*(?:statsråd(?:et)?|statsminister[ns]?|(?:\w+)?minister[ns]?))/i ); if (ministerMatch?.[1]) { return ministerMatch[1].trim(); @@ -221,14 +222,13 @@ export function generateInterpellationsContent(data: ArticleContentData, lang: L articleType: 'interpellations', }); - // Narrative bridge before coalition dynamics (inter-pillar transition) - const watchTransition = getPillarTransition(lang, 'watchToOpposition'); - if (watchTransition) { - content += `

    ${escapeHtml(watchTransition)}

    \n`; - } - // Coalition dynamics — which parties are driving accountability if (partyCount > 0) { + // Narrative bridge before coalition dynamics (inter-pillar transition) + const watchTransition = getPillarTransition(lang, 'watchToOpposition'); + if (watchTransition) { + content += `

    ${escapeHtml(watchTransition)}

    \n`; + } content += `\n

    ${L(lang, 'coalitionDynamics')}

    \n`; content += `
    \n
      \n`; Object.entries(byParty).forEach(([party, partyIps]) => { diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts index e8e810cecb5..24041a19b4d 100644 --- a/tests/interpellations.test.ts +++ b/tests/interpellations.test.ts @@ -231,6 +231,39 @@ describe('Interpellations Content Generator', () => { // The regex should match "till justitie- och inrikes" before "minister" expect(result).toContain('Directed to'); }); + + it('should extract full compound minister title like "utrikesminister"', () => { + const data = { + motions: [ + { + titel: 'Fråga till utrikesministern om bistånd', + parti: 'V', + url: '#', + dokumentnamn: 'IP2', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + // The regex should capture "utrikesministern" — not just "utrikes" + expect(result).toContain('utrikesministern'); + }); + + it('should extract full compound "finansministern" target', () => { + const data = { + motions: [ + { + titel: 'Interpellation till finansministern', + parti: 'M', + url: '#', + dokumentnamn: 'IP3', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).toContain('finansministern'); + }); }); describe('Party rendering', () => { From 39975b6ad1a80274a53538bb36e384dc4280c4d2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 23:11:17 +0000 Subject: [PATCH 06/24] =?UTF-8?q?fix:=20address=20review=20round=204=20?= =?UTF-8?q?=E2=80=94=20data.documents=20fallback=20with=20length=20check,?= =?UTF-8?q?=20Example=202=20cross-reference=20step?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. data.motions||data.documents||[] → length-based fallback so empty motions array correctly falls through to data.documents (2 new tests). 2. Example 2 now adds a real cross-reference step (search_dokument for committee handling) instead of only filtering by date. All 3097 tests pass. Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .github/workflows/news-evening-analysis.md | 4 +- .../content-generators/interpellations.ts | 3 +- tests/interpellations.test.ts | 45 +++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/.github/workflows/news-evening-analysis.md b/.github/workflows/news-evening-analysis.md index c8b4c76e2ae..0556e02b035 100644 --- a/.github/workflows/news-evening-analysis.md +++ b/.github/workflows/news-evening-analysis.md @@ -216,8 +216,10 @@ const fresh = reports.filter(r => new Date(r.publicerad) >= new Date(fromDate)); ```javascript // 1. Fetch government propositions const props = get_propositioner({ rm: "", limit: 20 }); -// 2. Cross-reference with committee handling +// 2. Filter propositions by date const filtered = props.filter(p => new Date(p.publicerad) >= new Date(fromDate)); +// 3. Cross-reference with committee handling +const handling = filtered.map(p => search_dokument({ relaterat_id: p.dok_id, doktyp: "bet" })); ``` **Example 3: Party Behavior Analysis** diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts index b524069bf68..05a884c3935 100644 --- a/scripts/data-transformers/content-generators/interpellations.ts +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -141,7 +141,8 @@ function renderInterpellationEntry(doc: RawDocument, lang: Language | string): s export function generateInterpellationsContent(data: ArticleContentData, lang: Language | string): string { // Interpellations are stored in data.motions by the data pipeline // (for backward compatibility) but may also appear in data.documents. - const interpellations: RawDocument[] = data.motions || data.documents || []; + const motionsArray = data.motions ?? []; + const interpellations: RawDocument[] = motionsArray.length > 0 ? motionsArray : (data.documents ?? []); let content = `

      ${L(lang, 'interpellationsTag')}

      \n`; diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts index 24041a19b4d..a170afdca2b 100644 --- a/tests/interpellations.test.ts +++ b/tests/interpellations.test.ts @@ -380,4 +380,49 @@ describe('Interpellations Content Generator', () => { expect(result).toContain('Interpellation by: Anna Andersson'); }); }); + + describe('Data fallback', () => { + it('should use data.documents when data.motions is an empty array', () => { + const data = { + motions: [] as MockInterpellation[], + documents: [ + { + titel: 'Interpellation from documents fallback', + url: '#', + dokumentnamn: 'IP-DOC1', + parti: 'M', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + // Should pick up the document from data.documents, not show empty state + expect(result).toContain('Interpellation from documents fallback'); + expect(result).not.toContain('No interpellations'); + }); + + it('should prefer data.motions when it has items', () => { + const data = { + motions: [ + { + titel: 'Primary interpellation', + url: '#', + dokumentnamn: 'IP-MOT1', + parti: 'S', + }, + ] as MockInterpellation[], + documents: [ + { + titel: 'Secondary interpellation', + url: '#', + dokumentnamn: 'IP-DOC1', + parti: 'V', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).toContain('Primary interpellation'); + }); + }); }); From d7418cb350a0e239683ff74f10aef9161848e006 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 00:45:04 +0000 Subject: [PATCH 07/24] =?UTF-8?q?fix:=20address=20review=20round=205=20?= =?UTF-8?q?=E2=80=94=20remove=20misleading=20'ip'=20doktyp,=20expand=20min?= =?UTF-8?q?ister=20regex=20suffix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Removed 'ip' argument from generateDeepPolicyAnalysis() call — policy-analysis.ts only distinguishes 'mot'/'bet' with type-specific text; all other types (including interpellations) already fall through to 'default'. Updated comment to explain. 2. Expanded minister regex suffix from [ns]? to handle multi-char inflections: ministerns, statsministerns, statsrådets (possessive/definite+possessive forms). Added 2 unit tests for the expanded suffix handling. All 179 interpellations tests pass. Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .../content-generators/interpellations.ts | 10 +++--- tests/interpellations.test.ts | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts index 05a884c3935..ac23369c07f 100644 --- a/scripts/data-transformers/content-generators/interpellations.ts +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -45,7 +45,7 @@ function extractMinisterTarget(doc: RawDocument): string { // Only match when a minister-related term is present to avoid false positives (e.g. "till Gaza"). const titleText = doc.titel || doc.title || ''; const ministerMatch = titleText.match( - /(?:till|to)\s+(.{3,60}?\s*(?:statsråd(?:et)?|statsminister[ns]?|(?:\w+)?minister[ns]?))/i + /(?:till|to)\s+(.{3,60}?\s*(?:statsråd(?:et[s]?)?|statsminister(?:n(?:s)?|s)?|(?:\w+)?minister(?:n(?:s)?|s)?))/i ); if (ministerMatch?.[1]) { return ministerMatch[1].trim(); @@ -82,9 +82,11 @@ function renderInterpellationEntry(doc: RawDocument, lang: Language | string): s const readFullLabel = L(lang, 'readFullInterpellation'); const defaultText = L(lang, 'interpellationDefault'); - // Policy analysis — explains what accountability issue is being raised - // 'ip' = interpellation document type code (riksdag.se document type abbreviation) - const policyAnalysis = generateDeepPolicyAnalysis(doc, lang, 'ip'); + // Policy analysis — explains what accountability issue is being raised. + // No explicit doktyp override is passed; policy-analysis.ts only distinguishes + // 'mot'/'bet' with type-specific text — all other types (including interpellations) + // fall through to the 'default' analysis, which is the correct behavior here. + const policyAnalysis = generateDeepPolicyAnalysis(doc, lang); let html = `\n
      \n`; html += `

      ${titleHtml}

      \n`; diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts index a170afdca2b..10d29ff1ca1 100644 --- a/tests/interpellations.test.ts +++ b/tests/interpellations.test.ts @@ -264,6 +264,38 @@ describe('Interpellations Content Generator', () => { const result = generateArticleContent(data, 'interpellations', 'en') as string; expect(result).toContain('finansministern'); }); + + it('should capture possessive "ministerns" suffix (not drop trailing s)', () => { + const data = { + motions: [ + { + titel: 'Interpellation till finansministerns ansvar', + parti: 'S', + url: '#', + dokumentnamn: 'IP4', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).toContain('finansministerns'); + }); + + it('should capture "statsrådets" inflection', () => { + const data = { + motions: [ + { + titel: 'Interpellation till statsrådets politik', + parti: 'V', + url: '#', + dokumentnamn: 'IP5', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).toContain('statsrådets'); + }); }); describe('Party rendering', () => { From 2ab8cebaebe6aa6d0db8d27494be96b2e0199193 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 02:22:04 +0000 Subject: [PATCH 08/24] feat: add interpellation-specific 'ip' type key to getDomainSpecificAnalysis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add accountability-focused analysis copy for all 12 policy domains when doktyp is 'ip', so interpellations produce text referencing minister scrutiny and chamber debate — not committee processing or proposals. - Add 'ip' entries (en + sv) to all 12 domains in DOMAIN_ANALYSES - Update getDomainSpecificAnalysis() to recognise 'ip' as a valid type key - Add interpellation-specific generic fallback in generatePolicySignificance when no domain matches — references minister obligation, not committee review - Pass 'ip' as impliedDoktyp from interpellations generator - Update JSDoc to list 'ip' as supported impliedDoktyp - Add 4 tests: ip domain analysis (en + sv), generic ip fallback, no-default check Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .../content-generators/interpellations.ts | 9 ++-- scripts/data-transformers/policy-analysis.ts | 42 +++++++++++++++++-- tests/data-transformers.test.ts | 30 +++++++++++++ 3 files changed, 73 insertions(+), 8 deletions(-) diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts index ac23369c07f..c367200ebd1 100644 --- a/scripts/data-transformers/content-generators/interpellations.ts +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -83,10 +83,11 @@ function renderInterpellationEntry(doc: RawDocument, lang: Language | string): s const defaultText = L(lang, 'interpellationDefault'); // Policy analysis — explains what accountability issue is being raised. - // No explicit doktyp override is passed; policy-analysis.ts only distinguishes - // 'mot'/'bet' with type-specific text — all other types (including interpellations) - // fall through to the 'default' analysis, which is the correct behavior here. - const policyAnalysis = generateDeepPolicyAnalysis(doc, lang); + // 'ip' is the interpellation document type code. getDomainSpecificAnalysis() + // and generatePolicySignificance() recognise 'ip' and produce + // accountability-focused text referencing minister scrutiny and chamber + // debate rather than committee processing or proposals. + const policyAnalysis = generateDeepPolicyAnalysis(doc, lang, 'ip'); let html = `\n
      \n`; html += `

      ${titleHtml}

      \n`; diff --git a/scripts/data-transformers/policy-analysis.ts b/scripts/data-transformers/policy-analysis.ts index 624080d6596..5165deb6c97 100644 --- a/scripts/data-transformers/policy-analysis.ts +++ b/scripts/data-transformers/policy-analysis.ts @@ -310,11 +310,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Fiscal policy motions directly challenge the government\'s budget assumptions and signal opposition readiness to contest tax and spending priorities.', bet: 'The Finance Committee\'s position on fiscal matters is usually decisive — the chamber almost always follows its recommendation on budgetary questions.', + ip: 'This interpellation demands ministerial accountability on fiscal policy, compelling the responsible minister to justify tax and spending decisions in open chamber debate.', default: 'Government fiscal proposals must clear rigorous Finance Committee scrutiny and align with Sweden\'s fiscal surplus rule, making the committee\'s verdict pivotal.' }, sv: { mot: 'Finanspolitiska motioner utmanar direkt regeringens budgetantaganden och signalerar oppositionens beredskap att bestrida skatte- och utgiftsprioriteringar.', bet: 'Finansutskottets ståndpunkt i finanspolitiska frågor är i regel avgörande – kammaren följer nästan alltid utskottets rekommendation.', + ip: 'Interpellationen kräver ministerns redovisning av finanspolitiken och tvingar ansvarigt statsråd att försvara skatte- och utgiftsbeslut i öppen kammardebatt.', default: 'Regeringens finanspolitiska förslag måste klara finansutskottets granskning och harmonisera med överskottsmålet för att nå bifall.' } }, @@ -322,11 +324,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Defence motions carry heightened strategic significance following Sweden\'s NATO accession, pressing the government on long-term security commitments.', bet: 'Committee reports on defence shape Sweden\'s military posture and NATO integration trajectory — decisions here have multi-decade consequences.', + ip: 'This interpellation scrutinises the minister\'s defence and security decisions, demanding public accountability on Sweden\'s NATO commitments and military readiness.', default: 'Defence proposals engage Sweden\'s NATO obligations and cross-party consensus-building mechanisms for national security legislation.' }, sv: { mot: 'Försvarsrelaterade motioner har förhöjd strategisk betydelse efter Sveriges NATO-inträde och pressar regeringen om långsiktiga säkerhetsåtaganden.', bet: 'Utskottsbetänkanden om försvar formar Sveriges militära inriktning och NATO-integration – besluten har konsekvenser i decennier.', + ip: 'Interpellationen granskar ministerns försvars- och säkerhetsbeslut och kräver offentlig redovisning av Sveriges NATO-åtaganden och militära beredskap.', default: 'Försvarspropositioner engagerar Sveriges NATO-förpliktelser och mekanismer för brett partistöd inom säkerhetspolitiken.' } }, @@ -334,11 +338,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Climate motions reflect growing parliamentary pressure for faster decarbonisation, often targeting specific industries or the pace of policy implementation.', bet: 'The Environment Committee\'s recommendations balance climate ambition against economic competitiveness — its position sets the legislative baseline.', + ip: 'This interpellation holds the minister accountable on environmental and climate commitments, pressing for transparency on emissions targets and implementation timelines.', default: 'Environmental proposals must navigate competing interests from industry, regional governments, and EU climate commitments, making parliamentary support critical.' }, sv: { mot: 'Klimatmotioner speglar växande parlamentariskt tryck för snabbare koldioxidminskning och riktar sig ofta mot specifika branscher.', bet: 'Miljöutskottet väger klimatambition mot ekonomisk konkurrenskraft – dess rekommendation sätter lagstiftningens utgångspunkt.', + ip: 'Interpellationen ställer ministern till svars för miljö- och klimatåtaganden och kräver insyn i utsläppsmål och genomförandetider.', default: 'Miljöförslag måste navigera konkurrerande intressen från industrin, regionerna och EU:s klimatåtaganden.' } }, @@ -346,11 +352,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Healthcare motions typically target gaps in regional service delivery, pressing for national minimum standards, additional funding, or new patient rights.', bet: 'Social Affairs Committee reports on healthcare set the framework for Sweden\'s regionally delivered but nationally financed health system.', + ip: 'This interpellation challenges the minister on healthcare delivery and patient outcomes, demanding answers on service gaps, waiting times, or regional disparities.', default: 'Healthcare proposals require coordination between national government, regional councils, and professional bodies — a complexity that shapes the legislative timeline.' }, sv: { mot: 'Hälso- och sjukvårdsmotioner riktar sig typiskt mot brister i regionala tjänster och driver på för nationella miniminivåer eller nya patienträttigheter.', bet: 'Socialutskottets betänkanden om hälso- och sjukvård sätter ramarna för det regionalt levererade men nationellt finansierade hälsosystemet.', + ip: 'Interpellationen utmanar ministern om sjukvårdens leverans och patientresultat och kräver svar om tjänstebrister, väntetider eller regionala skillnader.', default: 'Hälso- och sjukvårdspropositioner kräver samordning mellan stat, regioner och professioner – en komplexitet som formar lagstiftningens tidslinje.' } }, @@ -358,11 +366,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Migration motions reflect one of Sweden\'s most contested policy areas, with parties divided on asylum rules, integration requirements, and deportation procedures.', bet: 'The Social Insurance Committee\'s migration reports navigate Sweden\'s EU law obligations and UN Refugee Convention commitments alongside domestic political pressures.', + ip: 'This interpellation demands the minister\'s public defence of migration decisions, pressing for accountability on asylum processing, integration outcomes, or deportation policy.', default: 'Migration proposals must balance EU regulatory obligations with national political imperatives, making cross-party support essential for durable legislation.' }, sv: { mot: 'Migrationsmotioner speglar ett av Sveriges mest omtvistade politikområden, med partier delade om asylregler, integrationskrav och återvändanderutiner.', bet: 'Socialförsäkringsutskottets migrationsbetänkanden navigerar Sveriges åtaganden enligt EU-rätten och FN:s flyktingkonvention.', + ip: 'Interpellationen kräver ministerns offentliga försvar av migrationsbeslut och pressar på ansvarsskyldighet för asylprocesser, integrationsresultat eller utvisningspolitik.', default: 'Migrationspropositioner måste balansera EU-rättsliga förpliktelser med nationella politiska imperativ.' } }, @@ -370,11 +380,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'EU and foreign affairs motions signal parliamentary expectations for government negotiating positions — influential despite executive prerogative in external relations.', bet: 'The Foreign Affairs Committee\'s reports on EU matters reflect Sweden\'s positioning within the bloc and may bind future negotiating postures.', + ip: 'This interpellation compels the minister to explain Sweden\'s foreign policy or EU negotiating stance in open debate, reinforcing parliamentary oversight of executive diplomacy.', default: 'EU and foreign affairs proposals engage Sweden\'s treaty obligations and often require coordination with European partners before domestic enactment.' }, sv: { mot: 'EU- och utrikespolitiska motioner signalerar parlamentets förväntningar på regeringens förhandlingspositioner.', bet: 'Utrikesutskottets betänkanden om EU-frågor speglar Sveriges positionering inom unionen och kan binda framtida förhandlingslinjer.', + ip: 'Interpellationen tvingar ministern att redovisa Sveriges utrikespolitiska eller EU-relaterade förhandlingsposition i öppen debatt och stärker riksdagens insyn i regeringens diplomati.', default: 'EU- och utrikespropositioner engagerar Sveriges fördragsförpliktelser och kräver samordning med europeiska partner.' } }, @@ -382,11 +394,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Justice motions address crime, sentencing, and policing — areas with high public salience where opposition parties frequently press for tougher or more targeted measures.', bet: 'The Justice Committee shapes the criminal law framework; its reports on sentencing and policing directly affect prosecution practice and enforcement priorities.', + ip: 'This interpellation forces the minister to defend justice policy decisions in the chamber, addressing public concerns about crime, sentencing, or policing effectiveness.', default: 'Justice proposals balance rule-of-law principles, human rights obligations, and public safety demands — requiring careful drafting to withstand constitutional scrutiny.' }, sv: { mot: 'Rättsliga motioner rör brott, straff och polis – frågor med hög allmän relevans där oppositionen ofta driver på för hårdare åtgärder.', bet: 'Justitieutskottet formar den straffrättsliga ramen; dess betänkanden om straffsatser och polisverksamhet påverkar direkt åklagarnas praxis.', + ip: 'Interpellationen tvingar ministern att försvara rättspolitiska beslut i kammaren och bemöta allmänhetens oro om brottslighet, straffsatser eller polisens effektivitet.', default: 'Rättsliga propositioner balanserar rättsstatsprinciper, mänskliga rättigheter och allmän säkerhet.' } }, @@ -394,11 +408,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Labour market motions engage sensitive negotiations between employers, unions, and the state — every motion sends a signal to Sweden\'s social partners.', bet: 'The Labour Committee\'s reports on workplace legislation must navigate collective bargaining autonomy while setting minimum statutory floors.', + ip: 'This interpellation holds the minister accountable on employment conditions, wage policy, or labour market regulation, demanding answers before the social partners and the public.', default: 'Labour market proposals enter an arena where tripartite negotiation shapes the final legislative outcome as much as parliamentary votes.' }, sv: { mot: 'Arbetsmarknadsmotioner engagerar känsliga förhandlingar mellan arbetsgivare, fackförbund och stat – varje motion signalerar till parterna.', bet: 'Arbetsmarknadsutskottets betänkanden om arbetsplatslagar måste navigera kollektivavtalens självständighet.', + ip: 'Interpellationen ställer ministern till svars för anställningsvillkor, lönepolitik eller arbetsmarknadsreglering och kräver svar inför arbetsmarknadens parter och allmänheten.', default: 'Arbetsmarknadspropositioner träder in i en arena där trepartsförhandlingar formar det slutliga lagstiftningsresultatet.' } }, @@ -406,11 +422,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Housing motions reflect structural tension between demand for affordable homes and constraints of planning law, rent regulation, and construction cost pressures.', bet: 'The Civil Affairs Committee\'s housing reports address one of Sweden\'s most persistent policy challenges, where committee decisions unlock or block major regulatory change.', + ip: 'This interpellation presses the minister to account for housing supply shortages, rent regulation, or construction barriers — issues with direct impact on household welfare.', default: 'Housing proposals must reconcile competing interests from municipalities, property owners, tenants, and developers — a coalition rarely achieved quickly.' }, sv: { mot: 'Bostadsmotioner speglar strukturell spänning mellan efterfrågan på prisvärda bostäder och begränsningarna i plan- och hyreslagstiftning.', bet: 'Civilutskottets bostadsbetänkanden hanterar en av Sveriges mest ihållande politiska utmaningar.', + ip: 'Interpellationen pressar ministern att svara för bostadsbrist, hyresreglering eller bygghinder – frågor med direkt påverkan på hushållens välfärd.', default: 'Bostadspropositioner måste balansera konkurrerande intressen från kommuner, fastighetsägare, hyresgäster och byggföretag.' } }, @@ -418,11 +436,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Transport motions address infrastructure investment, road safety, and public transit — areas where regional and national interests frequently diverge.', bet: 'The Transport Committee\'s reports guide Sweden\'s national infrastructure planning cycle, directly affecting long-term investment priorities.', + ip: 'This interpellation challenges the minister on transport infrastructure, road safety, or public transit decisions, demanding transparency on investment priorities and regional equity.', default: 'Transport proposals engage the national infrastructure budget, regional equity, and climate transition targets — all must be balanced in committee deliberation.' }, sv: { mot: 'Transportmotioner rör infrastrukturinvesteringar, trafiksäkerhet och kollektivtrafik – frågor där regionala och nationella intressen ofta divergerar.', bet: 'Trafikutskottets betänkanden vägleder Sveriges nationella infrastrukturplanering och påverkar direkt långsiktiga investeringsprioriteringar.', + ip: 'Interpellationen utmanar ministern om transportinfrastruktur, trafiksäkerhet eller kollektivtrafik och kräver insyn i investeringsprioriteringar och regional rättvisa.', default: 'Transportpropositioner engagerar den nationella infrastrukturbudgeten, regional jämlikhet och klimatomställningsmål.' } }, @@ -430,11 +450,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Industry and trade motions often target competitiveness, innovation, or trade agreements — signalling party positions ahead of EU-level or bilateral negotiations.', bet: 'The Committee on Industry and Trade shapes Sweden\'s business environment through reports that set conditions for investment, innovation, and exports.', + ip: 'This interpellation holds the minister accountable on trade, competitiveness, or industrial strategy, pressing for justification of policy choices that affect Sweden\'s economic positioning.', default: 'Industry and trade proposals engage international commitments, EU single-market rules, and domestic competitiveness imperatives simultaneously.' }, sv: { mot: 'Näringspolitiska motioner riktar sig ofta mot konkurrenskraft, innovation eller handelsavtal och signalerar partipositioner inför förhandlingar.', bet: 'Näringsutskottets betänkanden formar Sveriges affärsmiljö och sätter villkoren för investeringar och export.', + ip: 'Interpellationen ställer ministern till svars för handels-, konkurrens- eller industripolitik och kräver motivering av de policyval som påverkar Sveriges ekonomiska ställning.', default: 'Näringspolitiska propositioner engagerar internationella åtaganden, EU:s inre marknadsregler och inhemsk konkurrenskraft.' } }, @@ -442,11 +464,13 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Education motions reflect deep disagreements on school standards, teacher pay, and the role of independent schools — one of Sweden\'s most contested domestic debates.', bet: 'The Education Committee\'s reports directly shape curriculum standards, funding formulas, and school regulation — decisions with long generational consequences.', + ip: 'This interpellation demands the minister explain education policy choices in open debate — whether on school standards, teacher recruitment, or the independent school framework.', default: 'Education proposals must balance national curriculum standards with municipal delivery autonomy and the contested role of private providers in the Swedish school system.' }, sv: { mot: 'Utbildningsmotioner speglar djupa meningsskiljaktigheter om skolstandard, lärarlöner och friskolornas roll.', bet: 'Utbildningsutskottets betänkanden formar direkt läroplaner, finansieringsmodeller och skolreglering.', + ip: 'Interpellationen kräver att ministern förklarar utbildningspolitiska val i öppen debatt — vare sig det gäller skolstandard, lärarrekrytering eller friskolesystemet.', default: 'Utbildningspropositioner måste balansera nationella läroplaner med kommunalt leveransansvar och de privata aktörernas omstridda roll.' } } @@ -458,7 +482,8 @@ const EN_DOMAIN_MAP: Record = _LOCALISED_TO_EN; /** * Return a substantive domain-specific and type-specific analysis sentence. * Each of 12 policy domains has tailored text for motions (mot), committee - * reports (bet), and propositions/default, in both English and Swedish. + * reports (bet), interpellations (ip), and propositions/default, in both + * English and Swedish. */ export function getDomainSpecificAnalysis(primaryDomain: string, doktyp: string, lang: Language | string): string { const isSv = lang === 'sv'; @@ -468,7 +493,7 @@ export function getDomainSpecificAnalysis(primaryDomain: string, doktyp: string, if (!entry) return ''; const langEntry = isSv ? entry.sv : entry.en; - const typeKey = (doktyp === 'mot' || doktyp === 'bet') ? doktyp : 'default'; + const typeKey = (doktyp === 'mot' || doktyp === 'bet' || doktyp === 'ip') ? doktyp : 'default'; return langEntry[typeKey] ?? langEntry['default'] ?? ''; } @@ -480,7 +505,7 @@ export function getDomainSpecificAnalysis(primaryDomain: string, doktyp: string, * when no domain keyword matches but the document's organ field identifies a * known Riksdag committee. * @param impliedDoktyp - document type inferred from the calling context - * ('mot', 'bet', 'prop') when doc.doktyp / doc.documentType is absent. + * ('mot', 'bet', 'prop', 'ip') when doc.doktyp / doc.documentType is absent. */ export function generatePolicySignificance(doc: RawDocument, lang: Language | string, impliedDoktyp?: string): string { const domains = detectPolicyDomains(doc, lang); @@ -527,6 +552,15 @@ export function generatePolicySignificance(doc: RawDocument, lang: Language | st } } + // Interpellation-specific generic: references minister scrutiny/chamber debate + // rather than committee processing + const effectiveDoktyp = doc.doktyp || doc.documentType || impliedDoktyp || ''; + if (effectiveDoktyp === 'ip') { + return lang === 'sv' + ? 'Interpellationen debatteras i kammaren där ministern är skyldig att svara och stå till svars.' + : 'The interpellation is debated in the chamber, where the minister is obliged to respond and be held accountable.'; + } + // Generic significance when no domain detected and no known committee const genericVal = L(lang, 'policySignificanceGeneric'); return typeof genericVal === 'string' ? genericVal : 'Requires committee review and chamber debate before a decision is reached.'; @@ -539,7 +573,7 @@ export function generatePolicySignificance(doc: RawDocument, lang: Language | st * above in structured views and must not be duplicated here. * Falls back to generatePolicySignificance when no enriched text is available. * @param impliedDoktyp - document type inferred from the calling context - * ('mot', 'bet', 'prop') when doc.doktyp / doc.documentType is absent. + * ('mot', 'bet', 'prop', 'ip') when doc.doktyp / doc.documentType is absent. */ export function generateDeepPolicyAnalysis(doc: RawDocument, lang: Language | string, impliedDoktyp?: string, maxPassageChars = 300): string { const effectiveDoktyp = doc.doktyp || doc.documentType || impliedDoktyp || ''; diff --git a/tests/data-transformers.test.ts b/tests/data-transformers.test.ts index 79d886df354..0a1cc8f7460 100644 --- a/tests/data-transformers.test.ts +++ b/tests/data-transformers.test.ts @@ -1528,6 +1528,36 @@ describe('Data Transformers', () => { // Should contain Swedish domain-specific text about NATO expect(content).toMatch(/NATO|säkerhetsåtaganden|strategisk/i); }); + + it('should use ip-specific analysis for interpellations — not default/proposal text', () => { + const content = generateArticleContent({ + motions: [{ titel: 'Försvarspolitik', url: 'https://example.com/1', dok_id: 'IP1' }] + } as MockArticlePayload, 'interpellations', 'en') as string; + + // ip-specific text references minister accountability / NATO scrutiny + expect(content).toMatch(/minister|accountability|scrutini/i); + // NOT the default proposition-focused text about "cross-party consensus-building" + expect(content).not.toContain('cross-party consensus-building mechanisms'); + }); + + it('should use ip-specific Swedish analysis for interpellations in sv', () => { + const content = generateArticleContent({ + motions: [{ titel: 'Klimatpolitik', url: 'https://example.com/1', dok_id: 'IP2' }] + } as MockArticlePayload, 'interpellations', 'sv') as string; + + // ip-specific Swedish text references minister accountability + expect(content).toMatch(/ministern|statsråd|insyn|redovisning/i); + }); + + it('should use interpellation generic fallback when no domain detected', () => { + const content = generateArticleContent({ + motions: [{ titel: 'Allmän fråga', url: 'https://example.com/1', dok_id: 'IP3' }] + } as MockArticlePayload, 'interpellations', 'en') as string; + + // Generic interpellation text — references minister obligation, not committee review + expect(content).toMatch(/minister is obliged to respond|held accountable/i); + expect(content).not.toContain('Requires committee review'); + }); }); describe('Thematic grouping in motions content', () => { From c2e0bf8f895c9527fc93875825b2cd337c9cffc6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 02:25:29 +0000 Subject: [PATCH 09/24] fix: simplify redundant Swedish phrasing in ip generic fallback Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- scripts/data-transformers/policy-analysis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/data-transformers/policy-analysis.ts b/scripts/data-transformers/policy-analysis.ts index 5165deb6c97..c86e87efa65 100644 --- a/scripts/data-transformers/policy-analysis.ts +++ b/scripts/data-transformers/policy-analysis.ts @@ -557,7 +557,7 @@ export function generatePolicySignificance(doc: RawDocument, lang: Language | st const effectiveDoktyp = doc.doktyp || doc.documentType || impliedDoktyp || ''; if (effectiveDoktyp === 'ip') { return lang === 'sv' - ? 'Interpellationen debatteras i kammaren där ministern är skyldig att svara och stå till svars.' + ? 'Interpellationen debatteras i kammaren där ministern är skyldig att svara och förklara sina beslut.' : 'The interpellation is debated in the chamber, where the minister is obliged to respond and be held accountable.'; } From f2cec989fb3d71d9ce806144ec150e112078a53d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 10:44:08 +0000 Subject: [PATCH 10/24] fix: Unicode-aware minister regex and Example 3 party/date field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. extractMinisterTarget() regex: \w → [\p{L}\p{M}] with u flag so Swedish diacritics (å/ä/ö) in compound minister titles like "miljöministern" and "försvarsministern" are captured correctly. Added 2 unit tests for diacritical compound titles. 2. Example 3 (Party Behavior): defined partyCode variable, replaced m.datum with m.inlämnad (correct field for motions per guidance), and used currentRm instead of placeholder. Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .github/workflows/news-evening-analysis.md | 9 ++--- .../content-generators/interpellations.ts | 2 +- tests/interpellations.test.ts | 33 +++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/.github/workflows/news-evening-analysis.md b/.github/workflows/news-evening-analysis.md index 0556e02b035..af042465b4d 100644 --- a/.github/workflows/news-evening-analysis.md +++ b/.github/workflows/news-evening-analysis.md @@ -224,12 +224,13 @@ const handling = filtered.map(p => search_dokument({ relaterat_id: p.dok_id, dok **Example 3: Party Behavior Analysis** ```javascript +const partyCode = "S"; // example party code // 1. Fetch motions by party -const motions = get_motioner({ rm: "", limit: 100 }); +const motions = get_motioner({ rm: currentRm, limit: 100 }); // 2. Cross-reference with speeches -const speeches = search_anforanden({ rm: "", parti: party }); -// 3. Filter motions by date -const recentMotions = motions.filter(m => new Date(m.datum) >= new Date(fromDate)); +const speeches = search_anforanden({ rm: currentRm, parti: partyCode }); +// 3. Filter motions by date (inlämnad = submission date for motions) +const recentMotions = motions.filter(m => new Date(m.inlämnad) >= new Date(fromDate)); ``` ### Date Calculation Helpers diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts index c367200ebd1..f293acc6aac 100644 --- a/scripts/data-transformers/content-generators/interpellations.ts +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -45,7 +45,7 @@ function extractMinisterTarget(doc: RawDocument): string { // Only match when a minister-related term is present to avoid false positives (e.g. "till Gaza"). const titleText = doc.titel || doc.title || ''; const ministerMatch = titleText.match( - /(?:till|to)\s+(.{3,60}?\s*(?:statsråd(?:et[s]?)?|statsminister(?:n(?:s)?|s)?|(?:\w+)?minister(?:n(?:s)?|s)?))/i + /(?:till|to)\s+(.{3,60}?\s*(?:statsråd(?:et[s]?)?|statsminister(?:n(?:s)?|s)?|(?:[\p{L}\p{M}]+)?minister(?:n(?:s)?|s)?))/iu ); if (ministerMatch?.[1]) { return ministerMatch[1].trim(); diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts index 10d29ff1ca1..b0b86235a2d 100644 --- a/tests/interpellations.test.ts +++ b/tests/interpellations.test.ts @@ -296,6 +296,39 @@ describe('Interpellations Content Generator', () => { const result = generateArticleContent(data, 'interpellations', 'en') as string; expect(result).toContain('statsrådets'); }); + + it('should extract compound minister title with Swedish diacritics like "miljöministern"', () => { + const data = { + motions: [ + { + titel: 'Interpellation till miljöministern om kemikaliepolitiken', + parti: 'MP', + url: '#', + dokumentnamn: 'IP-MILJO', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + // The regex must match ö in "miljöministern" via Unicode \p{L} + expect(result).toContain('miljöministern'); + }); + + it('should extract compound minister title with å like "inrikesministern" preceded by "försvars- och"', () => { + const data = { + motions: [ + { + titel: 'Interpellation till försvarsministern om säkerhetspolitik', + parti: 'S', + url: '#', + dokumentnamn: 'IP-FORSVAR', + }, + ] as MockInterpellation[], + }; + + const result = generateArticleContent(data, 'interpellations', 'en') as string; + expect(result).toContain('försvarsministern'); + }); }); describe('Party rendering', () => { From 7b5a07915879ec722b710b382ee52cf1cf898a71 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 10:47:15 +0000 Subject: [PATCH 11/24] =?UTF-8?q?fix:=20correct=20test=20description=20for?= =?UTF-8?q?=20f=C3=B6rsvarsministern=20Unicode=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- tests/interpellations.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts index b0b86235a2d..33556f3eaa6 100644 --- a/tests/interpellations.test.ts +++ b/tests/interpellations.test.ts @@ -314,7 +314,7 @@ describe('Interpellations Content Generator', () => { expect(result).toContain('miljöministern'); }); - it('should extract compound minister title with å like "inrikesministern" preceded by "försvars- och"', () => { + it('should extract compound minister title with ö like "försvarsministern"', () => { const data = { motions: [ { From cc9bdedad9ce63fb3b9cbb3ba8601a487f00349f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 17:46:52 +0000 Subject: [PATCH 12/24] fix: resolve merge conflicts with main branch - unify interpellations data contract and add de/fr/es policy analysis locales Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .../workflows/news-article-generator.lock.yml | 27 +- .../workflows/news-committee-reports.lock.yml | 27 +- .../workflows/news-evening-analysis.lock.yml | 27 +- .github/workflows/news-evening-analysis.md | 75 ++- .../workflows/news-interpellations.lock.yml | 27 +- .github/workflows/news-month-ahead.lock.yml | 27 +- .../workflows/news-monthly-review.lock.yml | 27 +- .github/workflows/news-motions.lock.yml | 27 +- .github/workflows/news-propositions.lock.yml | 27 +- .../workflows/news-realtime-monitor.lock.yml | 27 +- .github/workflows/news-translate.lock.yml | 27 +- .github/workflows/news-translate.md | 2 +- .github/workflows/news-week-ahead.lock.yml | 27 +- .github/workflows/news-weekly-review.lock.yml | 27 +- .github/workflows/release.yml | 2 +- package-lock.json | 9 +- package.json | 2 +- scripts/ai-analysis/dashboard-analyzer.ts | 628 ++++++++++++++++++ .../constants/content-labels-part1.ts | 21 +- .../constants/content-labels-part2.ts | 21 +- .../content-generators/interpellations.ts | 7 +- .../data-transformers/document-analysis.ts | 55 ++ scripts/data-transformers/helpers.ts | 20 +- scripts/data-transformers/policy-analysis.ts | 63 +- scripts/data-transformers/types.ts | 2 + scripts/generate-news-enhanced/generators.ts | 80 ++- scripts/types/content.ts | 4 + tests/ai-analysis-dashboard.test.ts | 554 +++++++++++++++ tests/data-transformers.test.ts | 102 ++- tests/interpellations.test.ts | 69 +- 30 files changed, 1773 insertions(+), 267 deletions(-) create mode 100644 scripts/ai-analysis/dashboard-analyzer.ts create mode 100644 tests/ai-analysis-dashboard.test.ts diff --git a/.github/workflows/news-article-generator.lock.yml b/.github/workflows/news-article-generator.lock.yml index dc9a15030cd..016be56ced8 100644 --- a/.github/workflows/news-article-generator.lock.yml +++ b/.github/workflows/news-article-generator.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Manual-only multi-type article generator. For automated per-type generation, use the dedicated news-committee-reports, news-propositions, news-motions, news-week-ahead, news-month-ahead, news-weekly-review, news-monthly-review workflows. For translations, use news-translate workflow. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"c30ea23e8dc845af2025a6803d5813de9762e6c76b936871730af84cf1be6f97","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"c30ea23e8dc845af2025a6803d5813de9762e6c76b936871730af84cf1be6f97","compiler_version":"v0.58.3"} name: "News: Article Generator (Manual)" "on": @@ -70,7 +70,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -81,7 +81,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News: Article Generator (Manual)" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -302,7 +302,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -347,6 +347,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -360,7 +362,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -563,7 +565,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -760,7 +762,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -770,6 +772,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -869,7 +872,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1091,7 +1094,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1164,7 +1167,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1295,7 +1298,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-committee-reports.lock.yml b/.github/workflows/news-committee-reports.lock.yml index 70cea269e42..d6533200605 100644 --- a/.github/workflows/news-committee-reports.lock.yml +++ b/.github/workflows/news-committee-reports.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Generates committee reports analysis articles in core languages (EN, SV). Translations for remaining 12 languages are handled by the dedicated news-translate workflow via dispatch-workflow. Single article type per run. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"1c4b7899cafd225a0d39c3af17e03494da166f19d0426d4976836a22669cff56","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"1c4b7899cafd225a0d39c3af17e03494da166f19d0426d4976836a22669cff56","compiler_version":"v0.58.3"} name: "News: Committee Reports" "on": @@ -60,7 +60,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -71,7 +71,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News: Committee Reports" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -278,7 +278,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -323,6 +323,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -336,7 +338,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -539,7 +541,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -736,7 +738,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -746,6 +748,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -828,7 +831,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1050,7 +1053,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1123,7 +1126,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1254,7 +1257,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-evening-analysis.lock.yml b/.github/workflows/news-evening-analysis.lock.yml index 756af3ffd4c..43dc5372132 100644 --- a/.github/workflows/news-evening-analysis.lock.yml +++ b/.github/workflows/news-evening-analysis.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Generates comprehensive evening analysis articles in core languages (EN, SV) with Playwright validation. Translations handled by news-translate workflow. On Saturdays, produces a weekly wrap-up reviewing the full parliamentary week. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"6dc11196cb6c0f996b63031af5ada000c7083338501190fd67b58f1cc7fc3212","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"6dc11196cb6c0f996b63031af5ada000c7083338501190fd67b58f1cc7fc3212","compiler_version":"v0.58.3"} name: "News Evening Analysis" "on": @@ -64,7 +64,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -75,7 +75,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News Evening Analysis" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -286,7 +286,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -331,6 +331,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -344,7 +346,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -547,7 +549,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -744,7 +746,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -754,6 +756,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -853,7 +856,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1075,7 +1078,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1148,7 +1151,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1279,7 +1282,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-evening-analysis.md b/.github/workflows/news-evening-analysis.md index af042465b4d..44aeb85619c 100644 --- a/.github/workflows/news-evening-analysis.md +++ b/.github/workflows/news-evening-analysis.md @@ -178,12 +178,25 @@ STEP 1: ALWAYS check data freshness first — call `get_sync_status({})` to warm ### DATA FRESHNESS CHECK -After `get_sync_status()` succeeds, check if data is stale. If `hoursSinceSync > 48`, add a disclaimer note in analysis mentioning "stale data (> 48 hours old)" but proceed with cached data. +After `get_sync_status()` succeeds, compute hours since last sync and check if data is stale: +```js +const hoursSinceSync = (Date.now() - new Date(syncResult.last_updated).getTime()) / 3600000; +if (hoursSinceSync > 48) { /* add stale data disclaimer */ } +``` +If `hoursSinceSync > 48`, add a disclaimer note in analysis mentioning "stale data (> 48 hours old)" but proceed with cached data. ### IMPORTANT: Date Filtering in Analysis Use riksdag-regering-mcp (32 tools for Swedish parliament data). For ad-hoc queries, use `scripts/mcp-query-cli.ts` — NEVER implement custom MCP client code (PROHIBITION). +Calculate date range for queries: +```js +const today = new Date().toISOString().slice(0, 10); +const fromDate = new Date(Date.now() - lookbackHours * 3600000).toISOString().slice(0, 10); +// For weekly review (Saturday): 5-day lookback = 5 * 86400000 ms +const weekFromDate = new Date(Date.now() - 5 * 86400000).toISOString().slice(0, 10); +``` + **Tools with native date params** (supports from/tom or dateFrom/dateTo): - `get_calendar_events` — supports `from`/`tom` parameters - `search_regering` — supports `dateFrom`/`dateTo` parameters @@ -196,11 +209,67 @@ Use riksdag-regering-mcp (32 tools for Swedish parliament data). For ad-hoc quer - `get_propositioner` — filter by `publicerad` date - `search_anforanden` — filter by `datum` field -Filter results to only include items with dates `>= fromDate`. +Filter results to only include items with dates `>= fromDate`: +```js +const filtered = results.filter(item => new Date(item.datum || item.publicerad) >= new Date(fromDate)); +``` + +**Date calculation pattern:** +```javascript +const today = new Date().toISOString().split('T')[0]; +const dayOfWeek = new Date().getUTCDay(); // 0=Sunday, 6=Saturday +const lookbackHours = dayOfWeek === 6 ? 120 : 12; +const fromDate = dayOfWeek === 6 + ? new Date(Date.now() - 5 * 86400000).toISOString().split('T')[0] // Monday + : new Date(Date.now() - lookbackHours * 3600000).toISOString().split('T')[0]; +``` + +**Post-query filtering example:** +```javascript +const results = get_betankanden({ rm: currentRm, limit: 50 }); +const recent = results.filter(b => new Date(b.publicerad) >= new Date(fromDate)); +``` ### Cross-Referencing Strategy -Cross-reference related data sources for richer analysis (e.g., committee reports with voting records, government propositions with parliamentary motions). Filter all results by date to `>= fromDate`. +Cross-reference related data sources for richer analysis. Filter all results by date to `>= fromDate`. + +**Example 1: Committee Report Deep Dive** +```javascript +// 1. Get recent committee reports +const betankanden = get_betankanden({ rm: currentRm, limit: 20 }); +const recentBet = betankanden.filter(b => new Date(b.publicerad) >= new Date(fromDate)); + +// 2. For each report, get full details +const reportDetails = recentBet.map(bet => + get_dokument({ dok_id: bet.dok_id, include_full_text: false }) +); + +// 3. Check related votes +const relatedVotes = search_voteringar({ rm: currentRm, limit: 50 }) + .filter(v => recentBet.some(bet => v.bet === bet.beteckning)); +``` + +**Example 2: Government Activity Analysis** +```javascript +// 1. Get government documents in date range +const govDocs = search_regering({ dateFrom: fromDate, dateTo: today, limit: 30 }); + +// 2. Get related propositions +const propositions = get_propositioner({ rm: currentRm, limit: 20 }) + .filter(p => new Date(p.publicerad) >= new Date(fromDate)); +``` + +**Example 3: Party Behavior Analysis** +```javascript +// 1. Get party voting records +const votes = search_voteringar({ rm: currentRm, limit: 100 }) + .filter(v => new Date(v.datum) >= new Date(fromDate)); + +// 2. Get party speeches +const speeches = search_anforanden({ rm: currentRm, limit: 100 }) + .filter(a => new Date(a.datum) >= new Date(fromDate)); +``` **Example 1: Committee Report Deep Dive** ```javascript diff --git a/.github/workflows/news-interpellations.lock.yml b/.github/workflows/news-interpellations.lock.yml index 3825f80cef8..00a0bf3b97e 100644 --- a/.github/workflows/news-interpellations.lock.yml +++ b/.github/workflows/news-interpellations.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Generates interpellation debates analysis articles in core languages (EN, SV). Translations for remaining 12 languages are handled by the dedicated news-translate workflow via dispatch-workflow. Single article type per run. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"5fe2efeb1ef184e05acfa63f73c5f93b34194862d9168ff692a4660a0b5348c4","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"5fe2efeb1ef184e05acfa63f73c5f93b34194862d9168ff692a4660a0b5348c4","compiler_version":"v0.58.3"} name: "News: Interpellation Debates" "on": @@ -60,7 +60,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -71,7 +71,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News: Interpellation Debates" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -278,7 +278,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -323,6 +323,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -336,7 +338,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -539,7 +541,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -736,7 +738,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -746,6 +748,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -828,7 +831,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1050,7 +1053,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1123,7 +1126,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1254,7 +1257,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-month-ahead.lock.yml b/.github/workflows/news-month-ahead.lock.yml index 9ae30130be2..49cb2b7003e 100644 --- a/.github/workflows/news-month-ahead.lock.yml +++ b/.github/workflows/news-month-ahead.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Generates month-ahead strategic outlook articles in core languages (EN, SV). Translations handled by news-translate workflow. Runs on 1st of each month. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"820328008f3f02b810d36c18322cae105b1b86ea19594228719c2b6601ea09aa","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"820328008f3f02b810d36c18322cae105b1b86ea19594228719c2b6601ea09aa","compiler_version":"v0.58.3"} name: "News: Month Ahead" "on": @@ -60,7 +60,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -71,7 +71,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News: Month Ahead" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -278,7 +278,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -323,6 +323,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -336,7 +338,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -539,7 +541,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -736,7 +738,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -746,6 +748,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -828,7 +831,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1050,7 +1053,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1123,7 +1126,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1254,7 +1257,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-monthly-review.lock.yml b/.github/workflows/news-monthly-review.lock.yml index c5111730e33..fc2409d85c7 100644 --- a/.github/workflows/news-monthly-review.lock.yml +++ b/.github/workflows/news-monthly-review.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Generates monthly review retrospective articles in core languages (EN, SV). Translations handled by news-translate workflow. Runs on 28th of each month. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"5791024e7c5acb25a9f4e9acdc78cdf53b8a2ec89d410a5c69b374be0c6775ad","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"5791024e7c5acb25a9f4e9acdc78cdf53b8a2ec89d410a5c69b374be0c6775ad","compiler_version":"v0.58.3"} name: "News: Monthly Review" "on": @@ -60,7 +60,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -71,7 +71,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News: Monthly Review" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -278,7 +278,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -323,6 +323,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -336,7 +338,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -539,7 +541,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -736,7 +738,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -746,6 +748,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -828,7 +831,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1050,7 +1053,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1123,7 +1126,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1254,7 +1257,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-motions.lock.yml b/.github/workflows/news-motions.lock.yml index 961056ec9ec..c83c5aaef63 100644 --- a/.github/workflows/news-motions.lock.yml +++ b/.github/workflows/news-motions.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Generates opposition motions analysis articles in core languages (EN, SV). Translations for remaining 12 languages are handled by the dedicated news-translate workflow via dispatch-workflow. Single article type per run. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"e1d60f870e2b311322d476753c2d3d8a54856e37fe6b373c564396560f43ab64","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"e1d60f870e2b311322d476753c2d3d8a54856e37fe6b373c564396560f43ab64","compiler_version":"v0.58.3"} name: "News: Opposition Motions" "on": @@ -60,7 +60,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -71,7 +71,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News: Opposition Motions" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -278,7 +278,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -323,6 +323,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -336,7 +338,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -539,7 +541,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -736,7 +738,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -746,6 +748,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -828,7 +831,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1050,7 +1053,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1123,7 +1126,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1254,7 +1257,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-propositions.lock.yml b/.github/workflows/news-propositions.lock.yml index c608394d28b..c297ecca06c 100644 --- a/.github/workflows/news-propositions.lock.yml +++ b/.github/workflows/news-propositions.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Generates government propositions analysis articles in core languages (EN, SV). Translations for remaining 12 languages are handled by the dedicated news-translate workflow via dispatch-workflow. Single article type per run. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"036b19230a16ac965f0ca8c9bdb61887a20c351cf2cc240ba4a65da73f782c13","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"036b19230a16ac965f0ca8c9bdb61887a20c351cf2cc240ba4a65da73f782c13","compiler_version":"v0.58.3"} name: "News: Government Propositions" "on": @@ -60,7 +60,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -71,7 +71,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News: Government Propositions" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -278,7 +278,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -323,6 +323,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -336,7 +338,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -539,7 +541,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -736,7 +738,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -746,6 +748,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -828,7 +831,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1050,7 +1053,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1123,7 +1126,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1254,7 +1257,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-realtime-monitor.lock.yml b/.github/workflows/news-realtime-monitor.lock.yml index c2d45d14d34..84866774def 100644 --- a/.github/workflows/news-realtime-monitor.lock.yml +++ b/.github/workflows/news-realtime-monitor.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Monitors Riksdag and Government for real-time updates and generates breaking news articles in core languages (EN, SV) with Playwright validation. Translations handled by news-translate workflow. Runs twice daily on weekdays, once on weekends. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"f0a5cc538e1f41c797e715b5d2e382a256675eab5571ea87cc710214a02c1a1b","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"f0a5cc538e1f41c797e715b5d2e382a256675eab5571ea87cc710214a02c1a1b","compiler_version":"v0.58.3"} name: "News Realtime Monitor" "on": @@ -65,7 +65,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -76,7 +76,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News Realtime Monitor" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -287,7 +287,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -332,6 +332,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -345,7 +347,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -548,7 +550,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -745,7 +747,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -755,6 +757,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -854,7 +857,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1076,7 +1079,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1149,7 +1152,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1280,7 +1283,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-translate.lock.yml b/.github/workflows/news-translate.lock.yml index 610d0ac4628..b27ea86b7c7 100644 --- a/.github/workflows/news-translate.lock.yml +++ b/.github/workflows/news-translate.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Dedicated translation workflow for news articles. Generates high-quality translations for all non-core languages. Dispatched by content workflows or run manually/on schedule to translate untranslated articles. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"0288ba640ff5cdc92c6db74fca92af7cdc24af6f40dad7e7685370cd25f9263e","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"624f2f39ce3dfbe5f8a8de607bb43ad1f1d4c258a9d3872346483c5263a9a1f2","compiler_version":"v0.58.3"} name: "News: Translate Articles" "on": @@ -37,7 +37,7 @@ name: "News: Translate Articles" description: Article date (YYYY-MM-DD). Defaults to today. required: false article_type: - description: Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles. + description: Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles. required: false languages: default: all-extra @@ -67,7 +67,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -78,7 +78,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News: Translate Articles" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -293,7 +293,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -338,6 +338,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -351,7 +353,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -722,7 +724,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -732,6 +734,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -814,7 +817,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1036,7 +1039,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1108,7 +1111,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1238,7 +1241,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-translate.md b/.github/workflows/news-translate.md index 996fb7b05fd..557fe6706d8 100644 --- a/.github/workflows/news-translate.md +++ b/.github/workflows/news-translate.md @@ -15,7 +15,7 @@ on: description: 'Article date (YYYY-MM-DD). Defaults to today.' required: false article_type: - description: 'Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.' + description: 'Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.' required: false languages: description: 'Target languages (da,no,fi,de,fr,es,nl,ar,he,ja,ko,zh | nordic-extra | eu-extra | cjk | rtl | all-extra). Default: all-extra (all except en,sv)' diff --git a/.github/workflows/news-week-ahead.lock.yml b/.github/workflows/news-week-ahead.lock.yml index d3c8b1e6856..e0b8ed7f5eb 100644 --- a/.github/workflows/news-week-ahead.lock.yml +++ b/.github/workflows/news-week-ahead.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Generates week-ahead prospective articles in core languages (EN, SV). Translations handled by news-translate workflow. Runs Fridays to preview the upcoming parliamentary week. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"890d34e214ef9fe0420f7cc7172d9c944657669a3060b121945d6a2bab80b6db","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"890d34e214ef9fe0420f7cc7172d9c944657669a3060b121945d6a2bab80b6db","compiler_version":"v0.58.3"} name: "News: Week Ahead" "on": @@ -60,7 +60,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -71,7 +71,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News: Week Ahead" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -278,7 +278,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -323,6 +323,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -336,7 +338,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -539,7 +541,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -736,7 +738,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -746,6 +748,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -828,7 +831,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1050,7 +1053,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1123,7 +1126,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1254,7 +1257,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-weekly-review.lock.yml b/.github/workflows/news-weekly-review.lock.yml index b461d05e239..9aece1efad9 100644 --- a/.github/workflows/news-weekly-review.lock.yml +++ b/.github/workflows/news-weekly-review.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.58.1). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.58.3). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -23,7 +23,7 @@ # # Generates weekly review retrospective articles in core languages (EN, SV). Translations handled by news-translate workflow. Runs Saturdays to review the past week. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"fd2c99338b6e1188d73dbb909fe76002668769e9fa0386cff3be227ffe5d9316","compiler_version":"v0.58.1"} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"fd2c99338b6e1188d73dbb909fe76002668769e9fa0386cff3be227ffe5d9316","compiler_version":"v0.58.3"} name: "News: Weekly Review" "on": @@ -60,7 +60,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -71,7 +71,7 @@ jobs: GH_AW_INFO_MODEL: "claude-opus-4.6" GH_AW_INFO_VERSION: "" GH_AW_INFO_AGENT_VERSION: "latest" - GH_AW_INFO_CLI_VERSION: "v0.58.1" + GH_AW_INFO_CLI_VERSION: "v0.58.3" GH_AW_INFO_WORKFLOW_NAME: "News: Weekly Review" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -278,7 +278,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -323,6 +323,8 @@ jobs: await main(); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com - name: Install AWF binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.1 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -336,7 +338,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.1 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.1 ghcr.io/github/gh-aw-firewall/squid:0.24.1 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs @@ -539,7 +541,7 @@ jobs: "type": "string" }, "article_type": { - "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection). Leave empty to scan for all untranslated articles.", + "description": "Article type to translate (propositions, motions, committee-reports, week-ahead, month-ahead, weekly-review, monthly-review, breaking, evening-analysis, deep-inspection, interpellations). Leave empty to scan for all untranslated articles.", "type": "string" }, "languages": { @@ -736,7 +738,7 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.14' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -746,6 +748,7 @@ jobs: "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", @@ -828,7 +831,7 @@ jobs: GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1050,7 +1053,7 @@ jobs: COPILOT_MODEL: claude-opus-4.6 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_VERSION: v0.58.1 + GH_AW_VERSION: v0.58.3 GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} @@ -1123,7 +1126,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1254,7 +1257,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@fa061e89469ef007881d22d3af5a8c9e62363a0d # v0.58.1 + uses: github/gh-aw/actions/setup@08a903b1fb2e493a84a57577778fe5dd711f9468 # v0.58.3 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f48122d72ed..475aeb327e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -543,7 +543,7 @@ jobs: # Create GitHub Release with all artifacts - name: Create GitHub Release - uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 + uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0 with: tag: ${{ needs.prepare.outputs.version }} name: Riksdagsmonitor ${{ needs.prepare.outputs.version }} diff --git a/package-lock.json b/package-lock.json index b76caab8a24..f52ac30c7a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "ajv-formats": "^3.0.1", "chart.js": "^4.5.1", "chartjs-plugin-annotation": "^3.1.0", + "cypress": "15.12.0", "d3": "^7.9.0", "papaparse": "^5.5.3" }, @@ -45,7 +46,7 @@ "node": ">=24" }, "optionalDependencies": { - "cypress": "^15.11.0" + "cypress": "^15.12.0" } }, "node_modules/@apidevtools/json-schema-ref-parser": { @@ -3359,9 +3360,9 @@ } }, "node_modules/cypress": { - "version": "15.11.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-15.11.0.tgz", - "integrity": "sha512-NXDE6/fqZuzh1Zr53nyhCCa4lcANNTYWQNP9fJO+tzD3qVTDaTUni5xXMuigYjMujQ7CRiT9RkJJONmPQSsDFw==", + "version": "15.12.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-15.12.0.tgz", + "integrity": "sha512-B2BRcudLfA4NZZP5QpA45J70bu1heCH59V1yKRLHAtiC49r7RV03X5ifUh7Nfbk8QNg93RAsc6oAmodm/+j0pA==", "hasInstallScript": true, "license": "MIT", "optional": true, diff --git a/package.json b/package.json index 90f6c1e665d..b7cd68881ba 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "worldbank-mcp": "1.0.1" }, "optionalDependencies": { - "cypress": "^15.11.0" + "cypress": "^15.12.0" }, "overrides": { "glob": "^13.0.0" diff --git a/scripts/ai-analysis/dashboard-analyzer.ts b/scripts/ai-analysis/dashboard-analyzer.ts new file mode 100644 index 00000000000..f60230f687e --- /dev/null +++ b/scripts/ai-analysis/dashboard-analyzer.ts @@ -0,0 +1,628 @@ +/** + * @module ai-analysis/dashboard-analyzer + * @description AI-powered dashboard analysis for deep-inspection articles. + * Analyzes document content to produce structured chart configurations for + * multiple chart types: risk radar, stakeholder alignment (scatter), and + * legislative pipeline (bar). Each chart ships with an accessible data table. + * + * The analysis is deterministic — scores are derived from document keyword + * matching, type classification, and structural metadata rather than an + * external ML service. This keeps the pipeline fully offline while still + * producing content-aware, multi-dimensional insights. + * + * Charts conform to Chart.js 4.x conventions and are embedded as + * `data-chart-config` attributes (no inline scripts), consistent with the + * dashboard-section.ts "no inline scripts" pattern. + * + * **Supported chart types produced**: + * - `radar` — Policy Risk Assessment (5 dimensions) + * - `scatter` — Stakeholder Alignment Matrix (influence vs. alignment) + * - `bar` — Legislative Pipeline (docs per stage) + * + * @author Hack23 AB + * @license Apache-2.0 + */ + +import type { Language } from '../types/language.js'; +import type { + DashboardChartConfig, + DashboardTableConfig, + DashboardDataset, + DashboardPoint, +} from '../types/article.js'; +import type { RawDocument } from '../data-transformers/types.js'; + +// --------------------------------------------------------------------------- +// Public types +// --------------------------------------------------------------------------- + +/** Extends DashboardChartConfig with AI-provenance metadata. */ +export interface AIChartConfig extends DashboardChartConfig { + /** Human-readable explanation of what this chart shows and how it was derived. */ + analysisNote: string; + /** Description of the documents / data that informed this chart. */ + dataSource: string; + /** 0–1 confidence score for this analysis (1 = high confidence). */ + confidence: number; +} + +/** Data quality level for the overall dashboard analysis. */ +export type DataQuality = 'high' | 'medium' | 'low'; + +/** Complete result from `analyzeDashboardData()`. */ +export interface DashboardAnalysisResult { + /** AI-analyzed chart configurations (3 charts: radar, scatter, bar). */ + charts: AIChartConfig[]; + /** Accessible data tables — one per chart. */ + tables: DashboardTableConfig[]; + /** Human-readable narrative summary of the analysis. */ + summary: string; + /** Data quality classification driven by document richness. */ + dataQuality: DataQuality; +} + +// --------------------------------------------------------------------------- +// Multi-language label tables +// --------------------------------------------------------------------------- + +/** Helper type: sparse per-language record with English as mandatory fallback. */ +type LangMap = Partial> & { en: string }; + +const L: Record = { + riskAssessmentTitle: { + en: 'Policy Risk Assessment', sv: 'Politisk riskbedömning', da: 'Politisk risikovurdering', + no: 'Politisk risikovurdering', fi: 'Poliittinen riskinarviointi', de: 'Politische Risikobewertung', + fr: 'Évaluation des risques politiques', es: 'Evaluación de riesgos políticos', + nl: 'Politieke risicobeoordeling', ar: 'تقييم المخاطر السياسية', + he: 'הערכת סיכונים פוליטיים', ja: '政策リスク評価', ko: '정책 리스크 평가', zh: '政策风险评估', + }, + implementationRisk: { + en: 'Implementation Risk', sv: 'Genomföranderisk', da: 'Implementeringsrisiko', + no: 'Implementeringsrisiko', fi: 'Toimeenpanoriski', de: 'Umsetzungsrisiko', + fr: 'Risque de mise en œuvre', es: 'Riesgo de implementación', + nl: 'Implementatierisico', ar: 'مخاطر التنفيذ', + he: 'סיכון יישום', ja: '実施リスク', ko: '이행 위험', zh: '实施风险', + }, + stakeholderOpposition: { + en: 'Stakeholder Opposition', sv: 'Intressentmotstånd', da: 'Interessentmodstand', + no: 'Interessentmotstand', fi: 'Sidosryhmävastustus', de: 'Stakeholder-Widerstand', + fr: "Opposition des parties prenantes", es: 'Oposición de partes interesadas', + nl: 'Stakeholderweerstand', ar: 'معارضة أصحاب المصلحة', + he: 'התנגדות בעלי עניין', ja: 'ステークホルダーの反対', ko: '이해관계자 반대', zh: '利益相关者反对', + }, + budgetPressure: { + en: 'Budget Pressure', sv: 'Budgettryck', da: 'Budgetpres', + no: 'Budsjettpress', fi: 'Budjettipaine', de: 'Haushaltsdruck', + fr: 'Pression budgétaire', es: 'Presión presupuestaria', + nl: 'Budgetdruk', ar: 'ضغط الميزانية', + he: 'לחץ תקציבי', ja: '予算圧力', ko: '예산 압박', zh: '预算压力', + }, + regulatoryComplexity: { + en: 'Regulatory Complexity', sv: 'Regulatorisk komplexitet', da: 'Regulatorisk kompleksitet', + no: 'Regulatorisk kompleksitet', fi: 'Sääntelymonimutkaisuus', de: 'Regulatorische Komplexität', + fr: 'Complexité réglementaire', es: 'Complejidad regulatoria', + nl: 'Regelgevingscomplexiteit', ar: 'التعقيد التنظيمي', + he: 'מורכבות רגולטורית', ja: '規制の複雑さ', ko: '규제 복잡성', zh: '监管复杂性', + }, + timelinePressure: { + en: 'Timeline Pressure', sv: 'Tidspress', da: 'Tidspres', + no: 'Tidspress', fi: 'Aikataulupaine', de: 'Zeitdruck', + fr: 'Pression temporelle', es: 'Presión de plazos', + nl: 'Tijdsdruk', ar: 'ضغط الجدول الزمني', + he: 'לחץ לוחות זמנים', ja: 'タイムライン圧力', ko: '일정 압박', zh: '时间线压力', + }, + stakeholderAlignmentTitle: { + en: 'Stakeholder Alignment', sv: 'Intressentanpassning', da: 'Interessenttilpasning', + no: 'Interessenttilpasning', fi: 'Sidosryhmäasemointi', de: 'Stakeholder-Ausrichtung', + fr: 'Alignement des parties prenantes', es: 'Alineación de partes interesadas', + nl: 'Stakeholderafstemming', ar: 'مواءمة أصحاب المصلحة', + he: 'יישור בעלי עניין', ja: 'ステークホルダー整合', ko: '이해관계자 정렬', zh: '利益相关者一致性', + }, + policyAlignment: { + en: 'Policy Alignment (1–10)', sv: 'Policyanpassning (1–10)', da: 'Politisk tilpasning (1–10)', + no: 'Politisk tilpasning (1–10)', fi: 'Poliittinen yhdenmukaistaminen (1–10)', + de: 'Politikausrichtung (1–10)', fr: 'Alignement politique (1–10)', es: 'Alineación política (1–10)', + nl: 'Politieke afstemming (1–10)', ar: 'التوافق السياسي (١–١٠)', + he: 'יישור מדיניות (1–10)', ja: '政策整合性 (1–10)', ko: '정책 정렬 (1–10)', zh: '政策一致性 (1–10)', + }, + influenceLevel: { + en: 'Influence Level (1–10)', sv: 'Inflytandenivå (1–10)', da: 'Indflydelsesniveau (1–10)', + no: 'Innflytelsenivå (1–10)', fi: 'Vaikutustaso (1–10)', de: 'Einflussniveau (1–10)', + fr: "Niveau d'influence (1–10)", es: 'Nivel de influencia (1–10)', + nl: 'Invloedniveau (1–10)', ar: 'مستوى التأثير (١–١٠)', + he: 'רמת השפעה (1–10)', ja: '影響力レベル (1–10)', ko: '영향력 수준 (1–10)', zh: '影响力级别 (1–10)', + }, + legislativePipelineTitle: { + en: 'Legislative Pipeline', sv: 'Lagstiftningspipeline', da: 'Lovgivningspipeline', + no: 'Lovgivningspipeline', fi: 'Lainsäädäntöputki', de: 'Gesetzgebungspipeline', + fr: 'Pipeline législatif', es: 'Pipeline legislativo', + nl: 'Wetgevingspijplijn', ar: 'خط الأنابيب التشريعي', + he: 'צנרת חקיקה', ja: '立法パイプライン', ko: '입법 파이프라인', zh: '立法流程', + }, + draftProposal: { + en: 'Draft Proposal', sv: 'Utkast', da: 'Udkast', no: 'Utkast', + fi: 'Luonnos', de: 'Entwurf', fr: 'Avant-projet', es: 'Borrador', + nl: 'Ontwerp', ar: 'مسودة اقتراح', he: 'טיוטת הצעה', + ja: '提案草案', ko: '초안 제안', zh: '初步提案', + }, + parliamentaryMotion: { + en: 'Parliamentary Motion', sv: 'Motion', da: 'Motion', no: 'Forslag', + fi: 'Kirjelmä', de: 'Parlamentarischer Antrag', fr: 'Motion parlementaire', es: 'Moción parlamentaria', + nl: 'Parlementaire motie', ar: 'اقتراح برلماني', he: 'הצעת חוק פרלמנטרית', + ja: '議会動議', ko: '의회 동의', zh: '议会动议', + }, + governmentProposal: { + en: 'Government Proposal', sv: 'Proposition', da: 'Regeringsforslag', no: 'Regjeringsfremlegg', + fi: 'Hallituksen esitys', de: 'Regierungsvorschlag', fr: 'Proposition du gouvernement', es: 'Propuesta del gobierno', + nl: 'Regeringsvoorstel', ar: 'اقتراح حكومي', he: 'הצעת ממשלה', + ja: '政府提案', ko: '정부 제안', zh: '政府提案', + }, + committeeReview: { + en: 'Committee Review', sv: 'Utskottsbetänkande', da: 'Udvalgsbehandling', no: 'Komitébehandling', + fi: 'Valiokunnan mietintö', de: 'Ausschussprüfung', fr: 'Examen en commission', es: 'Revisión en comité', + nl: 'Commissietoetsing', ar: 'مراجعة اللجنة', he: 'סקירת ועדה', + ja: '委員会審査', ko: '위원회 검토', zh: '委员会审查', + }, + enactedLaw: { + en: 'Enacted Law', sv: 'Antagen lag', da: 'Vedtaget lov', no: 'Vedtatt lov', + fi: 'Säädetty laki', de: 'Verabschiedetes Gesetz', fr: 'Loi adoptée', es: 'Ley promulgada', + nl: 'Ingevoerde wet', ar: 'قانون مُصدَّق', he: 'חוק שנחקק', + ja: '制定法', ko: '제정법', zh: '颁布法律', + }, + pressRelease: { + en: 'Press Release', sv: 'Pressmeddelande', da: 'Pressemeddelelse', no: 'Pressemelding', + fi: 'Lehdistötiedote', de: 'Pressemitteilung', fr: 'Communiqué de presse', es: 'Comunicado de prensa', + nl: 'Persbericht', ar: 'بيان صحفي', he: 'הודעה לעיתונות', + ja: 'プレスリリース', ko: '보도 자료', zh: '新闻稿', + }, + euPosition: { + en: 'EU Position', sv: 'EU-position', da: 'EU-position', no: 'EU-posisjon', + fi: 'EU-kanta', de: 'EU-Position', fr: 'Position UE', es: 'Posición UE', + nl: 'EU-standpunt', ar: 'موقف الاتحاد الأوروبي', he: 'עמדת האיחוד האירופי', + ja: 'EU 立場', ko: 'EU 입장', zh: '欧盟立场', + }, + otherDocuments: { + en: 'Other Documents', sv: 'Övriga dokument', da: 'Andre dokumenter', no: 'Andre dokumenter', + fi: 'Muut asiakirjat', de: 'Sonstige Dokumente', fr: 'Autres documents', es: 'Otros documentos', + nl: 'Overige documenten', ar: 'وثائق أخرى', he: 'מסמכים אחרים', + ja: 'その他の文書', ko: '기타 문서', zh: '其他文件', + }, + government: { + en: 'Government', sv: 'Regering', da: 'Regering', no: 'Regjering', + fi: 'Hallitus', de: 'Regierung', fr: 'Gouvernement', es: 'Gobierno', + nl: 'Regering', ar: 'حكومة', he: 'ממשלה', + ja: '政府', ko: '정부', zh: '政府', + }, + opposition: { + en: 'Opposition', sv: 'Opposition', da: 'Opposition', no: 'Opposisjon', + fi: 'Oppositio', de: 'Opposition', fr: 'Opposition', es: 'Oposición', + nl: 'Oppositie', ar: 'المعارضة', he: 'אופוזיציה', + ja: '野党', ko: '야당', zh: '反对派', + }, + civilSociety: { + en: 'Civil Society', sv: 'Civilsamhälle', da: 'Civilsamfund', no: 'Sivilsamfunn', + fi: 'Kansalaisyhteiskunta', de: 'Zivilgesellschaft', fr: 'Société civile', es: 'Sociedad civil', + nl: 'Maatschappelijk middenveld', ar: 'المجتمع المدني', he: 'החברה האזרחית', + ja: '市民社会', ko: '시민 사회', zh: '公民社会', + }, + riskDimension: { + en: 'Risk Dimension', sv: 'Riskdimension', da: 'Risikodimension', no: 'Risikodimensjon', + fi: 'Riskidimensio', de: 'Risikodimension', fr: 'Dimension de risque', es: 'Dimensión de riesgo', + nl: 'Risicodimensie', ar: 'بُعد المخاطرة', he: 'ממד סיכון', + ja: 'リスク次元', ko: '위험 차원', zh: '风险维度', + }, + scoreOutOf10: { + en: 'Score (1–10)', sv: 'Poäng (1–10)', da: 'Score (1–10)', no: 'Score (1–10)', + fi: 'Pisteet (1–10)', de: 'Wert (1–10)', fr: 'Score (1–10)', es: 'Puntuación (1–10)', + nl: 'Score (1–10)', ar: 'النتيجة (١–١٠)', he: 'ציון (1–10)', + ja: 'スコア (1–10)', ko: '점수 (1–10)', zh: '分数 (1–10)', + }, + stakeholder: { + en: 'Stakeholder', sv: 'Intressent', da: 'Interessent', no: 'Interessent', + fi: 'Sidosryhmä', de: 'Stakeholder', fr: 'Partie prenante', es: 'Parte interesada', + nl: 'Belanghebbende', ar: 'صاحب المصلحة', he: 'בעל עניין', + ja: 'ステークホルダー', ko: '이해관계자', zh: '利益相关者', + }, + stage: { + en: 'Stage', sv: 'Steg', da: 'Trin', no: 'Trinn', + fi: 'Vaihe', de: 'Stufe', fr: 'Étape', es: 'Etapa', + nl: 'Stadium', ar: 'مرحلة', he: 'שלב', + ja: 'ステージ', ko: '단계', zh: '阶段', + }, + count: { + en: 'Count', sv: 'Antal', da: 'Antal', no: 'Antall', + fi: 'Määrä', de: 'Anzahl', fr: 'Nombre', es: 'Cantidad', + nl: 'Aantal', ar: 'العدد', he: 'ספירה', + ja: '数', ko: '수', zh: '数量', + }, +}; + +/** Retrieve a localized label with fallback to English. */ +function lbl(key: string, lang: string): string { + return L[key]?.[lang] ?? L[key]?.en ?? key; +} + +// --------------------------------------------------------------------------- +// Score derivation helpers +// --------------------------------------------------------------------------- + +/** Keywords that raise implementation risk. */ +const IMPLEMENTATION_RISK_KEYWORDS = [ + 'genomföra', 'implement', 'reform', 'förändring', 'ny lag', 'new law', + 'deadline', 'tidsfrist', 'pilotprojekt', 'pilot project', +]; +/** Keywords that raise stakeholder opposition risk. */ +const OPPOSITION_RISK_KEYWORDS = [ + 'opposition', 'avslag', 'reject', 'protest', 'invändning', 'objection', + 'remiss', 'konsultation', 'consultation', 'kritik', 'criticism', +]; +/** Keywords that raise budget pressure. */ +const BUDGET_RISK_KEYWORDS = [ + 'budget', 'kostnad', 'cost', 'finansiering', 'funding', 'anslag', 'appropriation', + 'skatt', 'tax', 'avgift', 'fee', 'anslagsökning', 'spending increase', +]; +/** Keywords that raise regulatory complexity. */ +const REGULATORY_RISK_KEYWORDS = [ + 'reglera', 'regulat', 'direktiv', 'directive', 'förordning', 'regulation', + 'tillämpning', 'application', 'undantag', 'exemption', 'tillstånd', 'permit', +]; +/** Keywords that raise timeline pressure. */ +const TIMELINE_RISK_KEYWORDS = [ + 'senast', 'deadline', 'tidsgräns', 'skyndsamt', 'urgent', 'omgående', + 'omedelbart', 'immediately', '2025', '2026', '2027', +]; + +/** + * Count how many risk keywords appear in a document's text fields. + * Looks at title, summary, and a portion of fullText (or fullContent as fallback). + */ +function countKeywords(doc: RawDocument, keywords: string[]): number { + const haystack = [ + doc.titel ?? '', + doc.rubrik ?? '', + doc.title ?? '', + doc.summary ?? '', + doc.notis ?? '', + (doc.fullText || doc.fullContent || '').slice(0, 500), + ].join(' ').toLowerCase(); + return keywords.filter(kw => haystack.includes(kw.toLowerCase())).length; +} + +/** + * Derive a risk score 1–10 for a single dimension from a set of documents. + * Base score comes from document type counts and keyword frequency. + */ +function deriveRiskScore( + docs: RawDocument[], + keywords: string[], + docTypeWeight: Record, +): number { + // Keyword contribution: 0–5 points + const totalKeywordHits = docs.reduce((sum, doc) => sum + countKeywords(doc, keywords), 0); + const keywordScore = Math.min(5, totalKeywordHits * 0.8); + + // Doc-type contribution: 0–5 points + const typeScore = docs.reduce((sum, doc) => { + const t = (doc.doktyp ?? doc.documentType ?? 'other').toLowerCase(); + return sum + (docTypeWeight[t] ?? 0); + }, 0); + const clampedTypeScore = Math.min(5, typeScore / Math.max(1, docs.length) * 5); + + const raw = keywordScore + clampedTypeScore; + // Clamp to [1, 10] and round to 1 decimal + return Math.round(Math.max(1, Math.min(10, raw)) * 10) / 10; +} + +// --------------------------------------------------------------------------- +// Risk Assessment Radar chart +// --------------------------------------------------------------------------- + +function buildRiskRadarChart( + id: string, + docs: RawDocument[], + lang: string, +): { chart: AIChartConfig; table: DashboardTableConfig } { + const dimLabels = [ + lbl('implementationRisk', lang), + lbl('stakeholderOpposition', lang), + lbl('budgetPressure', lang), + lbl('regulatoryComplexity', lang), + lbl('timelinePressure', lang), + ]; + + const implementationScore = deriveRiskScore(docs, IMPLEMENTATION_RISK_KEYWORDS, { + prop: 1.2, sfs: 1.5, mot: 0.6, bet: 0.4, + }); + const oppositionScore = deriveRiskScore(docs, OPPOSITION_RISK_KEYWORDS, { + mot: 1.5, bet: 1.0, prop: 0.3, + }); + const budgetScore = deriveRiskScore(docs, BUDGET_RISK_KEYWORDS, { + prop: 1.2, sfs: 0.8, bet: 0.6, mot: 0.4, + }); + const regulatoryScore = deriveRiskScore(docs, REGULATORY_RISK_KEYWORDS, { + sfs: 1.5, prop: 1.0, bet: 0.8, fpm: 0.5, + }); + const timelineScore = deriveRiskScore(docs, TIMELINE_RISK_KEYWORDS, { + prop: 0.8, sfs: 1.2, pressm: 0.5, + }); + + const scores = [implementationScore, oppositionScore, budgetScore, regulatoryScore, timelineScore]; + + const hasFullText = docs.some(d => (d.fullText || d.fullContent || '').length > 100); + const confidence = hasFullText ? 0.8 : 0.5; + + const chart: AIChartConfig = { + id, + type: 'radar', + title: lbl('riskAssessmentTitle', lang), + labels: dimLabels, + datasets: [{ + label: lbl('scoreOutOf10', lang), + data: scores, + backgroundColor: 'rgba(255, 0, 110, 0.2)', + borderColor: '#ff006e', + borderWidth: 2, + }], + analysisNote: `Risk dimensions scored 1–10 from keyword analysis of ${docs.length} documents.`, + dataSource: `${docs.length} parliamentary documents analysed`, + confidence, + }; + + const table: DashboardTableConfig = { + caption: lbl('riskAssessmentTitle', lang), + headers: [lbl('riskDimension', lang), lbl('scoreOutOf10', lang)], + rows: dimLabels.map((dim, i) => [dim, String(scores[i])]), + }; + + return { chart, table }; +} + +// --------------------------------------------------------------------------- +// Stakeholder Alignment Scatter chart +// --------------------------------------------------------------------------- + +function buildStakeholderAlignmentChart( + id: string, + docs: RawDocument[], + lang: string, +): { chart: AIChartConfig; table: DashboardTableConfig } { + const propCount = docs.filter(d => + ['prop', 'skr'].includes((d.doktyp ?? d.documentType) ?? ''), + ).length; + const motCount = docs.filter(d => (d.doktyp ?? d.documentType) === 'mot').length; + const betCount = docs.filter(d => (d.doktyp ?? d.documentType) === 'bet').length; + const sfsDocs = docs.filter(d => + (d.doktyp ?? d.documentType) === 'sfs' || (d.dokumentnamn ?? '').startsWith('SFS'), + ).length; + const extCount = docs.filter(d => + ['ext', 'pressm', 'fpm'].includes((d.doktyp ?? d.documentType) ?? ''), + ).length; + + const total = Math.max(1, docs.length); + + // Government: high alignment when many propositions/laws; influence = proportion of proposals + const govAlignment = Math.round(Math.min(10, 2 + (propCount + sfsDocs) / total * 10) * 10) / 10; + const govInfluence = Math.round(Math.min(10, 3 + (propCount + sfsDocs) / total * 10) * 10) / 10; + + // Opposition: alignment inversely related to number of opposing motions + const oppAlignment = Math.round(Math.max(1, Math.min(10, 8 - motCount / total * 6)) * 10) / 10; + const oppInfluence = Math.round(Math.min(10, 2 + (motCount + betCount) / total * 10) * 10) / 10; + + // Civil society: moderate alignment, influence tied to external documents + const civAlignment = Math.round(Math.min(10, 4 + extCount / total * 4) * 10) / 10; + const civInfluence = Math.round(Math.min(10, 2 + extCount / total * 8) * 10) / 10; + + const govLabel = lbl('government', lang); + const oppLabel = lbl('opposition', lang); + const civLabel = lbl('civilSociety', lang); + + const makePoint = (x: number, y: number): DashboardPoint => ({ x, y }); + + const datasets: DashboardDataset[] = [ + { + label: govLabel, + data: [makePoint(govAlignment, govInfluence)], + backgroundColor: '#00d9ff', + borderColor: '#00b8d4', + borderWidth: 2, + }, + { + label: oppLabel, + data: [makePoint(oppAlignment, oppInfluence)], + backgroundColor: '#ff006e', + borderColor: '#d4004e', + borderWidth: 2, + }, + { + label: civLabel, + data: [makePoint(civAlignment, civInfluence)], + backgroundColor: '#ffbe0b', + borderColor: '#d4a00b', + borderWidth: 2, + }, + ]; + + const chart: AIChartConfig = { + id, + type: 'scatter', + title: lbl('stakeholderAlignmentTitle', lang), + datasets, + analysisNote: `Stakeholder positions derived from document type distribution (${docs.length} docs).`, + dataSource: `Propositions: ${propCount}, Motions: ${motCount}, Committee reports: ${betCount}`, + confidence: 0.6, + }; + + const table: DashboardTableConfig = { + caption: lbl('stakeholderAlignmentTitle', lang), + headers: [ + lbl('stakeholder', lang), + lbl('policyAlignment', lang), + lbl('influenceLevel', lang), + ], + rows: [ + [govLabel, String(govAlignment), String(govInfluence)], + [oppLabel, String(oppAlignment), String(oppInfluence)], + [civLabel, String(civAlignment), String(civInfluence)], + ], + }; + + return { chart, table }; +} + +// --------------------------------------------------------------------------- +// Legislative Pipeline Bar chart +// --------------------------------------------------------------------------- + +/** Maps a document type to its legislative stage label key. */ +const DOC_TYPE_TO_STAGE: Record = { + fpm: 'draftProposal', + mot: 'parliamentaryMotion', + prop: 'governmentProposal', + bet: 'committeeReview', + sfs: 'enactedLaw', + skr: 'governmentProposal', + pressm: 'pressRelease', + eu: 'euPosition', +}; + +function buildLegislativePipelineChart( + id: string, + docs: RawDocument[], + lang: string, +): { chart: AIChartConfig; table: DashboardTableConfig } { + // Aggregate counts per stage + const stageCounts: Record = {}; + docs.forEach(doc => { + const t = (doc.doktyp ?? doc.documentType ?? 'other').toLowerCase(); + const stageKey = DOC_TYPE_TO_STAGE[t] ?? 'otherDocuments'; + stageCounts[stageKey] = (stageCounts[stageKey] ?? 0) + 1; + }); + + const stageOrder = [ + 'draftProposal', + 'parliamentaryMotion', + 'governmentProposal', + 'committeeReview', + 'enactedLaw', + 'pressRelease', + 'euPosition', + 'otherDocuments', + ]; + + const presentStages = stageOrder.filter(s => (stageCounts[s] ?? 0) > 0); + if (presentStages.length === 0) { + // Fallback: show all stages with zero so chart still renders + presentStages.push('governmentProposal', 'committeeReview'); + stageCounts['governmentProposal'] = 0; + stageCounts['committeeReview'] = 0; + } + + const palette = ['#00d9ff', '#ff006e', '#ffbe0b', '#00ff88', '#ff8800', '#aa00ff', '#83cf39']; + const labels = presentStages.map(s => lbl(s, lang)); + const values = presentStages.map(s => stageCounts[s] ?? 0); + const colors = presentStages.map((_, i) => palette[i % palette.length]); + + const chart: AIChartConfig = { + id, + type: 'bar', + title: lbl('legislativePipelineTitle', lang), + labels, + datasets: [{ + label: lbl('count', lang), + data: values, + backgroundColor: colors, + borderColor: colors, + borderWidth: 1, + }], + analysisNote: `Documents mapped to ${presentStages.length} legislative stages.`, + dataSource: `${docs.length} parliamentary documents classified by type`, + confidence: 0.9, + }; + + const table: DashboardTableConfig = { + caption: lbl('legislativePipelineTitle', lang), + headers: [lbl('stage', lang), lbl('count', lang)], + rows: labels.map((label, i) => [label, String(values[i])]), + }; + + return { chart, table }; +} + +// --------------------------------------------------------------------------- +// Data quality assessment +// --------------------------------------------------------------------------- + +/** + * Classify overall data quality based on document richness. + * - `high` → several documents with full text + * - `medium` → documents with metadata but little or no full text + * - `low` → very few documents or only minimal metadata + */ +function assessDataQuality(docs: RawDocument[]): DataQuality { + if (docs.length === 0) return 'low'; + const withFullText = docs.filter(d => (d.fullText || d.fullContent || '').length > 100).length; + if (withFullText >= 3 || (withFullText >= 1 && docs.length >= 5)) return 'high'; + if (docs.length >= 3) return 'medium'; + return 'low'; +} + +// --------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------- + +/** + * Analyze a set of parliamentary documents and produce AI-aware chart + * configurations for the deep-inspection dashboard. + * + * Returns exactly **three** chart + table pairs covering: + * 1. **Policy Risk Assessment** (radar) — five risk dimensions scored 1–10 + * 2. **Stakeholder Alignment** (scatter) — three stakeholders on a 2D grid + * 3. **Legislative Pipeline** (bar) — document counts per legislative stage + * + * If `docs` is empty, all charts return minimal placeholder data so the + * dashboard still renders without errors. + * + * @example + * ```ts + * import { analyzeDashboardData } from './ai-analysis/dashboard-analyzer.js'; + * + * const result = analyzeDashboardData(docs, 'defence policy', 'en'); + * // result.charts — [AIChartConfig, AIChartConfig, AIChartConfig] + * // result.tables — [DashboardTableConfig × 3] + * // result.dataQuality — 'high' | 'medium' | 'low' + * ``` + */ +export function analyzeDashboardData( + docs: RawDocument[], + topic: string | null, + lang: Language | string, +): DashboardAnalysisResult { + const safeLang = typeof lang === 'string' ? lang : 'en'; + + // Pass the real (possibly empty) array to each builder — each handles + // the empty case internally rather than relying on a placeholder document. + const { chart: radarChart, table: radarTable } = + buildRiskRadarChart('ai-risk-radar', docs, safeLang); + + const { chart: scatterChart, table: scatterTable } = + buildStakeholderAlignmentChart('ai-stakeholder-alignment', docs, safeLang); + + const { chart: pipelineChart, table: pipelineTable } = + buildLegislativePipelineChart('ai-legislative-pipeline', docs, safeLang); + + const dataQuality = assessDataQuality(docs); + + const totalDocs = docs.length; + const uniqueTypes = new Set(docs.map(d => d.doktyp ?? d.documentType ?? 'other')).size; + const topicSuffix = topic ? ` — ${topic}` : ''; + const summary = totalDocs === 0 + ? 'No documents available for analysis.' + : `AI analysis of ${totalDocs} document${totalDocs > 1 ? 's' : ''} across ${uniqueTypes} type${uniqueTypes > 1 ? 's' : ''}${topicSuffix}.`; + + return { + charts: [radarChart, scatterChart, pipelineChart], + tables: [radarTable, scatterTable, pipelineTable], + summary, + dataQuality, + }; +} diff --git a/scripts/data-transformers/constants/content-labels-part1.ts b/scripts/data-transformers/constants/content-labels-part1.ts index 5bb4f32ed85..ac23b670c8b 100644 --- a/scripts/data-transformers/constants/content-labels-part1.ts +++ b/scripts/data-transformers/constants/content-labels-part1.ts @@ -70,7 +70,7 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Opposition MPs have filed ${n} new motions, mapping the political fault lines in the current Riksdag. These motions reveal not just policy disagreements but the strategic positioning of parties as they prepare for the next electoral contest.` , noInterpellations: 'No interpellations available at this time.', - interpellationDefault: 'Parliamentary interpellation addressed to a minister.', + interpellationDefault: 'Parliamentary interpellation demanding government accountability.', interpellationsBreakdown: (n: number): string => `Opposition MPs have filed ${n} interpellations demanding ministerial accountability. Interpellations are formal parliamentary questions that ministers must answer in chamber debate, making them a key tool of parliamentary scrutiny.`, debateDynamics: 'Debate Dynamics & Accountability', ministerAccountability: 'Directed to', @@ -85,6 +85,7 @@ export const CONTENT_LABELS_PART1: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Parliamentary committees have been active across ${committees} and ${extra} further policy domains.` : `Parliamentary committees have been active across ${committees}.`, committeeMomentumTakeaway: (n: number): string => `A total of ${n} reports demonstrates sustained legislative momentum and ongoing policy prioritisation.`, oppositionStrategyContext: (n: number): string => `Motions from ${n} different parties reveal the breadth of opposition political criticism and alternative policy agendas.`, + interpellationStrategyContext: (n: number): string => `Interpellations from ${n} different parties demonstrate coordinated parliamentary oversight and demands for government accountability.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `These ${propCount} propositions touch on ${domainCount} policy domain${domainCount > 1 ? 's' : ''}, demonstrating the government's broad legislative ambition. Committee review and chamber debate will determine whether these proposals command sufficient support to become law.`, genericOverview: (n: number): string => `During this period, ${n} documents were processed in parliament, offering a snapshot of ongoing legislative work.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} motion${n > 1 ? 's' : ''} filed`, @@ -183,7 +184,7 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} nya motioner som kartlägger de politiska skiljelinjerna i nuvarande riksdag.` , noInterpellations: 'Inga interpellationer tillgängliga för tillfället.', - interpellationDefault: 'Riksdagsinterpellation till en minister.', + interpellationDefault: 'Riksdagsinterpellation som kräver regeringens ansvarsskyldighet.', interpellationsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} interpellationer som kräver ministeransvar. Interpellationer är formella parlamentariska frågor som ministrar måste besvara i kammaren, vilket gör dem till ett viktigt verktyg för parlamentarisk granskning.`, debateDynamics: 'Debattdynamik och ansvarsutkrävande', ministerAccountability: 'Riktat till', @@ -198,6 +199,7 @@ export const CONTENT_LABELS_PART1: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Riksdagens utskott har varit aktiva inom ${committees} och ${extra} ytterligare områden.` : `Riksdagens utskott har varit aktiva inom ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Totalt ${n} betänkanden visar lagstiftande momentum och pågående politisk prioritering.`, oppositionStrategyContext: (n: number): string => `Motioner från ${n} olika partier visar bredden i oppositionens politiska kritik och alternativa agenda.`, + interpellationStrategyContext: (n: number): string => `Interpellationer från ${n} olika partier visar samordnad parlamentarisk granskning och krav på regeringsansvar.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `Dessa ${propCount} propositioner berör ${domainCount} politikområde${domainCount > 1 ? 'n' : ''}, vilket visar regeringens breda lagstiftningsambition. Utskottsbehandling och kammardebatt avgör om förslagen vinner tillräckligt stöd för att bli lag.`, genericOverview: (n: number): string => `Under perioden har ${n} dokument behandlats i riksdagen, vilket ger en bild av det pågående lagstiftningsarbetet.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} motion${n > 1 ? 'er' : ''} inlämnade`, @@ -296,7 +298,7 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} nye forslag.` , noInterpellations: 'Ingen interpellationer tilgængelige på nuværende tidspunkt.', - interpellationDefault: 'Parlamentarisk interpellation til en minister.', + interpellationDefault: 'Parlamentarisk forespørgsel der kræver regeringens ansvarlighed.', interpellationsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} interpellationer, der kræver ministeransvar.`, debateDynamics: 'Debatdynamik og ansvarlighed', ministerAccountability: 'Rettet til', @@ -311,6 +313,7 @@ export const CONTENT_LABELS_PART1: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Parlamentets udvalg har været aktive inden for ${committees} og ${extra} yderligere politikområder.` : `Parlamentets udvalg har været aktive inden for ${committees}.`, committeeMomentumTakeaway: (n: number): string => `I alt ${n} betænkninger viser vedvarende lovgivningsmæssig fremdrift.`, oppositionStrategyContext: (n: number): string => `Forslag fra ${n} forskellige partier viser bredden i oppositionens politiske kritik.`, + interpellationStrategyContext: (n: number): string => `Interpellationer fra ${n} forskellige partier viser koordineret parlamentarisk kontrol og krav om regeringsansvarlighed.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `Disse ${propCount} lovforslag berører ${domainCount} politikområde${domainCount > 1 ? 'r' : ''}, hvilket viser regeringens brede lovgivningsambition.`, genericOverview: (n: number): string => `I denne periode er ${n} dokumenter blevet behandlet i parlamentet.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} forslag indgivet`, @@ -409,7 +412,7 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} nye forslag.` , noInterpellations: 'Ingen interpellasjoner tilgjengelige for øyeblikket.', - interpellationDefault: 'Parlamentarisk interpellasjon til en statsråd.', + interpellationDefault: 'Parlamentarisk interpellasjon som krever regjeringens ansvarlighet.', interpellationsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} interpellasjoner som krever statsrådets ansvar.`, debateDynamics: 'Debattdynamikk og ansvarlighet', ministerAccountability: 'Rettet til', @@ -424,6 +427,7 @@ export const CONTENT_LABELS_PART1: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Stortingets komiteer har vært aktive innen ${committees} og ${extra} ytterligere politikkområder.` : `Stortingets komiteer har vært aktive innen ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Totalt ${n} innstillinger viser vedvarende lovgivende fremdrift.`, oppositionStrategyContext: (n: number): string => `Forslag fra ${n} ulike partier viser bredden i opposisjonens politiske kritikk.`, + interpellationStrategyContext: (n: number): string => `Interpellasjoner fra ${n} ulike partier viser koordinert parlamentarisk kontroll og krav om regjeringsansvar.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `Disse ${propCount} proposisjonene berører ${domainCount} politikkområde${domainCount > 1 ? 'r' : ''}, og viser regjeringens brede lovgivningsambisjon.`, genericOverview: (n: number): string => `I denne perioden er ${n} dokumenter blitt behandlet i parlamentet.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} forslag innsendt`, @@ -522,7 +526,7 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} uutta aloitetta.` , noInterpellations: 'Ei välikysymyksiä saatavilla tällä hetkellä.', - interpellationDefault: 'Parlamentaarinen välikysymys ministerille.', + interpellationDefault: 'Hallituksen vastuuvelvollisuutta vaativa eduskunnan välikysymys.', interpellationsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} välikysymystä ministereiden vastuullisuuden vaatimiseksi.`, debateDynamics: 'Väittelyn dynamiikka ja vastuullisuus', ministerAccountability: 'Osoitettu', @@ -537,6 +541,7 @@ export const CONTENT_LABELS_PART1: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Eduskunnan valiokunnat ovat olleet aktiivisia aloilla ${committees} ja ${extra} muulla politiikka-alueella.` : `Eduskunnan valiokunnat ovat olleet aktiivisia aloilla ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Yhteensä ${n} mietintöä osoittaa jatkuvaa lainsäädännöllistä vauhtia.`, oppositionStrategyContext: (n: number): string => `${n} eri puolueen aloitteet osoittavat opposition poliittisen kritiikin laajuuden.`, + interpellationStrategyContext: (n: number): string => `${n} eri puolueen välikysymykset osoittavat koordinoitua parlamentaarista valvontaa ja vaatimuksia hallituksen vastuullisuudesta.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `Nämä ${propCount} esitystä koskevat ${domainCount} politiikka-aluetta, mikä osoittaa hallituksen laajaa lainsäädäntökunnianhimoa.`, genericOverview: (n: number): string => `Tänä aikana eduskunnassa käsiteltiin ${n} asiakirjaa.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} aloitetta jätetty`, @@ -635,7 +640,7 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} neue Anträge eingereicht.` , noInterpellations: 'Derzeit keine Interpellationen verfügbar.', - interpellationDefault: 'Parlamentarische Interpellation an einen Minister.', + interpellationDefault: 'Parlamentarische Interpellation, die Rechenschaft der Regierung verlangt.', interpellationsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} Interpellationen eingereicht, um Ministerverantwortung einzufordern.`, debateDynamics: 'Debattendynamik und Verantwortlichkeit', ministerAccountability: 'Gerichtet an', @@ -650,6 +655,7 @@ export const CONTENT_LABELS_PART1: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Die Parlamentsausschüsse waren in den Bereichen ${committees} und ${extra} weiteren Politikfeldern aktiv.` : `Die Parlamentsausschüsse waren in den Bereichen ${committees} aktiv.`, committeeMomentumTakeaway: (n: number): string => `Insgesamt ${n} Berichte zeugen von anhaltendem Gesetzgebungsmomentum.`, oppositionStrategyContext: (n: number): string => `Anträge aus ${n} verschiedenen Parteien zeigen die Breite der oppositionellen politischen Kritik.`, + interpellationStrategyContext: (n: number): string => `Interpellationen aus ${n} verschiedenen Parteien zeigen koordinierte parlamentarische Kontrolle und Forderungen nach Regierungsverantwortung.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `Diese ${propCount} Vorlagen betreffen ${domainCount} Politikbereich${domainCount > 1 ? 'e' : ''} und demonstrieren den breiten Gesetzgebungsanspruch der Regierung.`, genericOverview: (n: number): string => `In diesem Zeitraum wurden ${n} Dokumente im Parlament behandelt.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} Antrag${n > 1 ? 'e' : ''} eingereicht`, @@ -748,7 +754,7 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} nouvelles motions.` , noInterpellations: 'Aucune interpellation disponible pour le moment.', - interpellationDefault: 'Interpellation parlementaire adressée à un ministre.', + interpellationDefault: 'Interpellation parlementaire exigeant la responsabilité du gouvernement.', interpellationsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} interpellations exigeant la responsabilité ministérielle.`, debateDynamics: 'Dynamique du débat et responsabilité', ministerAccountability: 'Adressée à', @@ -763,6 +769,7 @@ export const CONTENT_LABELS_PART1: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Les commissions parlementaires ont été actives dans les domaines ${committees} et ${extra} autres domaines politiques.` : `Les commissions parlementaires ont été actives dans les domaines ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Un total de ${n} rapports démontre un élan législatif soutenu.`, oppositionStrategyContext: (n: number): string => `Des motions de ${n} partis différents révèlent l'étendue de la critique politique de l'opposition.`, + interpellationStrategyContext: (n: number): string => `Des interpellations de ${n} partis différents démontrent un contrôle parlementaire coordonné et des exigences de responsabilité gouvernementale.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `Ces ${propCount} propositions touchent à ${domainCount} domaine${domainCount > 1 ? 's' : ''} politique${domainCount > 1 ? 's' : ''}, démontrant l'ambition législative du gouvernement.`, genericOverview: (n: number): string => `Durant cette période, ${n} documents ont été traités au parlement.`, partyMotionsFiled: (party: string, n: number): string => `${party} : ${n} motion${n > 1 ? 's' : ''} déposée${n > 1 ? 's' : ''}`, diff --git a/scripts/data-transformers/constants/content-labels-part2.ts b/scripts/data-transformers/constants/content-labels-part2.ts index 12d9bf679bd..fe4dfa07fe0 100644 --- a/scripts/data-transformers/constants/content-labels-part2.ts +++ b/scripts/data-transformers/constants/content-labels-part2.ts @@ -70,7 +70,7 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} nuevas mociones.` , noInterpellations: 'No hay interpelaciones disponibles en este momento.', - interpellationDefault: 'Interpelación parlamentaria dirigida a un ministro.', + interpellationDefault: 'Interpelación parlamentaria que exige rendición de cuentas gubernamental.', interpellationsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} interpelaciones exigiendo responsabilidad ministerial.`, debateDynamics: 'Dinámica del debate y responsabilidad', ministerAccountability: 'Dirigida a', @@ -85,6 +85,7 @@ export const CONTENT_LABELS_PART2: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Las comisiones parlamentarias han estado activas en las áreas de ${committees} y ${extra} dominios políticos más.` : `Las comisiones parlamentarias han estado activas en las áreas de ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Un total de ${n} informes demuestra un impulso legislativo sostenido.`, oppositionStrategyContext: (n: number): string => `Mociones de ${n} partidos diferentes revelan la amplitud de la crítica política de la oposición.`, + interpellationStrategyContext: (n: number): string => `Interpelaciones de ${n} partidos diferentes demuestran un control parlamentario coordinado y exigencias de rendición de cuentas gubernamental.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `Estas ${propCount} proposiciones abarcan ${domainCount} dominio${domainCount > 1 ? 's' : ''} político${domainCount > 1 ? 's' : ''}, demostrando la amplia ambición legislativa del gobierno.`, genericOverview: (n: number): string => `Durante este período, ${n} documentos fueron procesados en el parlamento.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} moción${n > 1 ? 'es' : ''} presentada${n > 1 ? 's' : ''}`, @@ -183,7 +184,7 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `Oppositieleden hebben ${n} nieuwe moties ingediend.` , noInterpellations: 'Geen interpellaties beschikbaar op dit moment.', - interpellationDefault: 'Parlementaire interpellatie aan een minister.', + interpellationDefault: 'Parlementaire interpellatie die verantwoording van de regering eist.', interpellationsBreakdown: (n: number): string => `Oppositieleden hebben ${n} interpellaties ingediend om ministeriële verantwoording te eisen.`, debateDynamics: 'Debatdynamiek en verantwoording', ministerAccountability: 'Gericht aan', @@ -198,6 +199,7 @@ export const CONTENT_LABELS_PART2: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `De parlementaire commissies zijn actief geweest op het gebied van ${committees} en ${extra} verdere beleidsterreinen.` : `De parlementaire commissies zijn actief geweest op het gebied van ${committees}.`, committeeMomentumTakeaway: (n: number): string => `In totaal ${n} rapporten tonen aanhoudend wetgevend momentum.`, oppositionStrategyContext: (n: number): string => `Moties van ${n} verschillende partijen tonen de breedte van de politieke kritiek van de oppositie.`, + interpellationStrategyContext: (n: number): string => `Interpellaties van ${n} verschillende partijen tonen gecoördineerde parlementaire controle en eisen van regeringsverantwoordelijkheid.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `Deze ${propCount} wetsvoorstellen bestrijken ${domainCount} beleidsterrein${domainCount > 1 ? 'en' : ''}, wat de brede wetgevende ambitie van de regering laat zien.`, genericOverview: (n: number): string => `In deze periode zijn ${n} documenten in het parlement behandeld.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} motie${n > 1 ? 's' : ''} ingediend`, @@ -296,7 +298,7 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} اقتراحات جديدة.` , noInterpellations: 'لا توجد استجوابات متاحة حالياً.', - interpellationDefault: 'استجواب برلماني موجه إلى وزير.', + interpellationDefault: 'استجواب برلماني يطالب بالمساءلة الحكومية.', interpellationsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} استجواباً يطالبون فيه بمساءلة الوزراء.`, debateDynamics: 'ديناميكيات النقاش والمساءلة', ministerAccountability: 'موجه إلى', @@ -311,6 +313,7 @@ export const CONTENT_LABELS_PART2: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `كانت اللجان البرلمانية نشطة في مجالات ${committees} و${extra} مجالات سياسية أخرى.` : `كانت اللجان البرلمانية نشطة في مجالات ${committees}.`, committeeMomentumTakeaway: (n: number): string => `إجمالي ${n} تقارير يدل على زخم تشريعي مستمر.`, oppositionStrategyContext: (n: number): string => `اقتراحات من ${n} أحزاب مختلفة تكشف عن اتساع النقد السياسي للمعارضة.`, + interpellationStrategyContext: (n: number): string => `استجوابات من ${n} أحزاب مختلفة تُظهر رقابة برلمانية منسقة ومطالب بالمساءلة الحكومية.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `تمس هذه المقترحات الـ ${propCount} ${domainCount} مجال${domainCount > 1 ? 'ات' : ''} سياسي${domainCount > 1 ? 'ة' : ''}.`, genericOverview: (n: number): string => `خلال هذه الفترة، تمت معالجة ${n} وثائق في البرلمان.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} اقتراح${n > 1 ? 'ات' : ''} مقدمة`, @@ -409,7 +412,7 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} הצעות חדשות.` , noInterpellations: 'אין אינטרפלציות זמינות כרגע.', - interpellationDefault: 'אינטרפלציה פרלמנטרית המופנית לשר.', + interpellationDefault: 'אינטרפלציה פרלמנטרית הדורשת אחריותיות ממשלתית.', interpellationsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} אינטרפלציות הדורשות אחריות שרים.`, debateDynamics: 'דינמיקת הדיון ואחריות', ministerAccountability: 'מופנית אל', @@ -424,6 +427,7 @@ export const CONTENT_LABELS_PART2: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `ועדות הפרלמנט היו פעילות בתחומי ${committees} ו-${extra} תחומי מדיניות נוספים.` : `ועדות הפרלמנט היו פעילות בתחומי ${committees}.`, committeeMomentumTakeaway: (n: number): string => `סך של ${n} דוחות מעיד על מומנטום חקיקתי מתמשך.`, oppositionStrategyContext: (n: number): string => `הצעות מ-${n} מפלגות שונות חושפות את רוחב הביקורת הפוליטית של האופוזיציה.`, + interpellationStrategyContext: (n: number): string => `אינטרפלציות מ-${n} מפלגות שונות מדגימות פיקוח פרלמנטרי מתואם ודרישות לאחריותיות ממשלתית.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `${propCount} הצעות חוק אלה נוגעות ל-${domainCount} תחומ${domainCount > 1 ? 'י' : ''} מדיניות, המדגימות את שאיפת החקיקה הרחבה של הממשלה.`, genericOverview: (n: number): string => `במהלך תקופה זו, ${n} מסמכים עברו טיפול בפרלמנט.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} הצע${n > 1 ? 'ות' : 'ה'} הוגש${n > 1 ? 'ו' : 'ה'}`, @@ -522,7 +526,7 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `野党議員が${n}件の新たな動議を提出しました。` , noInterpellations: '現在、質問主意書はありません。', - interpellationDefault: '大臣に宛てた議会質問主意書。', + interpellationDefault: '政府の説明責任を求める議会質問主意書。', interpellationsBreakdown: (n: number): string => `野党議員が${n}件の質問主意書を提出し、大臣の説明責任を求めています。`, debateDynamics: '討論の動態と説明責任', ministerAccountability: '宛先', @@ -537,6 +541,7 @@ export const CONTENT_LABELS_PART2: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `議会の委員会は${committees}および他${extra}の政策分野で活動しています。` : `議会の委員会は${committees}の分野で活動しています。`, committeeMomentumTakeaway: (n: number): string => `合計${n}件の報告書は、持続的な立法の勢いを示しています。`, oppositionStrategyContext: (n: number): string => `${n}つの異なる政党からの動議は、野党の政治的批判の幅を示しています。`, + interpellationStrategyContext: (n: number): string => `${n}つの異なる政党からの質問主意書は、協調的な議会監視と政府の説明責任への要求を示しています。`, policyImplicationsContext: (propCount: number, domainCount: number): string => `これら${propCount}件の法案は${domainCount}つの政策分野に及び、政府の幅広い立法意欲を示しています。`, genericOverview: (n: number): string => `この期間中、議会で${n}件の文書が処理されました。`, partyMotionsFiled: (party: string, n: number): string => `${party}:${n}件の動議を提出`, @@ -635,7 +640,7 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `야당 의원들이 ${n}개의 새 동의안을 제출했습니다.` , noInterpellations: '현재 대정부 질의가 없습니다.', - interpellationDefault: '장관에게 보내는 의회 대정부 질의.', + interpellationDefault: '정부의 책임을 요구하는 의회 대정부질문.', interpellationsBreakdown: (n: number): string => `야당 의원들이 장관의 책임을 요구하는 ${n}개의 대정부 질의를 제출했습니다.`, debateDynamics: '토론 역학 및 책임성', ministerAccountability: '수신자', @@ -650,6 +655,7 @@ export const CONTENT_LABELS_PART2: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `의회 위원회는 ${committees} 및 ${extra}개의 추가 정책 분야에서 활동했습니다.` : `의회 위원회는 ${committees} 분야에서 활동했습니다.`, committeeMomentumTakeaway: (n: number): string => `총 ${n}건의 보고서는 지속적인 입법 추진력을 보여줍니다.`, oppositionStrategyContext: (n: number): string => `${n}개 정당의 동의안은 야당의 정치적 비판의 폭을 보여줍니다.`, + interpellationStrategyContext: (n: number): string => `${n}개 정당의 대정부질문은 조율된 의회 감시와 정부 책임 요구를 보여줍니다.`, policyImplicationsContext: (propCount: number, domainCount: number): string => `이 ${propCount}건의 법안은 ${domainCount}개 정책 영역에 걸쳐 있으며, 정부의 광범위한 입법 야심을 보여줍니다.`, genericOverview: (n: number): string => `이 기간 동안 의회에서 ${n}건의 문서가 처리되었습니다.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n}건의 동의안 제출`, @@ -748,7 +754,7 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `反对党议员提交了${n}项新动议。` , noInterpellations: '目前没有质询可用。', - interpellationDefault: '向部长提出的议会质询。', + interpellationDefault: '要求政府问责的议会质询。', interpellationsBreakdown: (n: number): string => `反对党议员提交了${n}项质询,要求追究部长责任。`, debateDynamics: '辩论动态与问责制', ministerAccountability: '质询对象', @@ -763,6 +769,7 @@ export const CONTENT_LABELS_PART2: Partial> = { committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `议会委员会在${committees}以及其他${extra}个政策领域积极开展工作。` : `议会委员会在${committees}领域积极开展工作。`, committeeMomentumTakeaway: (n: number): string => `共${n}份报告展示了持续的立法势头。`, oppositionStrategyContext: (n: number): string => `来自${n}个不同政党的动议显示了反对派政治批评的广度。`, + interpellationStrategyContext: (n: number): string => `来自${n}个不同政党的质询显示了协调的议会监督和对政府问责的要求。`, policyImplicationsContext: (propCount: number, domainCount: number): string => `这${propCount}项提案涉及${domainCount}个政策领域,展示了政府广泛的立法雄心。`, genericOverview: (n: number): string => `在此期间,议会处理了${n}份文件。`, partyMotionsFiled: (party: string, n: number): string => `${party}:提交了${n}项动议`, diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts index f293acc6aac..56246fc8465 100644 --- a/scripts/data-transformers/content-generators/interpellations.ts +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -137,15 +137,12 @@ function renderInterpellationEntry(doc: RawDocument, lang: Language | string): s * 4. **Deep Analysis** — 5W framework (who, what, when, why, impact) * 5. **Coalition Dynamics** — Cross-party accountability patterns * - * @param data - Article content data (interpellations loaded in `data.motions`) + * @param data - Article content data (interpellations loaded in `data.interpellations`) * @param lang - Target language * @returns Generated HTML content string */ export function generateInterpellationsContent(data: ArticleContentData, lang: Language | string): string { - // Interpellations are stored in data.motions by the data pipeline - // (for backward compatibility) but may also appear in data.documents. - const motionsArray = data.motions ?? []; - const interpellations: RawDocument[] = motionsArray.length > 0 ? motionsArray : (data.documents ?? []); + const interpellations: RawDocument[] = data.interpellations || []; let content = `

      ${L(lang, 'interpellationsTag')}

      \n`; diff --git a/scripts/data-transformers/document-analysis.ts b/scripts/data-transformers/document-analysis.ts index a077dce580f..eb7b9e69a37 100644 --- a/scripts/data-transformers/document-analysis.ts +++ b/scripts/data-transformers/document-analysis.ts @@ -293,6 +293,61 @@ export function renderMotionEntry(motion: RawDocument, lang: Language | string): `; } +/** + * Render a single interpellation entry with interpellation-specific labels and CSS classes. + * Uses 'ip' doktyp for domain analysis and interpellation-specific link/default labels. + */ +export function renderInterpellationEntry(doc: RawDocument, lang: Language | string): string { + const titleText = doc.titel || doc.title || ''; + const escapedTitle = escapeHtml(titleText); + const titleHtml = (doc.titel && !doc.title) + ? svSpan(escapedTitle, lang) + : escapedTitle; + const docName = escapeHtml(doc.dokumentnamn || doc.dok_id || titleText); + + const unknownVal = L(lang, 'unknown'); + let authorName = (doc.intressent_namn !== 'Unknown' ? doc.intressent_namn : null) + || (doc.author !== 'Unknown' ? doc.author : null) + || ''; + let partyName = (doc.parti !== 'Unknown' ? doc.parti : '') || ''; + if (!authorName || !partyName) { + const rawText = doc.undertitel || doc.summary || doc.notis || doc.fullText || doc.titel || doc.rubrik || ''; + const parsed = parseMotionAuthorParty(rawText); + if (parsed) { + if (parsed.author && !authorName) authorName = parsed.author; + if (parsed.party && !partyName) partyName = parsed.party; + } + } + if (!authorName) authorName = typeof unknownVal === 'string' ? unknownVal : 'Unknown'; + const authorLine = partyName + ? `${escapeHtml(authorName)} (${escapeHtml(partyName)})` + : escapeHtml(authorName); + + const summaryText = generateEnhancedSummary(doc, 'interpellation', lang); + const interpDefaultVal = L(lang, 'interpellationDefault'); + // Detect Swedish content: use titel-without-title heuristic (same as renderMotionEntry) + // or match Swedish-specific boilerplate (not just "Interpellation" which is the same in English) + const isSwedishContent = (doc.titel && !doc.title) + || (doc.summary || doc.notis || '').includes('Interpellation till'); + const summaryHtml = (summaryText && summaryText !== interpDefaultVal && isSwedishContent) + ? svSpan(escapeHtml(summaryText), lang) + : escapeHtml(summaryText || (typeof interpDefaultVal === 'string' ? interpDefaultVal : '')); + + const readFullVal = L(lang, 'readFullInterpellation'); + const whyItMattersVal = L(lang, 'whyItMatters'); + const dateHtml = formatDocumentDate(doc, lang); + + return ` +
      +

      ${titleHtml}

      +

      ${L(lang, 'filedBy')}: ${authorLine}

      ${dateHtml ? `\n

      ${dateHtml}

      ` : ''} +

      ${summaryHtml}

      +

      ${escapeHtml(String(whyItMattersVal))}: ${generateDeepPolicyAnalysis(doc, lang, 'ip')}

      +

      ${escapeHtml(String(readFullVal))}: ${docName}

      +
      +`; +} + /** * Generate per-document analysis. * PRIMARY: full document text, policy significance, related speeches. diff --git a/scripts/data-transformers/helpers.ts b/scripts/data-transformers/helpers.ts index 6312010d3aa..7f86f5352b1 100644 --- a/scripts/data-transformers/helpers.ts +++ b/scripts/data-transformers/helpers.ts @@ -222,8 +222,10 @@ export function propSummaryFromOrgan(organ: string, lang: Language | string): st * Uses document type, subtype, organ, and other metadata to create informative placeholder */ export function generateEnhancedSummary(doc: RawDocument, type: string, lang: Language | string): string { - // For motions: clean raw Swedish notis text before returning - if ((type === 'motion') && (doc.summary || doc.notis)) { + // For motions/interpellations: clean raw Swedish notis text before returning. + // Note: cleanMotionText() only handles motion-specific boilerplate ("Motion till riksdagen", + // "Förslag till riksdagsbeslut"); interpellation text without those phrases is returned as-is. + if ((type === 'motion' || type === 'interpellation') && (doc.summary || doc.notis)) { const raw = (doc.summary || doc.notis || ''); // Skip person-profile data (e.g. "Tjänstgörande riksdagsledamot...", "Avliden 2011-09-20...") if (!isPersonProfileText(raw)) { @@ -286,6 +288,19 @@ export function generateEnhancedSummary(doc: RawDocument, type: string, lang: La const onVal = L(lang, 'on'); parts.push(`${typeof onVal === 'string' ? onVal : ''} ${subtyp}`); } + } else if (type === 'interpellation') { + // NOTE: do NOT prefix with author/party here — renderInterpellationEntry() + // already renders a dedicated "Filed by:" line, so including it in the + // summary would duplicate the attribution. + // Include target minister (mottagare) if available + // NOTE: do NOT escapeHtml here — callers escape the returned summary string + if (doc.mottagare) { + parts.push(`→ ${doc.mottagare}`); + } + if (subtyp) { + const onVal = L(lang, 'on'); + parts.push(`${typeof onVal === 'string' ? onVal : ''} ${subtyp}`); + } } // Add document type information if useful @@ -297,6 +312,7 @@ export function generateEnhancedSummary(doc: RawDocument, type: string, lang: La if (parts.length === 0) { const fallback = type === 'report' ? L(lang, 'reportDefault') : type === 'proposition' ? L(lang, 'propDefault') : + type === 'interpellation' ? L(lang, 'interpellationDefault') : L(lang, 'motionDefault'); return typeof fallback === 'string' ? fallback : ''; } diff --git a/scripts/data-transformers/policy-analysis.ts b/scripts/data-transformers/policy-analysis.ts index c86e87efa65..17ea821cb17 100644 --- a/scripts/data-transformers/policy-analysis.ts +++ b/scripts/data-transformers/policy-analysis.ts @@ -286,7 +286,8 @@ export function detectNarrativeFrames(doc: RawDocument): NarrativeFrame[] { return Array.from(frames); } -type _LangPair = { en: Record; sv: Record }; +/** Per-language analysis text. `en` and `sv` are always required; other languages are optional and fall back to `en`. */ +type _LangPair = { en: Record; sv: Record } & Partial>>; /** * Build a reverse lookup from any localised domain name back to the English key. @@ -318,7 +319,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Finansutskottets ståndpunkt i finanspolitiska frågor är i regel avgörande – kammaren följer nästan alltid utskottets rekommendation.', ip: 'Interpellationen kräver ministerns redovisning av finanspolitiken och tvingar ansvarigt statsråd att försvara skatte- och utgiftsbeslut i öppen kammardebatt.', default: 'Regeringens finanspolitiska förslag måste klara finansutskottets granskning och harmonisera med överskottsmålet för att nå bifall.' - } + }, + de: { default: `Haushaltspolitische Vorschläge müssen die strenge Prüfung des Finanzausschusses bestehen und die schwedische Überschussregel einhalten.` }, + fr: { default: `Les propositions fiscales doivent passer l'examen rigoureux de la commission des finances et s'aligner sur la règle d'excédent budgétaire suédoise.` }, + es: { default: `Las propuestas fiscales deben superar el riguroso escrutinio del comité de finanzas y alinearse con la regla de superávit fiscal sueca.` } }, 'defence and security policy': { en: { @@ -332,7 +336,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Utskottsbetänkanden om försvar formar Sveriges militära inriktning och NATO-integration – besluten har konsekvenser i decennier.', ip: 'Interpellationen granskar ministerns försvars- och säkerhetsbeslut och kräver offentlig redovisning av Sveriges NATO-åtaganden och militära beredskap.', default: 'Försvarspropositioner engagerar Sveriges NATO-förpliktelser och mekanismer för brett partistöd inom säkerhetspolitiken.' - } + }, + de: { default: `Verteidigungsvorschläge berühren Schwedens NATO-Verpflichtungen und Mechanismen zur parteiübergreifenden Konsensfindung in der nationalen Sicherheitspolitik.` }, + fr: { default: `Les propositions de défense engagent les obligations de la Suède envers l'OTAN et les mécanismes de consensus multipartite pour la législation de sécurité nationale.` }, + es: { default: `Las propuestas de defensa comprometen las obligaciones de Suecia con la OTAN y los mecanismos de consenso multipartidario para la legislación de seguridad nacional.` } }, 'environmental and climate policy': { en: { @@ -346,7 +353,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Miljöutskottet väger klimatambition mot ekonomisk konkurrenskraft – dess rekommendation sätter lagstiftningens utgångspunkt.', ip: 'Interpellationen ställer ministern till svars för miljö- och klimatåtaganden och kräver insyn i utsläppsmål och genomförandetider.', default: 'Miljöförslag måste navigera konkurrerande intressen från industrin, regionerna och EU:s klimatåtaganden.' - } + }, + de: { default: `Umweltvorschläge müssen konkurrierende Interessen aus Industrie, regionalen Regierungen und EU-Klimaverpflichtungen ausbalancieren.` }, + fr: { default: `Les propositions environnementales doivent naviguer entre les intérêts concurrents de l'industrie, des gouvernements régionaux et des engagements climatiques de l'UE.` }, + es: { default: `Las propuestas medioambientales deben navegar entre los intereses en competencia de la industria, los gobiernos regionales y los compromisos climáticos de la UE.` } }, 'healthcare policy': { en: { @@ -360,7 +370,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Socialutskottets betänkanden om hälso- och sjukvård sätter ramarna för det regionalt levererade men nationellt finansierade hälsosystemet.', ip: 'Interpellationen utmanar ministern om sjukvårdens leverans och patientresultat och kräver svar om tjänstebrister, väntetider eller regionala skillnader.', default: 'Hälso- och sjukvårdspropositioner kräver samordning mellan stat, regioner och professioner – en komplexitet som formar lagstiftningens tidslinje.' - } + }, + de: { default: `Gesundheitsvorschläge erfordern Koordination zwischen nationaler Regierung, Regionalräten und Fachverbänden — eine Komplexität, die den Gesetzgebungszeitplan prägt.` }, + fr: { default: `Les propositions en matière de santé nécessitent une coordination entre le gouvernement national, les conseils régionaux et les organismes professionnels.` }, + es: { default: `Las propuestas sanitarias requieren coordinación entre el gobierno nacional, los consejos regionales y los organismos profesionales — una complejidad que determina el calendario legislativo.` } }, 'migration policy': { en: { @@ -374,7 +387,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Socialförsäkringsutskottets migrationsbetänkanden navigerar Sveriges åtaganden enligt EU-rätten och FN:s flyktingkonvention.', ip: 'Interpellationen kräver ministerns offentliga försvar av migrationsbeslut och pressar på ansvarsskyldighet för asylprocesser, integrationsresultat eller utvisningspolitik.', default: 'Migrationspropositioner måste balansera EU-rättsliga förpliktelser med nationella politiska imperativ.' - } + }, + de: { default: `Migrationsvorschläge müssen EU-Regulierungsverpflichtungen mit nationalen politischen Erfordernissen in Einklang bringen — parteiübergreifende Unterstützung ist für dauerhafte Gesetzgebung unerlässlich.` }, + fr: { default: `Les propositions migratoires doivent équilibrer les obligations réglementaires de l'UE et les impératifs politiques nationaux — le soutien multipartite est essentiel pour une législation durable.` }, + es: { default: `Las propuestas migratorias deben equilibrar las obligaciones regulatorias de la UE con los imperativos políticos nacionales, siendo el apoyo multipartidario esencial para una legislación duradera.` } }, 'EU and foreign affairs': { en: { @@ -388,7 +404,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Utrikesutskottets betänkanden om EU-frågor speglar Sveriges positionering inom unionen och kan binda framtida förhandlingslinjer.', ip: 'Interpellationen tvingar ministern att redovisa Sveriges utrikespolitiska eller EU-relaterade förhandlingsposition i öppen debatt och stärker riksdagens insyn i regeringens diplomati.', default: 'EU- och utrikespropositioner engagerar Sveriges fördragsförpliktelser och kräver samordning med europeiska partner.' - } + }, + de: { default: `EU- und Außenpolitikvorschläge berühren Schwedens Vertragserpflichtungen und erfordern oft eine Koordination mit europäischen Partnern.` }, + fr: { default: `Les propositions en matière d'UE et d'affaires étrangères engagent les obligations conventionnelles de la Suède et nécessitent souvent une coordination avec les partenaires européens.` }, + es: { default: `Las propuestas de la UE y de asuntos exteriores comprometen las obligaciones convencionales de Suecia y a menudo requieren coordinación con los socios europeos.` } }, 'justice policy': { en: { @@ -402,7 +421,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Justitieutskottet formar den straffrättsliga ramen; dess betänkanden om straffsatser och polisverksamhet påverkar direkt åklagarnas praxis.', ip: 'Interpellationen tvingar ministern att försvara rättspolitiska beslut i kammaren och bemöta allmänhetens oro om brottslighet, straffsatser eller polisens effektivitet.', default: 'Rättsliga propositioner balanserar rättsstatsprinciper, mänskliga rättigheter och allmän säkerhet.' - } + }, + de: { default: `Justizvorschläge balancieren Rechtsstaatsprinzipien, Menschenrechtsverpflichtungen und öffentliche Sicherheitsforderungen — sorgfältige Ausarbeitung ist erforderlich, um der verfassungsrechtlichen Prüfung standzuhalten.` }, + fr: { default: `Les propositions de justice équilibrent les principes de l'État de droit, les obligations en matière de droits de l'homme et les exigences de sécurité publique.` }, + es: { default: `Las propuestas de justicia equilibran los principios del Estado de derecho, las obligaciones de derechos humanos y las demandas de seguridad pública — requiriendo una redacción cuidadosa para resistir el escrutinio constitucional.` } }, 'labour market policy': { en: { @@ -416,7 +438,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Arbetsmarknadsutskottets betänkanden om arbetsplatslagar måste navigera kollektivavtalens självständighet.', ip: 'Interpellationen ställer ministern till svars för anställningsvillkor, lönepolitik eller arbetsmarknadsreglering och kräver svar inför arbetsmarknadens parter och allmänheten.', default: 'Arbetsmarknadspropositioner träder in i en arena där trepartsförhandlingar formar det slutliga lagstiftningsresultatet.' - } + }, + de: { default: `Arbeitsmarktvorschläge betreten eine Arena, in der Tarifverhandlungen das Gesetzgebungsergebnis ebenso stark prägen wie Parlamentsabstimmungen.` }, + fr: { default: `Les propositions sur le marché du travail entrent dans une arène où la négociation tripartite détermine le résultat législatif autant que les votes parlementaires.` }, + es: { default: `Las propuestas del mercado laboral entran en una arena donde la negociación tripartita determina el resultado legislativo tanto como los votos parlamentarios.` } }, 'housing policy': { en: { @@ -430,7 +455,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Civilutskottets bostadsbetänkanden hanterar en av Sveriges mest ihållande politiska utmaningar.', ip: 'Interpellationen pressar ministern att svara för bostadsbrist, hyresreglering eller bygghinder – frågor med direkt påverkan på hushållens välfärd.', default: 'Bostadspropositioner måste balansera konkurrerande intressen från kommuner, fastighetsägare, hyresgäster och byggföretag.' - } + }, + de: { default: `Wohnungsvorschläge müssen konkurrierende Interessen von Gemeinden, Eigentümern, Mietern und Bauträgern in Einklang bringen.` }, + fr: { default: `Les propositions de logement doivent concilier les intérêts concurrents des municipalités, propriétaires, locataires et promoteurs.` }, + es: { default: `Las propuestas de vivienda deben conciliar los intereses en competencia de municipios, propietarios, inquilinos y promotores.` } }, 'transport policy': { en: { @@ -444,7 +472,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Trafikutskottets betänkanden vägleder Sveriges nationella infrastrukturplanering och påverkar direkt långsiktiga investeringsprioriteringar.', ip: 'Interpellationen utmanar ministern om transportinfrastruktur, trafiksäkerhet eller kollektivtrafik och kräver insyn i investeringsprioriteringar och regional rättvisa.', default: 'Transportpropositioner engagerar den nationella infrastrukturbudgeten, regional jämlikhet och klimatomställningsmål.' - } + }, + de: { default: `Verkehrsvorschläge betreffen den nationalen Infrastrukturhaushalt, regionale Gerechtigkeit und Klimaübergangsziele.` }, + fr: { default: `Les propositions de transport concernent le budget national d'infrastructure, l'équité régionale et les objectifs de transition climatique.` }, + es: { default: `Las propuestas de transporte implican el presupuesto nacional de infraestructura, la equidad regional y los objetivos de transición climática.` } }, 'trade and industry policy': { en: { @@ -458,7 +489,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Näringsutskottets betänkanden formar Sveriges affärsmiljö och sätter villkoren för investeringar och export.', ip: 'Interpellationen ställer ministern till svars för handels-, konkurrens- eller industripolitik och kräver motivering av de policyval som påverkar Sveriges ekonomiska ställning.', default: 'Näringspolitiska propositioner engagerar internationella åtaganden, EU:s inre marknadsregler och inhemsk konkurrenskraft.' - } + }, + de: { default: `Industrie- und Handelsvorschläge berühren gleichzeitig internationale Verpflichtungen, EU-Binnenmarktregeln und inländische Wettbewerbserfordernisse.` }, + fr: { default: `Les propositions industrielles et commerciales engagent simultanément des engagements internationaux, les règles du marché unique européen et les impératifs de compétitivité nationale.` }, + es: { default: `Las propuestas industriales y comerciales comprometen simultáneamente compromisos internacionales, normas del mercado único europeo e imperativos de competitividad nacional.` } }, 'education policy': { en: { @@ -472,7 +506,10 @@ const DOMAIN_ANALYSES: Record = { bet: 'Utbildningsutskottets betänkanden formar direkt läroplaner, finansieringsmodeller och skolreglering.', ip: 'Interpellationen kräver att ministern förklarar utbildningspolitiska val i öppen debatt — vare sig det gäller skolstandard, lärarrekrytering eller friskolesystemet.', default: 'Utbildningspropositioner måste balansera nationella läroplaner med kommunalt leveransansvar och de privata aktörernas omstridda roll.' - } + }, + de: { default: `Bildungsvorschläge berühren geteilte Zuständigkeiten zwischen Zentralregierung, Kommunen und unabhängigen Schulträgern.` }, + fr: { default: `Les propositions en matière d\'éducation engagent des responsabilités partagées entre le gouvernement central, les municipalités et les opérateurs scolaires indépendants.` }, + es: { default: `Las propuestas educativas comprometen responsabilidades compartidas entre el gobierno central, los municipios y los operadores escolares independientes.` } } }; diff --git a/scripts/data-transformers/types.ts b/scripts/data-transformers/types.ts index b63dc2462fe..14e6d64d6ac 100644 --- a/scripts/data-transformers/types.ts +++ b/scripts/data-transformers/types.ts @@ -139,6 +139,8 @@ export interface ArticleContentData { reports?: RawDocument[]; propositions?: RawDocument[]; motions?: RawDocument[]; + /** Parliamentary interpellations (interpellationer) */ + interpellations?: RawDocument[]; documents?: RawDocument[]; highlights?: Array<{ title: string; description: string }>; context?: string; diff --git a/scripts/generate-news-enhanced/generators.ts b/scripts/generate-news-enhanced/generators.ts index 9008292c79f..bbe0e4b31b6 100644 --- a/scripts/generate-news-enhanced/generators.ts +++ b/scripts/generate-news-enhanced/generators.ts @@ -30,6 +30,7 @@ import { } from '../data-transformers/index.js'; import { generateDeepAnalysisSection, localizeDocType } from '../data-transformers/content-generators/index.js'; import { generateDeepPolicyAnalysis, detectPolicyDomains } from '../data-transformers/policy-analysis.js'; +import { analyzeDashboardData } from '../ai-analysis/dashboard-analyzer.js'; import { escapeHtml } from '../html-utils.js'; import { generateArticleHTML } from '../article-template.js'; import { MCPClient } from '../mcp-client.js'; @@ -97,8 +98,8 @@ export async function generateWeekAhead(): Promise { const weekData = { events: events as Parameters[0], documents, - questions, - interpellations, + questions: questions as import('../data-transformers/types.js').RawDocument[], + interpellations: interpellations as import('../data-transformers/types.js').RawDocument[], highlights: [] as Array<{title: string; description: string}>, }; const content: string = generateArticleContent(weekData, 'week-ahead', lang); @@ -443,10 +444,10 @@ export async function generateInterpellations(): Promise { for (const lang of languages) { console.log(` 🌐 Generating ${lang.toUpperCase()} version...`); - const typedInterps = interpellations as Parameters[0]['motions']; - const content: string = generateArticleContent({ motions: typedInterps }, 'interpellations', lang); - const watchPoints = extractWatchPoints({ motions: typedInterps }, lang); - const metadata = generateMetadata({ motions: typedInterps }, 'interpellations', lang); + const typedInterps = interpellations as import('../data-transformers/types.js').ArticleContentData['interpellations']; + const content: string = generateArticleContent({ interpellations: typedInterps }, 'interpellations', lang); + const watchPoints = extractWatchPoints({ interpellations: typedInterps }, lang); + const metadata = generateMetadata({ interpellations: typedInterps }, 'interpellations', lang); const readTime: string = calculateReadTime(content); const sources: string[] = generateSources(['get_interpellationer', 'get_dokument_innehall']); @@ -1069,20 +1070,20 @@ function buildKeyTakeaways(docs: RawDocument[], topic: string | null, lang: Lang /** Localised default SWOT entry text for when a stakeholder's quadrant has no documents. */ const SWOT_DEFAULTS: Readonly>>> = { // [withTopic, withoutTopic] - govStrength: { en: ['Policy initiative and agenda-setting on %t', 'Policy legislation in place'], sv: ['Politiskt initiativ och agendasättning för %t', 'Befintlig policylagstiftning'], de: ['Politische Initiative und Agenda-Setting zu %t', 'Politikgesetzgebung vorhanden'], fr: ['Initiative politique sur %t', 'Législation politique en place'], es: ['Iniciativa política sobre %t', 'Legislación de política vigente'] }, - govWeakness: { en: ['Implementation timeline and resource allocation for %t', 'Implementation timeline and resource prioritisation'], sv: ['Genomförandetidsplan och resurstilldelning för %t', 'Genomförandetidsplan och resursprioritering'], de: ['Umsetzungszeitplan und Ressourcenallokation für %t', 'Umsetzungszeitplan und Ressourcenpriorisierung'] }, - govOpportunity: { en: ['EU and international cooperation on %t', 'EU framework alignment'], sv: ['EU och internationellt samarbete om %t', 'EU-ramverksanpassning'], de: ['EU- und internationale Kooperation zu %t', 'EU-Rahmenausrichtung'] }, - govThreat: { en: ['Execution risks and stakeholder resistance to %t reform', 'Evolving threat landscape'], sv: ['Genomföranderisker och motstånd mot %t-reform', 'Föränderligt hotlandskap'], de: ['Umsetzungsrisiken und Widerstand gegen %t-Reform', 'Sich entwickelnde Bedrohungslandschaft'] }, - oppStrength: { en: ['Parliamentary oversight and scrutiny of %t proposals', 'Parliamentary oversight and accountability function'], sv: ['Parlamentarisk tillsyn och granskning av %t-förslag', 'Parlamentarisk tillsyn och ansvarsfunktion'], de: ['Parlamentarische Kontrolle der %t-Vorschläge', 'Parlamentarische Aufsicht und Rechenschaftsfunktion'] }, - oppWeakness: { en: ['Limited access to implementation data on %t', 'Limited classified information access'], sv: ['Begränsad tillgång till genomförandedata om %t', 'Begränsad tillgång till sekretessbelagd information'], de: ['Begrenzter Zugang zu Umsetzungsdaten zu %t', 'Begrenzter Zugang zu klassifizierten Informationen'] }, - oppOpportunity: { en: ['Cross-party consensus building on %t', 'Cross-party consensus building'], sv: ['Konsensusbyggande över partigränser om %t', 'Konsensusbyggande över partigränser'], de: ['Parteiübergreifender Konsensaufbau zu %t', 'Parteiübergreifender Konsensaufbau'] }, - oppThreat: { en: ['Government majority limiting amendment capacity on %t', 'Government majority limiting amendment capacity'], sv: ['Regeringsmajoriteten begränsar ändringskapaciteten för %t', 'Regeringsmajoriteten begränsar ändringskapaciteten'], de: ['Regierungsmehrheit schränkt Änderungskapazität bei %t ein', 'Regierungsmehrheit schränkt Änderungskapazität ein'] }, - privateStrength: { en: ['Domain expertise and operational capacity in %t', 'Technical expertise and operational capacity'], sv: ['Domänexpertis och operativ kapacitet inom %t', 'Teknisk expertis och operativ kapacitet'], de: ['Fachkompetenz und operative Kapazität in %t', 'Technisches Fachwissen und operative Kapazität'] }, - privateWeakness1: { en: ['Compliance costs and adaptation burden from %t regulation', 'Compliance costs and regulatory burden'], sv: ['Efterlevnadskostnader och anpassningsbörda från %t-reglering', 'Efterlevnadskostnader och regulatorisk börda'], de: ['Compliance-Kosten und Anpassungsbelastung durch %t-Regulierung', 'Compliance-Kosten und regulatorische Belastung'] }, - privateWeakness2: { en: ['Resource allocation for emerging %t requirements', 'Resource allocation for emerging requirements'], sv: ['Resursallokering för nya %t-krav', 'Resursallokering för nya krav'], de: ['Ressourcenzuweisung für neue %t-Anforderungen', 'Ressourcenzuweisung für neue Anforderungen'] }, - privateOpportunity: { en: ['Investment and innovation driven by %t policy', 'Policy-driven investment and innovation'], sv: ['Investering och innovation driven av %t-politik', 'Policydriven investering och innovation'], de: ['Investitionen und Innovation durch %t-Politik', 'Politikgetriebene Investitionen und Innovation'] }, - privateThreat1: { en: ['Rapid policy evolution creating uncertainty for %t stakeholders', 'Rapid threat evolution'], sv: ['Snabb policyutveckling skapar osäkerhet för %t-intressenter', 'Snabb hotutveckling'], de: ['Schnelle Politikentwicklung schafft Unsicherheit für %t-Stakeholder', 'Schnelle Bedrohungsentwicklung'] }, - privateThreat2: { en: ['Short implementation timelines for new %t requirements', 'Short implementation timelines for new requirements'], sv: ['Korta implementeringstidsplaner för nya %t-krav', 'Korta implementeringstidsplaner för nya krav'], de: ['Kurze Umsetzungsfristen für neue %t-Anforderungen', 'Kurze Umsetzungsfristen für neue Anforderungen'] }, + govStrength: { en: ['Policy initiative and agenda-setting on %t', 'Policy legislation in place'], sv: ['Politiskt initiativ och agendasättning för %t', 'Befintlig policylagstiftning'], da: ['Politisk initiativ og dagsordensfastsættelse om %t', 'Politiklovgivning på plads'], no: ['Politisk initiativ og dagsordensetting om %t', 'Politikklovgivning på plass'], fi: ['Poliittinen aloite ja asialistalle asettaminen %t', 'Politiikkaosaaminen olemassa'], de: ['Politische Initiative und Agenda-Setting zu %t', 'Politikgesetzgebung vorhanden'], fr: ['Initiative politique sur %t', 'Législation politique en place'], es: ['Iniciativa política sobre %t', 'Legislación de política vigente'], nl: ['Politiek initiatief en agendabepaling over %t', 'Beleidsregels aanwezig'], ar: ['المبادرة السياسية وتحديد الأجندة بشأن %t', 'تشريع السياسات موجود'], he: ['יוזמה מדינית וקביעת סדר יום בנושא %t', 'חקיקת מדיניות קיימת'], ja: ['%tに関する政策立案とアジェンダ設定', '政策立法が整備済み'], ko: ['%t에 관한 정책 발의 및 의제 설정', '정책 법률 마련'], zh: ['关于%t的政策主导权和议程设定', '相关政策法规已到位'] }, + govWeakness: { en: ['Implementation timeline and resource allocation for %t', 'Implementation timeline and resource prioritisation'], sv: ['Genomförandetidsplan och resurstilldelning för %t', 'Genomförandetidsplan och resursprioritering'], da: ['Implementeringstidsplan og ressourceallokering til %t', 'Implementeringstidsplan og ressourceprioritering'], no: ['Gjennomføringstidsplan og ressursallokering for %t', 'Gjennomføringstidsplan og ressursprioritering'], fi: ['Toteutusaikataulu ja resurssien kohdentaminen %t', 'Toteutusaikataulu ja resurssien priorisointi'], de: ['Umsetzungszeitplan und Ressourcenallokation für %t', 'Umsetzungszeitplan und Ressourcenpriorisierung'], fr: ['Calendrier de mise en œuvre et allocation des ressources pour %t', 'Calendrier de mise en œuvre et priorisation des ressources'], es: ['Cronograma de implementación y asignación de recursos para %t', 'Cronograma de implementación y priorización de recursos'], nl: ['Implementatietijdlijn en toewijzing van middelen voor %t', 'Implementatietijdlijn en prioritering van middelen'], ar: ['الجدول الزمني للتنفيذ وتخصيص الموارد لـ %t', 'الجدول الزمني للتنفيذ وترتيب أولويات الموارد'], he: ['לוח זמנים לביצוע והקצאת משאבים עבור %t', 'לוח זמנים לביצוע ותעדוף משאבים'], ja: ['%tの実施スケジュールと資源配分', '実施スケジュールと資源の優先配分'], ko: ['%t의 이행 일정 및 자원 배분', '이행 일정 및 자원 우선순위화'], zh: ['%t的实施时间表和资源分配', '实施时间表和资源优先排序'] }, + govOpportunity: { en: ['EU and international cooperation on %t', 'EU framework alignment'], sv: ['EU och internationellt samarbete om %t', 'EU-ramverksanpassning'], da: ['EU og internationalt samarbejde om %t', 'Tilpasning til EU-rammer'], no: ['EU og internasjonalt samarbeid om %t', 'EU-rammetilpasning'], fi: ['EU ja kansainvälinen yhteistyö %t', 'EU-kehyksen mukauttaminen'], de: ['EU- und internationale Kooperation zu %t', 'EU-Rahmenausrichtung'], fr: ['Coopération avec l\'UE sur %t', 'Alignement sur le cadre européen'], es: ['Cooperación con la UE sobre %t', 'Alineación con el marco de la UE'], nl: ['EU- en internationale samenwerking over %t', 'Afstemming op EU-kader'], ar: ['التعاون مع الاتحاد الأوروبي والدولي بشأن %t', 'الانسجام مع الإطار الأوروبي'], he: ['שיתוף פעולה עם האיחוד האירופי בנושא %t', 'יישור קו עם מסגרת האיחוד האירופי'], ja: ['%tに関するEUおよび国際協力', 'EU枠組みとの整合'], ko: ['%t에 관한 EU 및 국제 협력', 'EU 프레임워크 정합성'], zh: ['关于%t的欧盟及国际合作', '与欧盟框架协调一致'] }, + govThreat: { en: ['Execution risks and stakeholder resistance to %t reform', 'Evolving threat landscape'], sv: ['Genomföranderisker och motstånd mot %t-reform', 'Föränderligt hotlandskap'], da: ['Gennemførelsesrisici og modstand mod %t-reform', 'Udviklende trusselsbillede'], no: ['Gjennomføringsrisiko og motstand mot %t-reform', 'Utviklende trusselbilde'], fi: ['Toteutusriskit ja sidosryhmien vastustus %t-uudistukselle', 'Kehittyvä uhkakenttä'], de: ['Umsetzungsrisiken und Widerstand gegen %t-Reform', 'Sich entwickelnde Bedrohungslandschaft'], fr: ['Risques d\'exécution et résistance à la réforme %t', 'Paysage des menaces en évolution'], es: ['Riesgos de ejecución y resistencia a la reforma %t', 'Panorama de amenazas en evolución'], nl: ['Uitvoeringsrisico\'s en weerstand tegen %t-hervorming', 'Evoluerend dreigingslandschap'], ar: ['مخاطر التنفيذ ومقاومة إصلاح %t', 'مشهد التهديدات المتطور'], he: ['סיכוני ביצוע והתנגדות לרפורמת %t', 'נוף איומים מתפתח'], ja: ['%t改革の実施リスクとステークホルダーの抵抗', '脅威情勢の変化'], ko: ['%t 개혁에 대한 실행 위험 및 이해관계자 저항', '진화하는 위협 환경'], zh: ['%t改革的执行风险和利益相关者抵制', '不断演变的威胁形势'] }, + oppStrength: { en: ['Parliamentary oversight and scrutiny of %t proposals', 'Parliamentary oversight and accountability function'], sv: ['Parlamentarisk tillsyn och granskning av %t-förslag', 'Parlamentarisk tillsyn och ansvarsfunktion'], da: ['Parlamentarisk tilsyn og kontrol af %t-forslag', 'Parlamentarisk tilsyn og ansvarlighedsfunktion'], no: ['Parlamentarisk tilsyn og gransking av %t-forslag', 'Parlamentarisk tilsyn og ansvarlighetsfunksjon'], fi: ['Parlamentaarinen valvonta ja %t-esitysten tarkastelu', 'Parlamentaarinen valvonta ja vastuuvelvollisuustehtävä'], de: ['Parlamentarische Kontrolle der %t-Vorschläge', 'Parlamentarische Aufsicht und Rechenschaftsfunktion'], fr: ['Contrôle parlementaire des propositions %t', 'Contrôle parlementaire et fonction de responsabilité'], es: ['Supervisión parlamentaria de las propuestas %t', 'Función de supervisión y rendición de cuentas parlamentaria'], nl: ['Parlementair toezicht op %t-voorstellen', 'Parlementair toezicht en verantwoordingsfunctie'], ar: ['الرقابة البرلمانية على مقترحات %t', 'وظيفة الرقابة البرلمانية والمساءلة'], he: ['פיקוח פרלמנטרי על הצעות %t', 'פיקוח פרלמנטרי ותפקיד אחריות'], ja: ['%t提案に対する議会の監視と精査', '議会の監視および説明責任機能'], ko: ['%t 제안에 대한 의회 감독 및 심사', '의회 감독 및 책임 기능'], zh: ['对%t提案的议会监督与审查', '议会监督和问责功能'] }, + oppWeakness: { en: ['Limited access to implementation data on %t', 'Limited classified information access'], sv: ['Begränsad tillgång till genomförandedata om %t', 'Begränsad tillgång till sekretessbelagd information'], da: ['Begrænset adgang til implementeringsdata om %t', 'Begrænset adgang til klassificerede oplysninger'], no: ['Begrenset tilgang til implementeringsdata om %t', 'Begrenset tilgang til klassifisert informasjon'], fi: ['Rajallinen pääsy %t:n toteutustietoihin', 'Rajallinen pääsy salaisiin tietoihin'], de: ['Begrenzter Zugang zu Umsetzungsdaten zu %t', 'Begrenzter Zugang zu klassifizierten Informationen'], fr: ['Accès limité aux données de mise en œuvre sur %t', 'Accès limité aux informations classifiées'], es: ['Acceso limitado a datos de implementación sobre %t', 'Acceso limitado a información clasificada'], nl: ['Beperkte toegang tot implementatiedata over %t', 'Beperkte toegang tot geclassificeerde informatie'], ar: ['محدودية الوصول إلى بيانات تنفيذ %t', 'محدودية الوصول إلى المعلومات السرية'], he: ['גישה מוגבלת לנתוני יישום %t', 'גישה מוגבלת למידע מסווג'], ja: ['%tに関する実施データへの限られたアクセス', '機密情報へのアクセスが限定的'], ko: ['%t에 관한 이행 데이터에 대한 제한된 접근', '기밀 정보 접근 제한'], zh: ['对%t实施数据的访问有限', '机密信息访问受限'] }, + oppOpportunity: { en: ['Cross-party consensus building on %t', 'Cross-party consensus building'], sv: ['Konsensusbyggande över partigränser om %t', 'Konsensusbyggande över partigränser'], da: ['Tværpartipolitisk konsensusopbygning om %t', 'Tværpartipolitisk konsensusopbygning'], no: ['Tverrpartipolitisk konsensusbygging om %t', 'Tverrpartipolitisk konsensusbygging'], fi: ['Puolueiden välinen konsensuksen rakentaminen %t', 'Puolueiden välinen konsensuksen rakentaminen'], de: ['Parteiübergreifender Konsensaufbau zu %t', 'Parteiübergreifender Konsensaufbau'], fr: ['Consensus multipartite sur %t', 'Consensus multipartite'], es: ['Consenso transpartidario sobre %t', 'Consenso transpartidario'], nl: ['Partijoverstijgende consensusvorming over %t', 'Partijoverstijgende consensusvorming'], ar: ['بناء توافق متعدد الأحزاب بشأن %t', 'بناء توافق متعدد الأحزاب'], he: ['בניית קונסנזוס רב-מפלגתי בנושא %t', 'בניית קונסנזוס רב-מפלגתי'], ja: ['%tに関する超党派コンセンサス形成', '超党派コンセンサス形成'], ko: ['%t에 관한 초당적 합의 구축', '초당적 합의 구축'], zh: ['就%t问题构建跨党派共识', '构建跨党派共识'] }, + oppThreat: { en: ['Government majority limiting amendment capacity on %t', 'Government majority limiting amendment capacity'], sv: ['Regeringsmajoriteten begränsar ändringskapaciteten för %t', 'Regeringsmajoriteten begränsar ändringskapaciteten'], da: ['Regeringsflertallet begrænser ændringskapaciteten for %t', 'Regeringsflertallet begrænser ændringskapaciteten'], no: ['Regjeringsflertallet begrenser endringskapasiteten for %t', 'Regjeringsflertallet begrenser endringskapasiteten'], fi: ['Hallitusenemmistö rajoittaa muutoskapasiteettia %t', 'Hallitusenemmistö rajoittaa muutoskapasiteettia'], de: ['Regierungsmehrheit schränkt Änderungskapazität bei %t ein', 'Regierungsmehrheit schränkt Änderungskapazität ein'], fr: ['Majorité gouvernementale limitant la capacité d\'amendement sur %t', 'Majorité gouvernementale limitant la capacité d\'amendement'], es: ['Mayoría gubernamental que limita la capacidad de enmienda sobre %t', 'Mayoría gubernamental que limita la capacidad de enmienda'], nl: ['Regeringsmeerderheid beperkt wijzigingscapaciteit voor %t', 'Regeringsmeerderheid beperkt wijzigingscapaciteit'], ar: ['أغلبية الحكومة تحدّ من قدرة التعديل على %t', 'أغلبية الحكومة تحدّ من قدرة التعديل'], he: ['רוב ממשלתי המגביל יכולת תיקון בנושא %t', 'רוב ממשלתי המגביל יכולת תיקון'], ja: ['%tに関する修正能力を制限する与党多数派', '修正能力を制限する与党多数派'], ko: ['%t에 관한 수정 능력을 제한하는 집권 다수당', '수정 능력을 제한하는 집권 다수당'], zh: ['执政党多数限制了对%t的修改能力', '执政党多数限制修改能力'] }, + privateStrength: { en: ['Domain expertise and operational capacity in %t', 'Technical expertise and operational capacity'], sv: ['Domänexpertis och operativ kapacitet inom %t', 'Teknisk expertis och operativ kapacitet'], da: ['Domæneekspertise og operationel kapacitet inden for %t', 'Teknisk ekspertise og operationel kapacitet'], no: ['Domeneekspertise og operativ kapasitet innen %t', 'Teknisk ekspertise og operativ kapasitet'], fi: ['Alan asiantuntemus ja operatiivinen kapasiteetti %t', 'Tekninen asiantuntemus ja operatiivinen kapasiteetti'], de: ['Fachkompetenz und operative Kapazität in %t', 'Technisches Fachwissen und operative Kapazität'], fr: ['Expertise sectorielle et capacité opérationnelle en %t', 'Expertise technique et capacité opérationnelle'], es: ['Experiencia sectorial y capacidad operativa en %t', 'Experiencia técnica y capacidad operativa'], nl: ['Domeinexpertise en operationele capaciteit in %t', 'Technische expertise en operationele capaciteit'], ar: ['الخبرة القطاعية والقدرة التشغيلية في %t', 'الخبرة التقنية والقدرة التشغيلية'], he: ['מומחיות תחום וכושר תפעולי ב-%t', 'מומחיות טכנית וכושר תפעולי'], ja: ['%tにおけるドメイン専門知識と運営能力', '技術的専門知識と運営能力'], ko: ['%t 분야의 전문 지식 및 운영 역량', '기술적 전문 지식 및 운영 역량'], zh: ['%t领域的专业知识和运营能力', '技术专长和运营能力'] }, + privateWeakness1: { en: ['Compliance costs and adaptation burden from %t regulation', 'Compliance costs and regulatory burden'], sv: ['Efterlevnadskostnader och anpassningsbörda från %t-reglering', 'Efterlevnadskostnader och regulatorisk börda'], da: ['Overholdelsesomkostninger og tilpasningsbyrde fra %t-regulering', 'Overholdelsesomkostninger og regulatorisk byrde'], no: ['Samsvarskostnader og tilpasningsbyrde fra %t-regulering', 'Samsvarskostnader og regulatorisk byrde'], fi: ['Vaatimustenmukaisuuskustannukset ja sopeutumisrasitus %t-sääntelystä', 'Vaatimustenmukaisuuskustannukset ja sääntelytaakka'], de: ['Compliance-Kosten und Anpassungsbelastung durch %t-Regulierung', 'Compliance-Kosten und regulatorische Belastung'], fr: ['Coûts de conformité et fardeau d\'adaptation liés à la réglementation %t', 'Coûts de conformité et charge réglementaire'], es: ['Costos de cumplimiento y carga de adaptación de la regulación %t', 'Costos de cumplimiento y carga regulatoria'], nl: ['Nalevingskosten en aanpassingslast van %t-regelgeving', 'Nalevingskosten en regelgevingslast'], ar: ['تكاليف الامتثال وعبء التكيّف من تنظيم %t', 'تكاليف الامتثال والعبء التنظيمي'], he: ['עלויות ציות ועול הסתגלות מרגולציית %t', 'עלויות ציות ועול רגולטורי'], ja: ['%t規制によるコンプライアンスコストと適応負担', 'コンプライアンスコストと規制負担'], ko: ['%t 규제로 인한 규정 준수 비용 및 적응 부담', '규정 준수 비용 및 규제 부담'], zh: ['%t监管带来的合规成本和适应负担', '合规成本和监管负担'] }, + privateWeakness2: { en: ['Resource allocation for emerging %t requirements', 'Resource allocation for emerging requirements'], sv: ['Resursallokering för nya %t-krav', 'Resursallokering för nya krav'], da: ['Ressourceallokering til nye %t-krav', 'Ressourceallokering til nye krav'], no: ['Ressursallokering for nye %t-krav', 'Ressursallokering for nye krav'], fi: ['Resurssien kohdentaminen uusiin %t-vaatimuksiin', 'Resurssien kohdentaminen uusiin vaatimuksiin'], de: ['Ressourcenzuweisung für neue %t-Anforderungen', 'Ressourcenzuweisung für neue Anforderungen'], fr: ['Allocation de ressources pour les nouvelles exigences %t', 'Allocation de ressources pour les nouvelles exigences'], es: ['Asignación de recursos para nuevos requisitos de %t', 'Asignación de recursos para nuevos requisitos'], nl: ['Toewijzing van middelen voor nieuwe %t-vereisten', 'Toewijzing van middelen voor nieuwe vereisten'], ar: ['تخصيص الموارد للمتطلبات الناشئة %t', 'تخصيص الموارد للمتطلبات الناشئة'], he: ['הקצאת משאבים לדרישות %t המתפתחות', 'הקצאת משאבים לדרישות מתפתחות'], ja: ['新しい%t要件への資源配分', '新しい要件への資源配分'], ko: ['새로운 %t 요건에 대한 자원 배분', '새로운 요건에 대한 자원 배분'], zh: ['为新兴%t要求分配资源', '为新兴要求分配资源'] }, + privateOpportunity: { en: ['Investment and innovation driven by %t policy', 'Policy-driven investment and innovation'], sv: ['Investering och innovation driven av %t-politik', 'Policydriven investering och innovation'], da: ['Investering og innovation drevet af %t-politik', 'Politikdrevet investering og innovation'], no: ['Investering og innovasjon drevet av %t-politikk', 'Politikkdrevet investering og innovasjon'], fi: ['%t-politiikan ohjaama investointi ja innovointi', 'Politiikan ohjaama investointi ja innovointi'], de: ['Investitionen und Innovation durch %t-Politik', 'Politikgetriebene Investitionen und Innovation'], fr: ['Investissement et innovation portés par la politique %t', 'Investissement et innovation liés aux politiques'], es: ['Inversión e innovación impulsadas por la política %t', 'Inversión e innovación impulsadas por políticas'], nl: ['Investering en innovatie gedreven door %t-beleid', 'Beleidsgestuurde investering en innovatie'], ar: ['الاستثمار والابتكار المدفوعان بسياسة %t', 'الاستثمار والابتكار المدفوعان بالسياسة'], he: ['השקעה וחדשנות מונעות על ידי מדיניות %t', 'השקעה וחדשנות מונעות על ידי מדיניות'], ja: ['%t政策主導の投資とイノベーション', '政策主導の投資とイノベーション'], ko: ['%t 정책이 이끄는 투자 및 혁신', '정책 주도의 투자 및 혁신'], zh: ['%t政策推动的投资和创新', '政策驱动的投资和创新'] }, + privateThreat1: { en: ['Rapid policy evolution creating uncertainty for %t stakeholders', 'Rapid threat evolution'], sv: ['Snabb policyutveckling skapar osäkerhet för %t-intressenter', 'Snabb hotutveckling'], da: ['Hurtig politikudvikling skaber usikkerhed for %t-interessenter', 'Hurtig trusseludvikling'], no: ['Rask politikkutvikling skaper usikkerhet for %t-interessenter', 'Rask trusselutvikling'], fi: ['Nopea politiikan kehitys luo epävarmuutta %t-sidosryhmille', 'Nopea uhkakehitys'], de: ['Schnelle Politikentwicklung schafft Unsicherheit für %t-Stakeholder', 'Schnelle Bedrohungsentwicklung'], fr: ['Évolution rapide des politiques créant de l\'incertitude pour les parties prenantes %t', 'Évolution rapide des menaces'], es: ['Evolución rápida de las políticas creando incertidumbre para las partes interesadas de %t', 'Evolución rápida de amenazas'], nl: ['Snelle beleidsontwikkeling creëert onzekerheid voor %t-stakeholders', 'Snelle dreigingsontwikkeling'], ar: ['التطور السريع للسياسات يخلق حالة من عدم اليقين لأصحاب المصلحة في %t', 'التطور السريع للتهديدات'], he: ['התפתחות מדיניות מהירה יוצרת אי-ודאות עבור בעלי עניין %t', 'התפתחות איומים מהירה'], ja: ['%t関係者に不確実性をもたらす急速な政策変化', '脅威の急速な変化'], ko: ['%t 이해관계자에게 불확실성을 야기하는 정책의 급속한 변화', '급속한 위협 변화'], zh: ['政策快速演变给%t利益相关者带来不确定性', '威胁形势快速演变'] }, + privateThreat2: { en: ['Short implementation timelines for new %t requirements', 'Short implementation timelines for new requirements'], sv: ['Korta implementeringstidsplaner för nya %t-krav', 'Korta implementeringstidsplaner för nya krav'], da: ['Korte implementeringstidsplaner for nye %t-krav', 'Korte implementeringstidsplaner for nye krav'], no: ['Korte gjennomføringstidsplaner for nye %t-krav', 'Korte gjennomføringstidsplaner for nye krav'], fi: ['Lyhyet toteutusaikataulut uusille %t-vaatimuksille', 'Lyhyet toteutusaikataulut uusille vaatimuksille'], de: ['Kurze Umsetzungsfristen für neue %t-Anforderungen', 'Kurze Umsetzungsfristen für neue Anforderungen'], fr: ['Délais de mise en œuvre courts pour les nouvelles exigences %t', 'Délais de mise en œuvre courts pour les nouvelles exigences'], es: ['Plazos cortos de implementación para nuevos requisitos %t', 'Plazos cortos de implementación para nuevos requisitos'], nl: ['Korte implementatietijdlijnen voor nieuwe %t-vereisten', 'Korte implementatietijdlijnen voor nieuwe vereisten'], ar: ['جداول زمنية قصيرة للتنفيذ للمتطلبات الجديدة %t', 'جداول زمنية قصيرة للتنفيذ للمتطلبات الجديدة'], he: ['לוחות זמנים קצרים ליישום דרישות %t החדשות', 'לוחות זמנים קצרים ליישום דרישות חדשות'], ja: ['新しい%t要件への短い実施期間', '新しい要件への短い実施期間'], ko: ['새로운 %t 요건을 위한 짧은 이행 기간', '새로운 요건을 위한 짧은 이행 기간'], zh: ['新%t要求的短期实施时间表', '新要求的短期实施时间表'] }, }; /** Return a localised SWOT fallback string, substituting %t for the topic when present. */ @@ -1252,7 +1253,12 @@ function buildDeepInspectionSections( : `Multi-stakeholder analysis of ${docs.length} parliamentary documents`; const swotSection = generateStakeholderSwotSection({ stakeholders, lang, strategicContext }); - // ── Dashboard: document type distribution ───────────────────────────────── + // ── AI-analyzed multi-chart dashboard ───────────────────────────────────── + // Produces 3 chart types (radar, scatter, bar) with accessible data tables. + const dashboardAnalysis = analyzeDashboardData(docs, topic, lang); + + // Also build the classic document-type distribution bar chart as chart #4 + // so existing article consumers still see document counts. const typeCounts: Record = {}; docs.forEach(d => { const t = d.doktyp || d.documentType || 'other'; @@ -1263,23 +1269,31 @@ function buildDeepInspectionSections( const chartLabels = rawTypeKeys.map(t => docTypeLabel(t, lang, typeCounts[t])); const chartValues = rawTypeKeys.map(t => typeCounts[t]); + const docTypeChart = { + id: 'deep-inspection-doc-types', + type: 'bar' as const, + title: deepLabel('documentsByType', lang), + labels: chartLabels, + datasets: [{ + label: deepLabel('documents', lang), + data: chartValues, + backgroundColor: rawTypeKeys.map((_, i) => DEEP_CHART_PALETTE[i % DEEP_CHART_PALETTE.length]), + }], + }; + const docTypeTable = { + caption: deepLabel('documentsByType', lang), + headers: [deepLabel('documentTypes', lang), deepLabel('documents', lang)], + rows: rawTypeKeys.map((t, i) => [docTypeLabel(t, lang, chartValues[i]), String(chartValues[i])]), + }; + const dashboardSection = generateDashboardSection({ data: { title: topic ? `${deepLabel('documentIntelligence', lang)} — ${topic}` : deepLabel('documentIntelligence', lang), - summary: `${docs.length} ${deepLabel(docs.length === 1 ? 'documentAnalysed' : 'documentsAnalysed', lang)}`, - charts: [{ - id: 'deep-inspection-doc-types', - type: 'bar', - title: deepLabel('documentsByType', lang), - labels: chartLabels, - datasets: [{ - label: deepLabel('documents', lang), - data: chartValues, - backgroundColor: rawTypeKeys.map((_, i) => DEEP_CHART_PALETTE[i % DEEP_CHART_PALETTE.length]), - }], - }], + summary: dashboardAnalysis.summary, + charts: [...dashboardAnalysis.charts, docTypeChart], + tables: [...dashboardAnalysis.tables, docTypeTable], }, lang, }); diff --git a/scripts/types/content.ts b/scripts/types/content.ts index c41fa8fd8eb..9f707a2b1f2 100644 --- a/scripts/types/content.ts +++ b/scripts/types/content.ts @@ -97,8 +97,10 @@ export interface ContentLabelSet { propsBreakdown: (n: number) => string; motionsBreakdown: (n: number) => string; // Interpellations-specific labels + /** Empty-state message for interpellations articles */ noInterpellations: string; interpellationDefault: string; + /** Analytical lede for interpellations articles */ interpellationsBreakdown: (n: number) => string; debateDynamics: string; /** Label for the target minister (recipient), e.g. "Directed to" */ @@ -117,6 +119,8 @@ export interface ContentLabelSet { committeeActivityTakeaway: (committees: string, extra: number) => string; committeeMomentumTakeaway: (n: number) => string; oppositionStrategyContext: (n: number) => string; + /** Interpellation-specific strategy context (parliamentary oversight framing, not motions) */ + interpellationStrategyContext: (n: number) => string; policyImplicationsContext: (propCount: number, domainCount: number) => string; genericOverview: (n: number) => string; partyMotionsFiled: (party: string, n: number) => string; diff --git a/tests/ai-analysis-dashboard.test.ts b/tests/ai-analysis-dashboard.test.ts new file mode 100644 index 00000000000..f229c3a3c95 --- /dev/null +++ b/tests/ai-analysis-dashboard.test.ts @@ -0,0 +1,554 @@ +/** + * Tests for analyzeDashboardData — AI-powered multi-chart dashboard analyzer. + * + * Validates: + * - Returns exactly 3 AI-analyzed charts (radar, scatter, bar) + * - Each chart has a corresponding accessible data table + * - All 14 languages supported without errors + * - Correct chart types: radar, scatter, bar + * - Accessible tables have correct structure (headers + rows) + * - Data quality classification (high / medium / low) + * - Scores are clamped to [1, 10] + * - XSS: chart titles and table cells use safe content (not user-controlled) + * - Empty document list produces valid placeholder charts + * - Charts carry AIChartConfig metadata (analysisNote, dataSource, confidence) + */ + +import { describe, it, expect } from 'vitest'; +import { + analyzeDashboardData, + type DashboardAnalysisResult, + type AIChartConfig, +} from '../scripts/ai-analysis/dashboard-analyzer.js'; +import type { RawDocument } from '../scripts/data-transformers/types.js'; + +// --------------------------------------------------------------------------- +// Test fixtures +// --------------------------------------------------------------------------- + +function makeDoc(overrides: Partial = {}): RawDocument { + return { + doktyp: 'prop', + titel: 'Test proposition om budget', + summary: 'A proposal about fiscal policy and budget implementation.', + ...overrides, + }; +} + +function makeDocSet(): RawDocument[] { + return [ + makeDoc({ doktyp: 'prop', titel: 'Budget proposition 2026' }), + makeDoc({ doktyp: 'mot', titel: 'Motion om skattelättnad' }), + makeDoc({ doktyp: 'bet', titel: 'Betänkande om finansutskott' }), + makeDoc({ doktyp: 'sfs', titel: 'Lag om förbättrad genomförandeprocess', dokumentnamn: 'SFS 2026:1' }), + makeDoc({ doktyp: 'fpm', titel: 'EU-fakta om direktiv' }), + makeDoc({ doktyp: 'pressm', titel: 'Pressmeddelande: ny lag antagen' }), + ]; +} + +// --------------------------------------------------------------------------- +// Basic structure +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — basic structure', () => { + it('returns a DashboardAnalysisResult with charts, tables, summary, and dataQuality', () => { + const result: DashboardAnalysisResult = analyzeDashboardData(makeDocSet(), 'budget', 'en'); + expect(result).toHaveProperty('charts'); + expect(result).toHaveProperty('tables'); + expect(result).toHaveProperty('summary'); + expect(result).toHaveProperty('dataQuality'); + }); + + it('returns exactly 3 AI charts', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + expect(result.charts).toHaveLength(3); + }); + + it('returns exactly 3 accessible tables (one per AI chart)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + expect(result.tables).toHaveLength(3); + }); + + it('summary contains document count', () => { + const docs = makeDocSet(); + const result = analyzeDashboardData(docs, null, 'en'); + expect(result.summary).toContain(String(docs.length)); + }); + + it('summary mentions "analysis"', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + expect(result.summary.toLowerCase()).toMatch(/anal/); + }); +}); + +// --------------------------------------------------------------------------- +// Chart types +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — chart types', () => { + it('first chart is a radar chart (Policy Risk Assessment)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + expect(result.charts[0].type).toBe('radar'); + }); + + it('second chart is a scatter chart (Stakeholder Alignment)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + expect(result.charts[1].type).toBe('scatter'); + }); + + it('third chart is a bar chart (Legislative Pipeline)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + expect(result.charts[2].type).toBe('bar'); + }); + + it('all 3 chart types are distinct', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const types = result.charts.map(c => c.type); + const unique = new Set(types); + expect(unique.size).toBe(3); + }); +}); + +// --------------------------------------------------------------------------- +// Chart IDs +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — chart IDs', () => { + it('radar chart id is ai-risk-radar', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + expect(result.charts[0].id).toBe('ai-risk-radar'); + }); + + it('scatter chart id is ai-stakeholder-alignment', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + expect(result.charts[1].id).toBe('ai-stakeholder-alignment'); + }); + + it('bar chart id is ai-legislative-pipeline', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + expect(result.charts[2].id).toBe('ai-legislative-pipeline'); + }); + + it('all chart IDs are unique', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const ids = result.charts.map(c => c.id); + const unique = new Set(ids); + expect(unique.size).toBe(ids.length); + }); +}); + +// --------------------------------------------------------------------------- +// AI metadata on charts +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — AIChartConfig metadata', () => { + it('each chart has a non-empty analysisNote', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + for (const chart of result.charts as AIChartConfig[]) { + expect(typeof chart.analysisNote).toBe('string'); + expect(chart.analysisNote.length).toBeGreaterThan(0); + } + }); + + it('each chart has a non-empty dataSource', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + for (const chart of result.charts as AIChartConfig[]) { + expect(typeof chart.dataSource).toBe('string'); + expect(chart.dataSource.length).toBeGreaterThan(0); + } + }); + + it('each chart has a confidence value between 0 and 1', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + for (const chart of result.charts as AIChartConfig[]) { + expect(chart.confidence).toBeGreaterThanOrEqual(0); + expect(chart.confidence).toBeLessThanOrEqual(1); + } + }); +}); + +// --------------------------------------------------------------------------- +// Radar chart (risk assessment) +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — radar chart scores', () => { + it('radar chart has exactly 5 labels (risk dimensions)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const radar = result.charts[0]; + expect(radar.labels).toHaveLength(5); + }); + + it('radar chart has one dataset', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const radar = result.charts[0]; + expect(radar.datasets).toHaveLength(1); + }); + + it('radar dataset has 5 numeric values', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const radar = result.charts[0]; + const data = radar.datasets[0].data as number[]; + expect(data).toHaveLength(5); + for (const v of data) { + expect(typeof v).toBe('number'); + } + }); + + it('radar scores are within [1, 10]', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const radar = result.charts[0]; + const data = radar.datasets[0].data as number[]; + for (const v of data) { + expect(v).toBeGreaterThanOrEqual(1); + expect(v).toBeLessThanOrEqual(10); + } + }); +}); + +// --------------------------------------------------------------------------- +// Scatter chart (stakeholder alignment) +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — scatter chart', () => { + it('scatter chart has exactly 3 datasets (gov, opp, civil)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const scatter = result.charts[1]; + expect(scatter.datasets).toHaveLength(3); + }); + + it('each scatter dataset has exactly one {x, y} point', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const scatter = result.charts[1]; + for (const ds of scatter.datasets) { + expect(ds.data).toHaveLength(1); + const pt = ds.data[0] as { x: number; y: number }; + expect(pt).toHaveProperty('x'); + expect(pt).toHaveProperty('y'); + } + }); + + it('scatter points are within [1, 10]', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const scatter = result.charts[1]; + for (const ds of scatter.datasets) { + const pt = ds.data[0] as { x: number; y: number }; + expect(pt.x).toBeGreaterThanOrEqual(1); + expect(pt.x).toBeLessThanOrEqual(10); + expect(pt.y).toBeGreaterThanOrEqual(1); + expect(pt.y).toBeLessThanOrEqual(10); + } + }); +}); + +// --------------------------------------------------------------------------- +// Bar chart (legislative pipeline) +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — bar chart (legislative pipeline)', () => { + it('bar chart has labels and matching dataset values', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const bar = result.charts[2]; + expect(bar.labels).toBeDefined(); + expect(bar.datasets[0].data).toHaveLength(bar.labels!.length); + }); + + it('bar chart has at least 1 label', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const bar = result.charts[2]; + expect((bar.labels ?? []).length).toBeGreaterThanOrEqual(1); + }); + + it('bar chart values are non-negative integers', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const bar = result.charts[2]; + for (const v of bar.datasets[0].data as number[]) { + expect(v).toBeGreaterThanOrEqual(0); + expect(Number.isInteger(v)).toBe(true); + } + }); +}); + +// --------------------------------------------------------------------------- +// Accessible data tables +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — accessible tables', () => { + it('radar table has 2 headers (dimension + score)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const radarTable = result.tables[0]; + expect(radarTable.headers).toHaveLength(2); + }); + + it('radar table has 5 rows (one per risk dimension)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const radarTable = result.tables[0]; + expect(radarTable.rows).toHaveLength(5); + }); + + it('radar table has a caption', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + expect(result.tables[0].caption).toBeTruthy(); + }); + + it('scatter table has 3 headers (stakeholder + alignment + influence)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const scatterTable = result.tables[1]; + expect(scatterTable.headers).toHaveLength(3); + }); + + it('scatter table has 3 rows (gov, opp, civil)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const scatterTable = result.tables[1]; + expect(scatterTable.rows).toHaveLength(3); + }); + + it('pipeline table has 2 headers (stage + count)', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const pipelineTable = result.tables[2]; + expect(pipelineTable.headers).toHaveLength(2); + }); + + it('pipeline table rows count matches pipeline chart labels', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + const bar = result.charts[2]; + const pipelineTable = result.tables[2]; + expect(pipelineTable.rows).toHaveLength((bar.labels ?? []).length); + }); + + it('each table row has same number of cells as headers', () => { + const result = analyzeDashboardData(makeDocSet(), null, 'en'); + for (const table of result.tables) { + for (const row of table.rows) { + expect(row).toHaveLength(table.headers.length); + } + } + }); +}); + +// --------------------------------------------------------------------------- +// Data quality +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — data quality', () => { + it('returns low quality for empty document set', () => { + const result = analyzeDashboardData([], null, 'en'); + expect(result.dataQuality).toBe('low'); + }); + + it('returns medium quality for documents with only metadata (no fullText)', () => { + const docs = [makeDoc(), makeDoc(), makeDoc()]; + const result = analyzeDashboardData(docs, null, 'en'); + expect(result.dataQuality).toBe('medium'); + }); + + it('returns high quality when several documents have fullText', () => { + const docs = Array.from({ length: 5 }, (_, i) => + makeDoc({ fullText: `Full text content for document ${i} that is longer than 100 characters and contains meaningful content about policy.` }), + ); + const result = analyzeDashboardData(docs, null, 'en'); + expect(result.dataQuality).toBe('high'); + }); + + it('returns high quality for 1 doc with full text + 5 other docs', () => { + const docs = [ + makeDoc({ fullText: 'Long full text content that is more than 100 characters for quality assessment purposes in this test.' }), + ...Array.from({ length: 5 }, () => makeDoc()), + ]; + const result = analyzeDashboardData(docs, null, 'en'); + expect(result.dataQuality).toBe('high'); + }); +}); + +// --------------------------------------------------------------------------- +// Empty document set +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — empty document set', () => { + it('returns 3 charts even for empty document set', () => { + const result = analyzeDashboardData([], null, 'en'); + expect(result.charts).toHaveLength(3); + }); + + it('returns 3 tables even for empty document set', () => { + const result = analyzeDashboardData([], null, 'en'); + expect(result.tables).toHaveLength(3); + }); + + it('summary explains no documents available', () => { + const result = analyzeDashboardData([], null, 'en'); + expect(result.summary.toLowerCase()).toContain('no documents'); + }); +}); + +// --------------------------------------------------------------------------- +// Language support +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — 14 language support', () => { + const LANGS = ['en', 'sv', 'da', 'no', 'fi', 'de', 'fr', 'es', 'nl', 'ar', 'he', 'ja', 'ko', 'zh']; + + it.each(LANGS)('lang=%s: returns 3 charts without error', (lang) => { + const result = analyzeDashboardData(makeDocSet(), 'fiscal policy', lang); + expect(result.charts).toHaveLength(3); + }); + + it.each(LANGS)('lang=%s: radar chart has non-empty labels', (lang) => { + const result = analyzeDashboardData(makeDocSet(), null, lang); + const radar = result.charts[0]; + expect((radar.labels ?? []).length).toBeGreaterThan(0); + for (const label of radar.labels ?? []) { + expect(label.length).toBeGreaterThan(0); + } + }); + + it.each(LANGS)('lang=%s: scatter chart dataset labels are non-empty', (lang) => { + const result = analyzeDashboardData(makeDocSet(), null, lang); + const scatter = result.charts[1]; + for (const ds of scatter.datasets) { + expect(ds.label.length).toBeGreaterThan(0); + } + }); + + it.each(LANGS)('lang=%s: tables have non-empty captions', (lang) => { + const result = analyzeDashboardData(makeDocSet(), null, lang); + for (const table of result.tables) { + expect((table.caption ?? '').length).toBeGreaterThan(0); + } + }); +}); + +// --------------------------------------------------------------------------- +// Single-document edge case +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — single document', () => { + it('handles single proposition without error', () => { + const result = analyzeDashboardData([makeDoc({ doktyp: 'prop' })], 'test', 'en'); + expect(result.charts).toHaveLength(3); + expect(result.tables).toHaveLength(3); + expect(result.dataQuality).toBeOneOf(['low', 'medium', 'high']); + }); + + it('handles single motion without error', () => { + const result = analyzeDashboardData([makeDoc({ doktyp: 'mot' })], null, 'sv'); + expect(result.charts).toHaveLength(3); + }); +}); + +// --------------------------------------------------------------------------- +// Keyword-driven scoring +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — keyword-driven risk scores', () => { + it('budget keywords elevate budget-pressure score', () => { + const richDocs = Array.from({ length: 5 }, () => + makeDoc({ + doktyp: 'prop', + titel: 'Budget finansiering skatt anslag kostnad', + summary: 'Major budgetary cost and spending increase requires funding allocation.', + }), + ); + const lean = Array.from({ length: 5 }, () => makeDoc({ doktyp: 'prop', titel: 'Allmän proposition' })); + + const rich = analyzeDashboardData(richDocs, null, 'en'); + const leanResult = analyzeDashboardData(lean, null, 'en'); + + const richBudget = (rich.charts[0].datasets[0].data as number[])[2]; // index 2 = budget pressure + const leanBudget = (leanResult.charts[0].datasets[0].data as number[])[2]; + // Rich budget keywords should produce a higher or equal budget pressure score + expect(richBudget).toBeGreaterThanOrEqual(leanBudget); + }); + + it('high motion count reduces government alignment score', () => { + const motionHeavy = Array.from({ length: 6 }, () => makeDoc({ doktyp: 'mot' })); + const propHeavy = Array.from({ length: 6 }, () => makeDoc({ doktyp: 'prop' })); + + const motResult = analyzeDashboardData(motionHeavy, null, 'en'); + const propResult = analyzeDashboardData(propHeavy, null, 'en'); + + // Government alignment (x for gov) should be lower when mostly motions + const govAlignMotion = ((motResult.charts[1].datasets[0].data[0]) as { x: number }).x; + const govAlignProp = ((propResult.charts[1].datasets[0].data[0]) as { x: number }).x; + expect(govAlignProp).toBeGreaterThan(govAlignMotion); + }); +}); + +// --------------------------------------------------------------------------- +// fullContent fallback (codebase convention: fullText || fullContent) +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — fullContent fallback', () => { + it('assessDataQuality returns high when documents have fullContent (not fullText)', () => { + const docs = Array.from({ length: 5 }, (_, i) => + makeDoc({ fullContent: `

      Full HTML content for document ${i} that is longer than 100 characters and contains meaningful content about policy.

      ` }), + ); + const result = analyzeDashboardData(docs, null, 'en'); + expect(result.dataQuality).toBe('high'); + }); + + it('radar confidence is higher when documents have fullContent', () => { + const withContent = Array.from({ length: 3 }, () => + makeDoc({ fullContent: '

      Full content that is longer than 100 characters to trigger the enriched-content quality signal in the analyzer.

      ' }), + ); + const withoutContent = Array.from({ length: 3 }, () => makeDoc()); + + const richResult = analyzeDashboardData(withContent, null, 'en'); + const leanResult = analyzeDashboardData(withoutContent, null, 'en'); + + const richConfidence = (richResult.charts[0] as AIChartConfig).confidence; + const leanConfidence = (leanResult.charts[0] as AIChartConfig).confidence; + expect(richConfidence).toBeGreaterThan(leanConfidence); + }); + + it('keywords in fullContent are counted for risk scoring', () => { + const docsWithContent = Array.from({ length: 5 }, () => + makeDoc({ + doktyp: 'prop', + titel: 'Allmän proposition', + fullContent: '

      Budget kostnad finansiering skatt anslag spending increase

      ', + }), + ); + const docsWithoutContent = Array.from({ length: 5 }, () => + makeDoc({ doktyp: 'prop', titel: 'Allmän proposition' }), + ); + + const rich = analyzeDashboardData(docsWithContent, null, 'en'); + const lean = analyzeDashboardData(docsWithoutContent, null, 'en'); + + const richBudget = (rich.charts[0].datasets[0].data as number[])[2]; // budget pressure + const leanBudget = (lean.charts[0].datasets[0].data as number[])[2]; + expect(richBudget).toBeGreaterThanOrEqual(leanBudget); + }); +}); + +// --------------------------------------------------------------------------- +// skr (government communication) counted as government document +// --------------------------------------------------------------------------- + +describe('analyzeDashboardData — skr document handling', () => { + it('skr documents boost government alignment like propositions', () => { + const skrDocs = Array.from({ length: 6 }, () => makeDoc({ doktyp: 'skr' })); + const propDocs = Array.from({ length: 6 }, () => makeDoc({ doktyp: 'prop' })); + + const skrResult = analyzeDashboardData(skrDocs, null, 'en'); + const propResult = analyzeDashboardData(propDocs, null, 'en'); + + const govAlignSkr = ((skrResult.charts[1].datasets[0].data[0]) as { x: number }).x; + const govAlignProp = ((propResult.charts[1].datasets[0].data[0]) as { x: number }).x; + // skr and prop should both produce high government alignment + expect(govAlignSkr).toEqual(govAlignProp); + }); + + it('skr documents do not dilute government alignment to base level', () => { + const motDocs = Array.from({ length: 6 }, () => makeDoc({ doktyp: 'mot' })); + const skrDocs = Array.from({ length: 6 }, () => makeDoc({ doktyp: 'skr' })); + + const motResult = analyzeDashboardData(motDocs, null, 'en'); + const skrResult = analyzeDashboardData(skrDocs, null, 'en'); + + const govAlignMot = ((motResult.charts[1].datasets[0].data[0]) as { x: number }).x; + const govAlignSkr = ((skrResult.charts[1].datasets[0].data[0]) as { x: number }).x; + // skr-heavy should have higher gov alignment than motion-heavy + expect(govAlignSkr).toBeGreaterThan(govAlignMot); + }); +}); diff --git a/tests/data-transformers.test.ts b/tests/data-transformers.test.ts index 0a1cc8f7460..21c393bd431 100644 --- a/tests/data-transformers.test.ts +++ b/tests/data-transformers.test.ts @@ -83,6 +83,26 @@ interface MockArticlePayload { }>; votes?: unknown[]; speeches?: unknown[]; + questions?: Array<{ + titel?: string; + parti?: string; + dok_id?: string; + }>; + interpellations?: Array<{ + titel?: string; + title?: string; + url?: string; + parti?: string; + dokumentnamn?: string; + dok_id?: string; + intressent_namn?: string; + author?: string; + notis?: string; + summary?: string; + undertitel?: string; + datum?: string; + mottagare?: string; + }>; } describe('Data Transformers', () => { @@ -1531,7 +1551,7 @@ describe('Data Transformers', () => { it('should use ip-specific analysis for interpellations — not default/proposal text', () => { const content = generateArticleContent({ - motions: [{ titel: 'Försvarspolitik', url: 'https://example.com/1', dok_id: 'IP1' }] + interpellations: [{ titel: 'Försvarspolitik', url: 'https://example.com/1', dok_id: 'IP1' }] } as MockArticlePayload, 'interpellations', 'en') as string; // ip-specific text references minister accountability / NATO scrutiny @@ -1542,7 +1562,7 @@ describe('Data Transformers', () => { it('should use ip-specific Swedish analysis for interpellations in sv', () => { const content = generateArticleContent({ - motions: [{ titel: 'Klimatpolitik', url: 'https://example.com/1', dok_id: 'IP2' }] + interpellations: [{ titel: 'Klimatpolitik', url: 'https://example.com/1', dok_id: 'IP2' }] } as MockArticlePayload, 'interpellations', 'sv') as string; // ip-specific Swedish text references minister accountability @@ -1551,7 +1571,7 @@ describe('Data Transformers', () => { it('should use interpellation generic fallback when no domain detected', () => { const content = generateArticleContent({ - motions: [{ titel: 'Allmän fråga', url: 'https://example.com/1', dok_id: 'IP3' }] + interpellations: [{ titel: 'Allmän fråga', url: 'https://example.com/1', dok_id: 'IP3' }] } as MockArticlePayload, 'interpellations', 'en') as string; // Generic interpellation text — references minister obligation, not committee review @@ -2652,4 +2672,80 @@ describe('content generators include publication dates', () => { } as MockArticlePayload, 'committee-reports', 'en') as string; expect(content).not.toContain('class="doc-date"'); }); + + describe('generateInterpellationsContent', () => { + it('should render interpellations heading and empty state in EN', () => { + const content = generateArticleContent({ + interpellations: [], + } as MockArticlePayload, 'interpellations', 'en') as string; + expect(content).toContain('Interpellation Debates'); + expect(content).toContain('No interpellations available'); + }); + + it('should render interpellations heading and empty state in SV', () => { + const content = generateArticleContent({ + interpellations: [], + } as MockArticlePayload, 'interpellations', 'sv') as string; + expect(content).toContain('Interpellationsdebatter'); + expect(content).toContain('Inga interpellationer'); + }); + + it('should render interpellation entries with interpellation-specific CSS class', () => { + const content = generateArticleContent({ + interpellations: [ + { titel: 'Interpellation om vården', parti: 'V', dok_id: 'I1', url: 'https://riksdagen.se/ip1' }, + ], + } as MockArticlePayload, 'interpellations', 'en') as string; + expect(content).toContain('interpellation-entry'); + expect(content).not.toContain('motion-entry'); + // Without summary/notis, should fall back to interpellation default, not motion default + expect(content).toContain('Parliamentary interpellation demanding government accountability'); + expect(content).not.toContain('Parliamentary motion by opposition member'); + }); + + it('should use "Read the full interpellation" link text, not motions', () => { + const content = generateArticleContent({ + interpellations: [ + { titel: 'Interpellation om skolan', parti: 'SD', dok_id: 'I2', url: 'https://riksdagen.se/ip2' }, + ], + } as MockArticlePayload, 'interpellations', 'en') as string; + expect(content).toContain('Read the full interpellation'); + expect(content).not.toContain('Read the full motion'); + }); + + it('should use partyInterpellationsFiled label in party breakdown', () => { + const content = generateArticleContent({ + interpellations: [ + { titel: 'IP om vården', parti: 'V', dok_id: 'I1', url: 'https://riksdagen.se/ip1' }, + { titel: 'IP om skolan', parti: 'SD', dok_id: 'I2', url: 'https://riksdagen.se/ip2' }, + ], + } as MockArticlePayload, 'interpellations', 'en') as string; + expect(content).toContain('interpellation'); + // Should use interpellations label, not motions + expect(content).not.toMatch(/\d+ motion[s]? filed/); + }); + + it('should render analytical lede with interpellations breakdown', () => { + const content = generateArticleContent({ + interpellations: [ + { titel: 'IP om arbetsmarknad', parti: 'V', dok_id: 'I1', url: 'https://riksdagen.se/ip1' }, + { titel: 'IP om migration', parti: 'SD', dok_id: 'I2', url: 'https://riksdagen.se/ip2' }, + { titel: 'IP om försvar', parti: 'M', dok_id: 'I3', url: 'https://riksdagen.se/ip3' }, + ], + } as MockArticlePayload, 'interpellations', 'en') as string; + expect(content).toContain('article-lede'); + expect(content).toContain('3 interpellations'); + }); + + it('should render SV interpellation entries with SV-specific labels', () => { + const content = generateArticleContent({ + interpellations: [ + { titel: 'Interpellation om vården', parti: 'V', dok_id: 'I1', url: 'https://riksdagen.se/ip1' }, + ], + } as MockArticlePayload, 'interpellations', 'sv') as string; + expect(content).toContain('Läs hela interpellationen'); + expect(content).not.toContain('Läs hela motionen'); + expect(content).toContain('interpellation-entry'); + }); + }); }); diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts index 33556f3eaa6..40dd925b7b1 100644 --- a/tests/interpellations.test.ts +++ b/tests/interpellations.test.ts @@ -109,7 +109,7 @@ describe('Interpellations Content Generator', () => { describe('Output content', () => { it('should generate interpellations content without motion phrasing', () => { const data = { - motions: [ + interpellations: [ { titel: 'Om sjukvårdsförsörjning till statsrådet', parti: 'V', @@ -146,7 +146,7 @@ describe('Interpellations Content Generator', () => { it('should not contain proposition-specific phrasing in accountability section', () => { const data = { - motions: [ + interpellations: [ { titel: 'Om försvaret till statsrådet', parti: 'M', @@ -165,7 +165,7 @@ describe('Interpellations Content Generator', () => { it('should use interpellations-specific party count label', () => { const data = { - motions: [ + interpellations: [ { titel: 'IP 1', parti: 'V', url: '#', dokumentnamn: 'IP1' }, { titel: 'IP 2', parti: 'S', url: '#', dokumentnamn: 'IP2' }, ] as MockInterpellation[], @@ -181,7 +181,7 @@ describe('Interpellations Content Generator', () => { describe('Minister target extraction', () => { it('should extract target from mottagare field', () => { const data = { - motions: [ + interpellations: [ { titel: 'Fråga om humanitär hjälp till Gaza', parti: 'V', @@ -198,7 +198,7 @@ describe('Interpellations Content Generator', () => { it('should NOT extract "Gaza" as minister from title "... till Gaza"', () => { const data = { - motions: [ + interpellations: [ { titel: 'Humanitär hjälp till Gaza – en fråga om solidaritet', parti: 'V', @@ -217,7 +217,7 @@ describe('Interpellations Content Generator', () => { it('should extract target from "till X statsråd" pattern', () => { const data = { - motions: [ + interpellations: [ { titel: 'Interpellation till justitie- och inrikesminister', parti: 'SD', @@ -234,7 +234,7 @@ describe('Interpellations Content Generator', () => { it('should extract full compound minister title like "utrikesminister"', () => { const data = { - motions: [ + interpellations: [ { titel: 'Fråga till utrikesministern om bistånd', parti: 'V', @@ -251,7 +251,7 @@ describe('Interpellations Content Generator', () => { it('should extract full compound "finansministern" target', () => { const data = { - motions: [ + interpellations: [ { titel: 'Interpellation till finansministern', parti: 'M', @@ -267,7 +267,7 @@ describe('Interpellations Content Generator', () => { it('should capture possessive "ministerns" suffix (not drop trailing s)', () => { const data = { - motions: [ + interpellations: [ { titel: 'Interpellation till finansministerns ansvar', parti: 'S', @@ -283,7 +283,7 @@ describe('Interpellations Content Generator', () => { it('should capture "statsrådets" inflection', () => { const data = { - motions: [ + interpellations: [ { titel: 'Interpellation till statsrådets politik', parti: 'V', @@ -299,7 +299,7 @@ describe('Interpellations Content Generator', () => { it('should extract compound minister title with Swedish diacritics like "miljöministern"', () => { const data = { - motions: [ + interpellations: [ { titel: 'Interpellation till miljöministern om kemikaliepolitiken', parti: 'MP', @@ -316,7 +316,7 @@ describe('Interpellations Content Generator', () => { it('should extract compound minister title with ö like "försvarsministern"', () => { const data = { - motions: [ + interpellations: [ { titel: 'Interpellation till försvarsministern om säkerhetspolitik', parti: 'S', @@ -334,7 +334,7 @@ describe('Interpellations Content Generator', () => { describe('Party rendering', () => { it('should NOT display "OTHER" when party is missing', () => { const data = { - motions: [ + interpellations: [ { titel: 'Test interpellation', url: '#', @@ -351,7 +351,7 @@ describe('Interpellations Content Generator', () => { it('should NOT display "OTHER" when party is "Unknown"', () => { const data = { - motions: [ + interpellations: [ { titel: 'Test interpellation', url: '#', @@ -369,7 +369,7 @@ describe('Interpellations Content Generator', () => { it('should display actual party when present', () => { const data = { - motions: [ + interpellations: [ { titel: 'Test interpellation', url: '#', @@ -387,7 +387,7 @@ describe('Interpellations Content Generator', () => { describe('Empty state', () => { it('should show no-interpellations message when empty', () => { - const data = { motions: [] as MockInterpellation[] }; + const data = { interpellations: [] as MockInterpellation[] }; const result = generateArticleContent(data, 'interpellations', 'en') as string; expect(result).toContain('No interpellations'); }); @@ -396,7 +396,7 @@ describe('Interpellations Content Generator', () => { describe('Author sentinel filtering', () => { it('should NOT display "Unknown" as author name', () => { const data = { - motions: [ + interpellations: [ { titel: 'Test interpellation', url: '#', @@ -414,7 +414,7 @@ describe('Interpellations Content Generator', () => { it('should NOT display "unknown" (lowercase) as author name', () => { const data = { - motions: [ + interpellations: [ { titel: 'Test interpellation', url: '#', @@ -430,7 +430,7 @@ describe('Interpellations Content Generator', () => { it('should display real author names', () => { const data = { - motions: [ + interpellations: [ { titel: 'Test interpellation', url: '#', @@ -447,12 +447,11 @@ describe('Interpellations Content Generator', () => { }); describe('Data fallback', () => { - it('should use data.documents when data.motions is an empty array', () => { + it('should use data.interpellations when provided', () => { const data = { - motions: [] as MockInterpellation[], - documents: [ + interpellations: [ { - titel: 'Interpellation from documents fallback', + titel: 'Interpellation from interpellations field', url: '#', dokumentnamn: 'IP-DOC1', parti: 'M', @@ -461,33 +460,17 @@ describe('Interpellations Content Generator', () => { }; const result = generateArticleContent(data, 'interpellations', 'en') as string; - // Should pick up the document from data.documents, not show empty state - expect(result).toContain('Interpellation from documents fallback'); + expect(result).toContain('Interpellation from interpellations field'); expect(result).not.toContain('No interpellations'); }); - it('should prefer data.motions when it has items', () => { + it('should show empty state when interpellations is empty', () => { const data = { - motions: [ - { - titel: 'Primary interpellation', - url: '#', - dokumentnamn: 'IP-MOT1', - parti: 'S', - }, - ] as MockInterpellation[], - documents: [ - { - titel: 'Secondary interpellation', - url: '#', - dokumentnamn: 'IP-DOC1', - parti: 'V', - }, - ] as MockInterpellation[], + interpellations: [] as MockInterpellation[], }; const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('Primary interpellation'); + expect(result).toContain('No interpellations'); }); }); }); From dc370a403bd445420ea7b9dc0addd90bd83a01a2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 18:27:35 +0000 Subject: [PATCH 13/24] =?UTF-8?q?fix:=20resolve=20merge=20conflicts=20with?= =?UTF-8?q?=20main=20branch=20(6=20files)=20=E2=80=94=20unify=20interpella?= =?UTF-8?q?tions=20data=20contract,=20add=20ip=20fallback,=20restore=20all?= =?UTF-8?q?=2014-language=20labels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .github/workflows/news-evening-analysis.md | 41 ----- .../constants/content-labels-part1.ts | 86 +++++------ .../constants/content-labels-part2.ts | 88 +++++------ .../content-generators/interpellations.ts | 18 +++ scripts/data-transformers/policy-analysis.ts | 145 +++++++++--------- scripts/types/content.ts | 14 +- 6 files changed, 174 insertions(+), 218 deletions(-) diff --git a/.github/workflows/news-evening-analysis.md b/.github/workflows/news-evening-analysis.md index 44aeb85619c..444cea2b669 100644 --- a/.github/workflows/news-evening-analysis.md +++ b/.github/workflows/news-evening-analysis.md @@ -271,47 +271,6 @@ const speeches = search_anforanden({ rm: currentRm, limit: 100 }) .filter(a => new Date(a.datum) >= new Date(fromDate)); ``` -**Example 1: Committee Report Deep Dive** -```javascript -// 1. Fetch committee reports -const reports = get_betankanden({ rm: "", limit: 50 }); -// 2. Cross-reference with voting records -const votes = search_voteringar({ rm: "", bet: reports[0].beteckning }); -// 3. Filter results by date -const fresh = reports.filter(r => new Date(r.publicerad) >= new Date(fromDate)); -``` - -**Example 2: Government Activity Analysis** -```javascript -// 1. Fetch government propositions -const props = get_propositioner({ rm: "", limit: 20 }); -// 2. Filter propositions by date -const filtered = props.filter(p => new Date(p.publicerad) >= new Date(fromDate)); -// 3. Cross-reference with committee handling -const handling = filtered.map(p => search_dokument({ relaterat_id: p.dok_id, doktyp: "bet" })); -``` - -**Example 3: Party Behavior Analysis** -```javascript -const partyCode = "S"; // example party code -// 1. Fetch motions by party -const motions = get_motioner({ rm: currentRm, limit: 100 }); -// 2. Cross-reference with speeches -const speeches = search_anforanden({ rm: currentRm, parti: partyCode }); -// 3. Filter motions by date (inlämnad = submission date for motions) -const recentMotions = motions.filter(m => new Date(m.inlämnad) >= new Date(fromDate)); -``` - -### Date Calculation Helpers - -```javascript -const now = new Date(); -const oneDayMs = 86400000; // 24 * 60 * 60 * 1000 -const oneHourMs = 3600000; // 60 * 60 * 1000 -const fromDate = new Date(now.getTime() - lookbackHours * oneHourMs).toISOString().slice(0, 10); -const fiveDaysAgo = new Date(now.getTime() - 5 * oneDayMs).toISOString().slice(0, 10); -``` - ### Saturday vs Weekday Mode - **Saturday** (day_of_week=6): Produce a **Weekly Parliamentary Review** looking back 5 days (Monday–Friday). Use `coverage_depth: comprehensive`. Title: "The Week in Swedish Politics: {key theme}". Article slug: `weekly-review`. diff --git a/scripts/data-transformers/constants/content-labels-part1.ts b/scripts/data-transformers/constants/content-labels-part1.ts index ac23b670c8b..80552d388e8 100644 --- a/scripts/data-transformers/constants/content-labels-part1.ts +++ b/scripts/data-transformers/constants/content-labels-part1.ts @@ -53,6 +53,8 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsOverview: (n: number): string => `Opposition MPs have filed ${n} new motions, pressing the government on issues ranging across policy domains.`, motionSignificance: 'This motion addresses', readFullMotion: 'Read the full motion', + interpellationDefault: 'Parliamentary interpellation demanding government accountability.', + readFullInterpellation: 'Read the full interpellation', policyContext: 'Policy context', filedBy: 'Filed by', published: 'Published', @@ -67,20 +69,15 @@ export const CONTENT_LABELS_PART1: Partial> = { whyItMatters: 'Why It Matters', committeeBreakdown: (n: number, c: number): string => `This batch of ${n} committee reports spans ${c} different committees, reflecting the breadth of legislative activity in the current parliamentary session. The thematic spread reveals the Riksdag's multi-front policy engagement and the government's legislative priorities.`, propsBreakdown: (n: number): string => `The government has submitted ${n} new propositions, signalling its policy priorities and the pace of its legislative agenda. Each proposition must navigate committee review and chamber debate, providing insight into the coalition's strategic direction and its ability to build cross-party support.`, - motionsBreakdown: (n: number): string => `Opposition MPs have filed ${n} new motions, mapping the political fault lines in the current Riksdag. These motions reveal not just policy disagreements but the strategic positioning of parties as they prepare for the next electoral contest.` -, + motionsBreakdown: (n: number): string => `Opposition MPs have filed ${n} new motions, mapping the political fault lines in the current Riksdag. These motions reveal not just policy disagreements but the strategic positioning of parties as they prepare for the next electoral contest.`, noInterpellations: 'No interpellations available at this time.', - interpellationDefault: 'Parliamentary interpellation demanding government accountability.', - interpellationsBreakdown: (n: number): string => `Opposition MPs have filed ${n} interpellations demanding ministerial accountability. Interpellations are formal parliamentary questions that ministers must answer in chamber debate, making them a key tool of parliamentary scrutiny.`, + interpellationsBreakdown: (n: number): string => `Opposition MPs have submitted ${n} interpellations to government ministers, demanding accountability and formal responses on pressing policy issues. Interpellations are among the most powerful tools of parliamentary oversight, forcing ministers to defend government decisions before the chamber.`, debateDynamics: 'Debate Dynamics & Accountability', ministerAccountability: 'Directed to', accountabilityAnalysis: 'Accountability Analysis', - readFullInterpellation: 'Read the full interpellation', interpellationBy: 'Interpellation by', interpellationsScrutinyContext: (n: number): string => `Interpellations from ${n} different parties demonstrate broad parliamentary scrutiny of government ministers.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `These ${count} interpellations span ${domains} policy domain${domains > 1 ? 's' : ''}, reflecting the breadth of parliamentary accountability demands on the government.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellation${n > 1 ? 's' : ''} filed`, - committeeCountContext: (n: number): string => `${n} reports from this committee signal intensive legislative work within its portfolio.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Parliamentary committees have been active across ${committees} and ${extra} further policy domains.` : `Parliamentary committees have been active across ${committees}.`, committeeMomentumTakeaway: (n: number): string => `A total of ${n} reports demonstrates sustained legislative momentum and ongoing policy prioritisation.`, @@ -89,6 +86,7 @@ export const CONTENT_LABELS_PART1: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `These ${propCount} propositions touch on ${domainCount} policy domain${domainCount > 1 ? 's' : ''}, demonstrating the government's broad legislative ambition. Committee review and chamber debate will determine whether these proposals command sufficient support to become law.`, genericOverview: (n: number): string => `During this period, ${n} documents were processed in parliament, offering a snapshot of ongoing legislative work.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} motion${n > 1 ? 's' : ''} filed`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellation${n > 1 ? 's' : ''} filed`, otherCommittee: 'Other committees', otherDocuments: 'Other documents', policySignificanceTouches: (domains: string): string => `Touches on ${domains}.`, @@ -167,6 +165,8 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsOverview: (n: number): string => `Oppositionsriksdagsledamöter har lämnat in ${n} nya motioner som pressar regeringen inom flera politikområden.`, motionSignificance: 'Denna motion behandlar', readFullMotion: 'Läs hela motionen', + interpellationDefault: 'Riksdagsinterpellation som kräver regeringens ansvarsskyldighet.', + readFullInterpellation: 'Läs hela interpellationen', policyContext: 'Politisk kontext', filedBy: 'Inlämnad av', published: 'Publicerad', @@ -181,20 +181,15 @@ export const CONTENT_LABELS_PART1: Partial> = { whyItMatters: 'Varför det spelar roll', committeeBreakdown: (n: number, c: number): string => `Denna omgång med ${n} utskottsbetänkanden omfattar ${c} olika utskott, vilket speglar bredden i riksdagens lagstiftningsarbete under innevarande session.`, propsBreakdown: (n: number): string => `Regeringen har överlämnat ${n} nya propositioner, som signalerar dess politiska prioriteringar och takten i den lagstiftande agendan.`, - motionsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} nya motioner som kartlägger de politiska skiljelinjerna i nuvarande riksdag.` -, + motionsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} nya motioner som kartlägger de politiska skiljelinjerna i nuvarande riksdag.`, noInterpellations: 'Inga interpellationer tillgängliga för tillfället.', - interpellationDefault: 'Riksdagsinterpellation som kräver regeringens ansvarsskyldighet.', - interpellationsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} interpellationer som kräver ministeransvar. Interpellationer är formella parlamentariska frågor som ministrar måste besvara i kammaren, vilket gör dem till ett viktigt verktyg för parlamentarisk granskning.`, + interpellationsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} interpellationer till regeringsministrar och kräver ansvarsskyldighet och formella svar i viktiga politikfrågor. Interpellationer är ett av riksdagens kraftfullaste verktyg för parlamentarisk kontroll och tvingar ministrar att försvara regeringsbeslut inför kammaren.`, debateDynamics: 'Debattdynamik och ansvarsutkrävande', ministerAccountability: 'Riktat till', accountabilityAnalysis: 'Ansvarsutkrävande', - readFullInterpellation: 'Läs hela interpellationen', interpellationBy: 'Interpellation av', interpellationsScrutinyContext: (n: number): string => `Interpellationer från ${n} olika partier visar på bred parlamentarisk granskning av regeringens ministrar.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `Dessa ${count} interpellationer spänner över ${domains} politikområde${domains > 1 ? 'n' : ''}, vilket avspeglar bredden i riksdagens ansvarsutkrävande av regeringen.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellation${n > 1 ? 'er' : ''} inlämnade`, - committeeCountContext: (n: number): string => `${n} betänkanden från detta utskott signalerar intensivt lagstiftningsarbete inom dess ansvarsområde.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Riksdagens utskott har varit aktiva inom ${committees} och ${extra} ytterligare områden.` : `Riksdagens utskott har varit aktiva inom ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Totalt ${n} betänkanden visar lagstiftande momentum och pågående politisk prioritering.`, @@ -203,6 +198,7 @@ export const CONTENT_LABELS_PART1: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `Dessa ${propCount} propositioner berör ${domainCount} politikområde${domainCount > 1 ? 'n' : ''}, vilket visar regeringens breda lagstiftningsambition. Utskottsbehandling och kammardebatt avgör om förslagen vinner tillräckligt stöd för att bli lag.`, genericOverview: (n: number): string => `Under perioden har ${n} dokument behandlats i riksdagen, vilket ger en bild av det pågående lagstiftningsarbetet.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} motion${n > 1 ? 'er' : ''} inlämnade`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellation${n > 1 ? 'er' : ''} inlämnade`, otherCommittee: 'Övriga utskott', otherDocuments: 'Övriga dokument', policySignificanceTouches: (domains: string): string => `Berör ${domains}.`, @@ -281,6 +277,8 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsOverview: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} nye forslag.`, motionSignificance: 'Dette forslag omhandler', readFullMotion: 'Læs hele forslaget', + interpellationDefault: 'Parlamentarisk forespørgsel der kræver regeringens ansvarlighed.', + readFullInterpellation: 'Læs hele interpellationen', policyContext: 'Politisk kontekst', filedBy: 'Indgivet af', published: 'Offentliggjort', @@ -295,20 +293,15 @@ export const CONTENT_LABELS_PART1: Partial> = { whyItMatters: 'Hvorfor det er vigtigt', committeeBreakdown: (n: number, c: number): string => `Denne omgang med ${n} udvalgsbetænkninger dækker ${c} forskellige udvalg.`, propsBreakdown: (n: number): string => `Regeringen har fremsat ${n} nye lovforslag.`, - motionsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} nye forslag.` -, + motionsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} nye forslag.`, noInterpellations: 'Ingen interpellationer tilgængelige på nuværende tidspunkt.', - interpellationDefault: 'Parlamentarisk forespørgsel der kræver regeringens ansvarlighed.', - interpellationsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} interpellationer, der kræver ministeransvar.`, + interpellationsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} interpellationer til ministre og kræver ansvarlighed og formelle svar om presserende politiske spørgsmål.`, debateDynamics: 'Debatdynamik og ansvarlighed', ministerAccountability: 'Rettet til', accountabilityAnalysis: 'Ansvarlighed', - readFullInterpellation: 'Læs den fulde interpellation', interpellationBy: 'Interpellation af', interpellationsScrutinyContext: (n: number): string => `Interpellationer fra ${n} forskellige partier viser en bred parlamentarisk granskning af regeringens ministre.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `Disse ${count} interpellationer dækker ${domains} politikområde${domains > 1 ? 'r' : ''}, der afspejler bredden i parlamentariske ansvarlighedskrav til regeringen.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellation${n > 1 ? 'er' : ''} indgivet`, - committeeCountContext: (n: number): string => `${n} betænkninger fra dette udvalg signalerer intensivt lovgivningsarbejde.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Parlamentets udvalg har været aktive inden for ${committees} og ${extra} yderligere politikområder.` : `Parlamentets udvalg har været aktive inden for ${committees}.`, committeeMomentumTakeaway: (n: number): string => `I alt ${n} betænkninger viser vedvarende lovgivningsmæssig fremdrift.`, @@ -317,6 +310,7 @@ export const CONTENT_LABELS_PART1: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `Disse ${propCount} lovforslag berører ${domainCount} politikområde${domainCount > 1 ? 'r' : ''}, hvilket viser regeringens brede lovgivningsambition.`, genericOverview: (n: number): string => `I denne periode er ${n} dokumenter blevet behandlet i parlamentet.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} forslag indgivet`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellation${n > 1 ? 'er' : ''} indgivet`, otherCommittee: 'Andre udvalg', otherDocuments: 'Andre dokumenter', policySignificanceTouches: (domains: string): string => `Berører ${domains}.`, @@ -395,6 +389,8 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsOverview: (n: number): string => `Opposisjonsmedlemmer har fremmet ${n} nye forslag.`, motionSignificance: 'Dette forslaget omhandler', readFullMotion: 'Les hele forslaget', + interpellationDefault: 'Parlamentarisk interpellasjon som krever regjeringens ansvarlighet.', + readFullInterpellation: 'Les hele interpellasjonen', policyContext: 'Politisk kontekst', filedBy: 'Innsendt av', published: 'Publisert', @@ -409,20 +405,15 @@ export const CONTENT_LABELS_PART1: Partial> = { whyItMatters: 'Hvorfor det er viktig', committeeBreakdown: (n: number, c: number): string => `Denne runden med ${n} komitéinnstillinger dekker ${c} forskjellige komiteer.`, propsBreakdown: (n: number): string => `Regjeringen har fremmet ${n} nye proposisjoner.`, - motionsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} nye forslag.` -, + motionsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} nye forslag.`, noInterpellations: 'Ingen interpellasjoner tilgjengelige for øyeblikket.', - interpellationDefault: 'Parlamentarisk interpellasjon som krever regjeringens ansvarlighet.', - interpellationsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} interpellasjoner som krever statsrådets ansvar.`, + interpellationsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} interpellasjoner til statsråder og krever ansvarlighet og formelle svar om presserende politiske spørsmål.`, debateDynamics: 'Debattdynamikk og ansvarlighet', ministerAccountability: 'Rettet til', accountabilityAnalysis: 'Ansvarlighetsanalyse', - readFullInterpellation: 'Les hele interpellasjonen', interpellationBy: 'Interpellasjon av', interpellationsScrutinyContext: (n: number): string => `Interpellasjoner fra ${n} ulike partier viser bred parlamentarisk gransking av regjeringens statsråder.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `Disse ${count} interpellasjonene spenner over ${domains} politikkområde${domains > 1 ? 'r' : ''}, noe som gjenspeiler bredden i parlamentariske ansvarlighetskrav til regjeringen.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellasjon${n > 1 ? 'er' : ''} innsendt`, - committeeCountContext: (n: number): string => `${n} innstillinger fra denne komiteen signaliserer intensivt lovgivningsarbeid.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Stortingets komiteer har vært aktive innen ${committees} og ${extra} ytterligere politikkområder.` : `Stortingets komiteer har vært aktive innen ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Totalt ${n} innstillinger viser vedvarende lovgivende fremdrift.`, @@ -431,6 +422,7 @@ export const CONTENT_LABELS_PART1: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `Disse ${propCount} proposisjonene berører ${domainCount} politikkområde${domainCount > 1 ? 'r' : ''}, og viser regjeringens brede lovgivningsambisjon.`, genericOverview: (n: number): string => `I denne perioden er ${n} dokumenter blitt behandlet i parlamentet.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} forslag innsendt`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellasjon${n > 1 ? 'er' : ''} innsendt`, otherCommittee: 'Andre komiteer', otherDocuments: 'Andre dokumenter', policySignificanceTouches: (domains: string): string => `Berører ${domains}.`, @@ -509,6 +501,8 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsOverview: (n: number): string => `Opposition kansanedustajat ovat jättäneet ${n} uutta aloitetta.`, motionSignificance: 'Tämä aloite käsittelee', readFullMotion: 'Lue koko aloite', + interpellationDefault: 'Hallituksen vastuuvelvollisuutta vaativa eduskunnan välikysymys.', + readFullInterpellation: 'Lue koko välikysymys', policyContext: 'Poliittinen konteksti', filedBy: 'Jättänyt', published: 'Julkaistu', @@ -523,20 +517,15 @@ export const CONTENT_LABELS_PART1: Partial> = { whyItMatters: 'Miksi tämä on tärkeää', committeeBreakdown: (n: number, c: number): string => `Tämä erä ${n} valiokunnan mietintöä kattaa ${c} eri valiokuntaa.`, propsBreakdown: (n: number): string => `Hallitus on jättänyt ${n} uutta esitystä.`, - motionsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} uutta aloitetta.` -, - noInterpellations: 'Ei välikysymyksiä saatavilla tällä hetkellä.', - interpellationDefault: 'Hallituksen vastuuvelvollisuutta vaativa eduskunnan välikysymys.', - interpellationsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} välikysymystä ministereiden vastuullisuuden vaatimiseksi.`, + motionsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} uutta aloitetta.`, + noInterpellations: 'Ei interpellaatioita saatavilla tällä hetkellä.', + interpellationsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} interpellaatiota ministereille vaatien vastuullisuutta ja virallisia vastauksia kiireellisiin poliittisiin kysymyksiin.`, debateDynamics: 'Väittelyn dynamiikka ja vastuullisuus', ministerAccountability: 'Osoitettu', accountabilityAnalysis: 'Vastuullisuusanalyysi', - readFullInterpellation: 'Lue koko välikysymys', interpellationBy: 'Välikysymys esittäjältä', interpellationsScrutinyContext: (n: number): string => `Välikysymykset ${n} eri puolueelta osoittavat laajaa parlamentaarista hallituksen ministereiden valvontaa.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `Nämä ${count} välikysymystä kattavat ${domains} politiikka-aluetta, mikä heijastaa eduskunnan vastuullisuusvaatimusten laajuutta.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} välikysymys${n > 1 ? 'tä' : ''} jätetty`, - committeeCountContext: (n: number): string => `${n} mietintöä tästä valiokunnasta osoittavat intensiivistä lainsäädäntötyötä.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Eduskunnan valiokunnat ovat olleet aktiivisia aloilla ${committees} ja ${extra} muulla politiikka-alueella.` : `Eduskunnan valiokunnat ovat olleet aktiivisia aloilla ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Yhteensä ${n} mietintöä osoittaa jatkuvaa lainsäädännöllistä vauhtia.`, @@ -545,6 +534,7 @@ export const CONTENT_LABELS_PART1: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `Nämä ${propCount} esitystä koskevat ${domainCount} politiikka-aluetta, mikä osoittaa hallituksen laajaa lainsäädäntökunnianhimoa.`, genericOverview: (n: number): string => `Tänä aikana eduskunnassa käsiteltiin ${n} asiakirjaa.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} aloitetta jätetty`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} välikysymy${n > 1 ? 'stä' : 's'} jätetty`, otherCommittee: 'Muut valiokunnat', otherDocuments: 'Muut asiakirjat', policySignificanceTouches: (domains: string): string => `Koskee aloja ${domains}.`, @@ -623,6 +613,8 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsOverview: (n: number): string => `Oppositionsabgeordnete haben ${n} neue Anträge eingereicht.`, motionSignificance: 'Dieser Antrag befasst sich mit', readFullMotion: 'Den vollständigen Antrag lesen', + interpellationDefault: 'Parlamentarische Interpellation, die Rechenschaft der Regierung verlangt.', + readFullInterpellation: 'Die vollständige Interpellation lesen', policyContext: 'Politischer Kontext', filedBy: 'Eingereicht von', published: 'Veröffentlicht', @@ -637,20 +629,15 @@ export const CONTENT_LABELS_PART1: Partial> = { whyItMatters: 'Warum es wichtig ist', committeeBreakdown: (n: number, c: number): string => `Diese Runde von ${n} Ausschussberichten umfasst ${c} verschiedene Ausschüsse.`, propsBreakdown: (n: number): string => `Die Regierung hat ${n} neue Vorlagen eingebracht.`, - motionsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} neue Anträge eingereicht.` -, + motionsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} neue Anträge eingereicht.`, noInterpellations: 'Derzeit keine Interpellationen verfügbar.', - interpellationDefault: 'Parlamentarische Interpellation, die Rechenschaft der Regierung verlangt.', - interpellationsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} Interpellationen eingereicht, um Ministerverantwortung einzufordern.`, + interpellationsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} Interpellationen an Regierungsminister eingereicht und fordern Rechenschaft sowie formelle Antworten zu drängenden politischen Fragen.`, debateDynamics: 'Debattendynamik und Verantwortlichkeit', ministerAccountability: 'Gerichtet an', accountabilityAnalysis: 'Verantwortlichkeitsanalyse', - readFullInterpellation: 'Vollständige Interpellation lesen', interpellationBy: 'Interpellation von', interpellationsScrutinyContext: (n: number): string => `Interpellationen aus ${n} verschiedenen Parteien zeigen eine breite parlamentarische Kontrolle der Regierungsminister.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `Diese ${count} Interpellationen erstrecken sich über ${domains} Politikbereich${domains > 1 ? 'e' : ''}, was die Breite der parlamentarischen Rechenschaftsforderungen an die Regierung widerspiegelt.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} Interpellation${n > 1 ? 'en' : ''} eingereicht`, - committeeCountContext: (n: number): string => `${n} Berichte dieses Ausschusses signalisieren intensive Gesetzgebungsarbeit.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Die Parlamentsausschüsse waren in den Bereichen ${committees} und ${extra} weiteren Politikfeldern aktiv.` : `Die Parlamentsausschüsse waren in den Bereichen ${committees} aktiv.`, committeeMomentumTakeaway: (n: number): string => `Insgesamt ${n} Berichte zeugen von anhaltendem Gesetzgebungsmomentum.`, @@ -659,6 +646,7 @@ export const CONTENT_LABELS_PART1: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `Diese ${propCount} Vorlagen betreffen ${domainCount} Politikbereich${domainCount > 1 ? 'e' : ''} und demonstrieren den breiten Gesetzgebungsanspruch der Regierung.`, genericOverview: (n: number): string => `In diesem Zeitraum wurden ${n} Dokumente im Parlament behandelt.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} Antrag${n > 1 ? 'e' : ''} eingereicht`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} Interpellation${n > 1 ? 'en' : ''} eingereicht`, otherCommittee: 'Sonstige Ausschüsse', otherDocuments: 'Sonstige Dokumente', policySignificanceTouches: (domains: string): string => `Betrifft ${domains}.`, @@ -737,6 +725,8 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsOverview: (n: number): string => `Des députés de l'opposition ont déposé ${n} nouvelles motions.`, motionSignificance: 'Cette motion traite de', readFullMotion: 'Lire la motion complète', + interpellationDefault: 'Interpellation parlementaire exigeant la responsabilité du gouvernement.', + readFullInterpellation: 'Lire l\'interpellation complète', policyContext: 'Contexte politique', filedBy: 'Déposé par', published: 'Publié', @@ -751,20 +741,15 @@ export const CONTENT_LABELS_PART1: Partial> = { whyItMatters: 'Pourquoi c\'est important', committeeBreakdown: (n: number, c: number): string => `Ce lot de ${n} rapports de commission couvre ${c} commissions différentes.`, propsBreakdown: (n: number): string => `Le gouvernement a soumis ${n} nouvelles propositions.`, - motionsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} nouvelles motions.` -, + motionsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} nouvelles motions.`, noInterpellations: 'Aucune interpellation disponible pour le moment.', - interpellationDefault: 'Interpellation parlementaire exigeant la responsabilité du gouvernement.', - interpellationsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} interpellations exigeant la responsabilité ministérielle.`, + interpellationsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} interpellations auprès des ministres du gouvernement, exigeant des comptes et des réponses formelles sur des questions politiques urgentes.`, debateDynamics: 'Dynamique du débat et responsabilité', ministerAccountability: 'Adressée à', accountabilityAnalysis: 'Analyse de la responsabilité', - readFullInterpellation: 'Lire l\'interpellation complète', interpellationBy: 'Interpellation par', interpellationsScrutinyContext: (n: number): string => `Des interpellations de ${n} partis différents démontrent un large contrôle parlementaire des ministres du gouvernement.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `Ces ${count} interpellations couvrent ${domains} domaine${domains > 1 ? 's' : ''} politique${domains > 1 ? 's' : ''}, reflétant l'ampleur des exigences de responsabilité parlementaire envers le gouvernement.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party} : ${n} interpellation${n > 1 ? 's' : ''} déposée${n > 1 ? 's' : ''}`, - committeeCountContext: (n: number): string => `${n} rapports de cette commission témoignent d'un travail législatif intensif.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Les commissions parlementaires ont été actives dans les domaines ${committees} et ${extra} autres domaines politiques.` : `Les commissions parlementaires ont été actives dans les domaines ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Un total de ${n} rapports démontre un élan législatif soutenu.`, @@ -773,6 +758,7 @@ export const CONTENT_LABELS_PART1: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `Ces ${propCount} propositions touchent à ${domainCount} domaine${domainCount > 1 ? 's' : ''} politique${domainCount > 1 ? 's' : ''}, démontrant l'ambition législative du gouvernement.`, genericOverview: (n: number): string => `Durant cette période, ${n} documents ont été traités au parlement.`, partyMotionsFiled: (party: string, n: number): string => `${party} : ${n} motion${n > 1 ? 's' : ''} déposée${n > 1 ? 's' : ''}`, + partyInterpellationsFiled: (party: string, n: number): string => `${party} : ${n} interpellation${n > 1 ? 's' : ''} déposée${n > 1 ? 's' : ''}`, otherCommittee: 'Autres commissions', otherDocuments: 'Autres documents', policySignificanceTouches: (domains: string): string => `Touche aux domaines ${domains}.`, diff --git a/scripts/data-transformers/constants/content-labels-part2.ts b/scripts/data-transformers/constants/content-labels-part2.ts index fe4dfa07fe0..0016ed603e8 100644 --- a/scripts/data-transformers/constants/content-labels-part2.ts +++ b/scripts/data-transformers/constants/content-labels-part2.ts @@ -53,6 +53,8 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsOverview: (n: number): string => `Diputados de la oposición han presentado ${n} nuevas mociones.`, motionSignificance: 'Esta moción aborda', readFullMotion: 'Leer la moción completa', + interpellationDefault: 'Interpelación parlamentaria que exige rendición de cuentas del gobierno.', + readFullInterpellation: 'Leer la interpelación completa', policyContext: 'Contexto político', filedBy: 'Presentada por', published: 'Publicado', @@ -67,20 +69,15 @@ export const CONTENT_LABELS_PART2: Partial> = { whyItMatters: 'Por qué es importante', committeeBreakdown: (n: number, c: number): string => `Este lote de ${n} informes de comisión abarca ${c} comisiones diferentes.`, propsBreakdown: (n: number): string => `El gobierno ha presentado ${n} nuevas proposiciones.`, - motionsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} nuevas mociones.` -, + motionsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} nuevas mociones.`, noInterpellations: 'No hay interpelaciones disponibles en este momento.', - interpellationDefault: 'Interpelación parlamentaria que exige rendición de cuentas gubernamental.', - interpellationsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} interpelaciones exigiendo responsabilidad ministerial.`, + interpellationsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} interpelaciones a ministros del gobierno, exigiendo rendición de cuentas y respuestas formales sobre cuestiones políticas urgentes.`, debateDynamics: 'Dinámica del debate y responsabilidad', ministerAccountability: 'Dirigida a', accountabilityAnalysis: 'Análisis de responsabilidad', - readFullInterpellation: 'Leer la interpelación completa', interpellationBy: 'Interpelación de', interpellationsScrutinyContext: (n: number): string => `Interpelaciones de ${n} partidos diferentes demuestran un amplio escrutinio parlamentario de los ministros del gobierno.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `Estas ${count} interpelaciones abarcan ${domains} ámbito${domains > 1 ? 's' : ''} de política, reflejando la amplitud de las demandas parlamentarias de responsabilidad al gobierno.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpelación${n > 1 ? 'es' : ''} presentada${n > 1 ? 's' : ''}`, - committeeCountContext: (n: number): string => `${n} informes de esta comisión señalan un trabajo legislativo intensivo.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Las comisiones parlamentarias han estado activas en las áreas de ${committees} y ${extra} dominios políticos más.` : `Las comisiones parlamentarias han estado activas en las áreas de ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Un total de ${n} informes demuestra un impulso legislativo sostenido.`, @@ -89,6 +86,7 @@ export const CONTENT_LABELS_PART2: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `Estas ${propCount} proposiciones abarcan ${domainCount} dominio${domainCount > 1 ? 's' : ''} político${domainCount > 1 ? 's' : ''}, demostrando la amplia ambición legislativa del gobierno.`, genericOverview: (n: number): string => `Durante este período, ${n} documentos fueron procesados en el parlamento.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} moción${n > 1 ? 'es' : ''} presentada${n > 1 ? 's' : ''}`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpelación${n > 1 ? 'es' : ''} presentada${n > 1 ? 's' : ''}`, otherCommittee: 'Otras comisiones', otherDocuments: 'Otros documentos', policySignificanceTouches: (domains: string): string => `Toca los ámbitos de ${domains}.`, @@ -167,6 +165,8 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsOverview: (n: number): string => `Oppositieleden hebben ${n} nieuwe moties ingediend.`, motionSignificance: 'Deze motie behandelt', readFullMotion: 'Lees de volledige motie', + interpellationDefault: 'Parlementaire interpellatie die verantwoording van de regering eist.', + readFullInterpellation: 'Lees de volledige interpellatie', policyContext: 'Politieke context', filedBy: 'Ingediend door', published: 'Gepubliceerd', @@ -181,20 +181,15 @@ export const CONTENT_LABELS_PART2: Partial> = { whyItMatters: 'Waarom het belangrijk is', committeeBreakdown: (n: number, c: number): string => `Deze reeks van ${n} commissierapporten bestrijkt ${c} verschillende commissies.`, propsBreakdown: (n: number): string => `De regering heeft ${n} nieuwe voorstellen ingediend.`, - motionsBreakdown: (n: number): string => `Oppositieleden hebben ${n} nieuwe moties ingediend.` -, + motionsBreakdown: (n: number): string => `Oppositieleden hebben ${n} nieuwe moties ingediend.`, noInterpellations: 'Geen interpellaties beschikbaar op dit moment.', - interpellationDefault: 'Parlementaire interpellatie die verantwoording van de regering eist.', - interpellationsBreakdown: (n: number): string => `Oppositieleden hebben ${n} interpellaties ingediend om ministeriële verantwoording te eisen.`, + interpellationsBreakdown: (n: number): string => `Oppositieleden hebben ${n} interpellaties ingediend bij regeringsministers en eisen verantwoording en formele antwoorden op dringende politieke kwesties.`, debateDynamics: 'Debatdynamiek en verantwoording', ministerAccountability: 'Gericht aan', accountabilityAnalysis: 'Verantwoordingsanalyse', - readFullInterpellation: 'Lees de volledige interpellatie', interpellationBy: 'Interpellatie door', interpellationsScrutinyContext: (n: number): string => `Interpellaties van ${n} verschillende partijen tonen brede parlementaire controle op de ministers van de regering.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `Deze ${count} interpellaties bestrijken ${domains} beleidsterrein${domains > 1 ? 'en' : ''}, wat de breedte van parlementaire verantwoordingseisen aan de regering weerspiegelt.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellatie${n > 1 ? 's' : ''} ingediend`, - committeeCountContext: (n: number): string => `${n} rapporten van deze commissie signaleren intensief wetgevend werk.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `De parlementaire commissies zijn actief geweest op het gebied van ${committees} en ${extra} verdere beleidsterreinen.` : `De parlementaire commissies zijn actief geweest op het gebied van ${committees}.`, committeeMomentumTakeaway: (n: number): string => `In totaal ${n} rapporten tonen aanhoudend wetgevend momentum.`, @@ -203,6 +198,7 @@ export const CONTENT_LABELS_PART2: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `Deze ${propCount} wetsvoorstellen bestrijken ${domainCount} beleidsterrein${domainCount > 1 ? 'en' : ''}, wat de brede wetgevende ambitie van de regering laat zien.`, genericOverview: (n: number): string => `In deze periode zijn ${n} documenten in het parlement behandeld.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} motie${n > 1 ? 's' : ''} ingediend`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} interpellatie${n > 1 ? 's' : ''} ingediend`, otherCommittee: 'Overige commissies', otherDocuments: 'Overige documenten', policySignificanceTouches: (domains: string): string => `Raakt aan ${domains}.`, @@ -281,6 +277,8 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsOverview: (n: number): string => `قدم أعضاء المعارضة ${n} اقتراحات جديدة.`, motionSignificance: 'يتناول هذا الاقتراح', readFullMotion: 'قراءة الاقتراح الكامل', + interpellationDefault: 'استجواب برلماني يطالب بمساءلة الحكومة.', + readFullInterpellation: 'قراءة الاستجواب الكامل', policyContext: 'السياق السياسي', filedBy: 'مقدم من', published: 'تاريخ النشر', @@ -295,20 +293,15 @@ export const CONTENT_LABELS_PART2: Partial> = { whyItMatters: 'لماذا هذا مهم', committeeBreakdown: (n: number, c: number): string => `تغطي هذه الدفعة من ${n} تقارير لجان ${c} لجان مختلفة.`, propsBreakdown: (n: number): string => `قدمت الحكومة ${n} مقترحات جديدة.`, - motionsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} اقتراحات جديدة.` -, + motionsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} اقتراحات جديدة.`, noInterpellations: 'لا توجد استجوابات متاحة حالياً.', - interpellationDefault: 'استجواب برلماني يطالب بالمساءلة الحكومية.', - interpellationsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} استجواباً يطالبون فيه بمساءلة الوزراء.`, + interpellationsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} استجواباً لوزراء الحكومة، مطالبين بالمساءلة وردود رسمية على القضايا السياسية الملحّة.`, debateDynamics: 'ديناميكيات النقاش والمساءلة', ministerAccountability: 'موجه إلى', accountabilityAnalysis: 'تحليل المساءلة', - readFullInterpellation: 'قراءة الاستجواب الكامل', interpellationBy: 'استجواب من', interpellationsScrutinyContext: (n: number): string => `استجوابات من ${n} أحزاب مختلفة تُظهر رقابة برلمانية واسعة على وزراء الحكومة.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `تغطي هذه الاستجوابات الـ ${count} عدد ${domains} من مجالات السياسة، مما يعكس اتساع مطالب المساءلة البرلمانية تجاه الحكومة.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} استجواب مقدم`, - committeeCountContext: (n: number): string => `${n} تقارير من هذه اللجنة تشير إلى عمل تشريعي مكثف.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `كانت اللجان البرلمانية نشطة في مجالات ${committees} و${extra} مجالات سياسية أخرى.` : `كانت اللجان البرلمانية نشطة في مجالات ${committees}.`, committeeMomentumTakeaway: (n: number): string => `إجمالي ${n} تقارير يدل على زخم تشريعي مستمر.`, @@ -317,6 +310,7 @@ export const CONTENT_LABELS_PART2: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `تمس هذه المقترحات الـ ${propCount} ${domainCount} مجال${domainCount > 1 ? 'ات' : ''} سياسي${domainCount > 1 ? 'ة' : ''}.`, genericOverview: (n: number): string => `خلال هذه الفترة، تمت معالجة ${n} وثائق في البرلمان.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} اقتراح${n > 1 ? 'ات' : ''} مقدمة`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} استجواب${n > 1 ? 'ات' : ''} مقدمة`, otherCommittee: 'لجان أخرى', otherDocuments: 'وثائق أخرى', policySignificanceTouches: (domains: string): string => `يتعلق بمجالات ${domains}.`, @@ -395,6 +389,8 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsOverview: (n: number): string => `חברי אופוזיציה הגישו ${n} הצעות חדשות.`, motionSignificance: 'הצעה זו עוסקת ב', readFullMotion: 'קראו את ההצעה המלאה', + interpellationDefault: 'אינטרפלציה פרלמנטרית הדורשת אחריותיות מהממשלה.', + readFullInterpellation: 'קראו את האינטרפלציה המלאה', policyContext: 'הקשר מדיני', filedBy: 'הוגשה על ידי', published: 'פורסם', @@ -409,20 +405,15 @@ export const CONTENT_LABELS_PART2: Partial> = { whyItMatters: 'למה זה חשוב', committeeBreakdown: (n: number, c: number): string => `קבוצה זו של ${n} דוחות ועדה מכסה ${c} ועדות שונות.`, propsBreakdown: (n: number): string => `הממשלה הגישה ${n} הצעות חדשות.`, - motionsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} הצעות חדשות.` -, + motionsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} הצעות חדשות.`, noInterpellations: 'אין אינטרפלציות זמינות כרגע.', - interpellationDefault: 'אינטרפלציה פרלמנטרית הדורשת אחריותיות ממשלתית.', - interpellationsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} אינטרפלציות הדורשות אחריות שרים.`, + interpellationsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} אינטרפלציות לשרי הממשלה, ודורשים אחריות ותשובות רשמיות על סוגיות מדיניות דחופות.`, debateDynamics: 'דינמיקת הדיון ואחריות', ministerAccountability: 'מופנית אל', accountabilityAnalysis: 'ניתוח אחריות', - readFullInterpellation: 'קרא את האינטרפלציה המלאה', interpellationBy: 'אינטרפלציה מאת', interpellationsScrutinyContext: (n: number): string => `אינטרפלציות מ-${n} מפלגות שונות מדגימות פיקוח פרלמנטרי רחב על שרי הממשלה.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `${count} אינטרפלציות אלה מכסות ${domains} תחומי מדיניות, המשקפים את רוחב דרישות האחריותיות הפרלמנטריות כלפי הממשלה.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} אינטרפלציות הוגשו`, - committeeCountContext: (n: number): string => `${n} דוחות מוועדה זו מעידים על עבודת חקיקה אינטנסיבית.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `ועדות הפרלמנט היו פעילות בתחומי ${committees} ו-${extra} תחומי מדיניות נוספים.` : `ועדות הפרלמנט היו פעילות בתחומי ${committees}.`, committeeMomentumTakeaway: (n: number): string => `סך של ${n} דוחות מעיד על מומנטום חקיקתי מתמשך.`, @@ -431,6 +422,7 @@ export const CONTENT_LABELS_PART2: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `${propCount} הצעות חוק אלה נוגעות ל-${domainCount} תחומ${domainCount > 1 ? 'י' : ''} מדיניות, המדגימות את שאיפת החקיקה הרחבה של הממשלה.`, genericOverview: (n: number): string => `במהלך תקופה זו, ${n} מסמכים עברו טיפול בפרלמנט.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n} הצע${n > 1 ? 'ות' : 'ה'} הוגש${n > 1 ? 'ו' : 'ה'}`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n} אינטרפלצי${n > 1 ? 'ות' : 'ה'} הוגש${n > 1 ? 'ו' : 'ה'}`, otherCommittee: 'ועדות אחרות', otherDocuments: 'מסמכים אחרים', policySignificanceTouches: (domains: string): string => `נוגע בתחומי ${domains}.`, @@ -509,6 +501,8 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsOverview: (n: number): string => `野党議員が${n}件の新しい動議を提出しました。`, motionSignificance: 'この動議は', readFullMotion: '動議全文を読む', + interpellationDefault: '政府の説明責任を求める国会質問主意書。', + readFullInterpellation: '質問主意書の全文を読む', policyContext: '政策的背景', filedBy: '提出者', published: '公開日', @@ -523,20 +517,15 @@ export const CONTENT_LABELS_PART2: Partial> = { whyItMatters: 'なぜ重要か', committeeBreakdown: (n: number, c: number): string => `この${n}件の委員会報告は${c}の異なる委員会にまたがっています。`, propsBreakdown: (n: number): string => `政府は${n}件の新たな法案を提出しました。`, - motionsBreakdown: (n: number): string => `野党議員が${n}件の新たな動議を提出しました。` -, + motionsBreakdown: (n: number): string => `野党議員が${n}件の新たな動議を提出しました。`, noInterpellations: '現在、質問主意書はありません。', - interpellationDefault: '政府の説明責任を求める議会質問主意書。', - interpellationsBreakdown: (n: number): string => `野党議員が${n}件の質問主意書を提出し、大臣の説明責任を求めています。`, + interpellationsBreakdown: (n: number): string => `野党議員が閣僚に対して${n}件の質問主意書を提出し、緊急の政策問題に関する説明責任と正式な回答を求めています。`, debateDynamics: '討論の動態と説明責任', ministerAccountability: '宛先', accountabilityAnalysis: '説明責任分析', - readFullInterpellation: '質問主意書全文を読む', interpellationBy: '質問主意書提出者', interpellationsScrutinyContext: (n: number): string => `${n}の異なる政党からの質問主意書は、政府大臣に対する広範な議会の監視を示しています。`, interpellationsAccountabilityContext: (count: number, domains: number): string => `これら${count}件の質問主意書は${domains}の政策分野にまたがり、政府に対する議会の説明責任要求の幅広さを反映しています。`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n}件の質問主意書を提出`, - committeeCountContext: (n: number): string => `この委員会からの${n}件の報告書は、集中的な立法作業を示しています。`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `議会の委員会は${committees}および他${extra}の政策分野で活動しています。` : `議会の委員会は${committees}の分野で活動しています。`, committeeMomentumTakeaway: (n: number): string => `合計${n}件の報告書は、持続的な立法の勢いを示しています。`, @@ -545,6 +534,7 @@ export const CONTENT_LABELS_PART2: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `これら${propCount}件の法案は${domainCount}つの政策分野に及び、政府の幅広い立法意欲を示しています。`, genericOverview: (n: number): string => `この期間中、議会で${n}件の文書が処理されました。`, partyMotionsFiled: (party: string, n: number): string => `${party}:${n}件の動議を提出`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}:${n}件の質問主意書を提出`, otherCommittee: 'その他の委員会', otherDocuments: 'その他の文書', policySignificanceTouches: (domains: string): string => `${domains}に関連します。`, @@ -623,6 +613,8 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsOverview: (n: number): string => `야당 의원들이 ${n}개의 새 동의안을 제출했습니다.`, motionSignificance: '이 동의안은', readFullMotion: '전체 동의안 읽기', + interpellationDefault: '정부의 책임을 요구하는 국회 대정부질문.', + readFullInterpellation: '전체 대정부질문 읽기', policyContext: '정책 맥락', filedBy: '제출자', published: '게시일', @@ -637,20 +629,15 @@ export const CONTENT_LABELS_PART2: Partial> = { whyItMatters: '왜 중요한가', committeeBreakdown: (n: number, c: number): string => `이 ${n}개 위원회 보고서는 ${c}개의 서로 다른 위원회에 걸쳐 있습니다.`, propsBreakdown: (n: number): string => `정부가 ${n}개의 새 법안을 제출했습니다.`, - motionsBreakdown: (n: number): string => `야당 의원들이 ${n}개의 새 동의안을 제출했습니다.` -, - noInterpellations: '현재 대정부 질의가 없습니다.', - interpellationDefault: '정부의 책임을 요구하는 의회 대정부질문.', - interpellationsBreakdown: (n: number): string => `야당 의원들이 장관의 책임을 요구하는 ${n}개의 대정부 질의를 제출했습니다.`, + motionsBreakdown: (n: number): string => `야당 의원들이 ${n}개의 새 동의안을 제출했습니다.`, + noInterpellations: '현재 이용 가능한 대정부질문이 없습니다.', + interpellationsBreakdown: (n: number): string => `야당 의원들이 정부 장관에게 ${n}건의 대정부질문을 제출하여 긴박한 정책 문제에 대한 책임과 공식 답변을 요구하고 있습니다.`, debateDynamics: '토론 역학 및 책임성', ministerAccountability: '수신자', accountabilityAnalysis: '책임성 분석', - readFullInterpellation: '전체 대정부 질의 읽기', interpellationBy: '질의 제출자', interpellationsScrutinyContext: (n: number): string => `${n}개의 서로 다른 정당의 대정부 질의는 정부 장관에 대한 광범위한 의회 감시를 보여줍니다.`, interpellationsAccountabilityContext: (count: number, domains: number): string => `이 ${count}건의 대정부 질의는 ${domains}개의 정책 분야에 걸쳐 있으며, 정부에 대한 의회 책임 요구의 폭을 반영합니다.`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n}건의 대정부 질의 제출`, - committeeCountContext: (n: number): string => `이 위원회의 ${n}건의 보고서는 집중적인 입법 작업을 나타냅니다.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `의회 위원회는 ${committees} 및 ${extra}개의 추가 정책 분야에서 활동했습니다.` : `의회 위원회는 ${committees} 분야에서 활동했습니다.`, committeeMomentumTakeaway: (n: number): string => `총 ${n}건의 보고서는 지속적인 입법 추진력을 보여줍니다.`, @@ -659,6 +646,7 @@ export const CONTENT_LABELS_PART2: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `이 ${propCount}건의 법안은 ${domainCount}개 정책 영역에 걸쳐 있으며, 정부의 광범위한 입법 야심을 보여줍니다.`, genericOverview: (n: number): string => `이 기간 동안 의회에서 ${n}건의 문서가 처리되었습니다.`, partyMotionsFiled: (party: string, n: number): string => `${party}: ${n}건의 동의안 제출`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}: ${n}건의 대정부질문 제출`, otherCommittee: '기타 위원회', otherDocuments: '기타 문서', policySignificanceTouches: (domains: string): string => `${domains} 분야에 관련됩니다.`, @@ -737,6 +725,8 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsOverview: (n: number): string => `反对党议员提交了${n}项新动议。`, motionSignificance: '该动议涉及', readFullMotion: '阅读完整动议', + interpellationDefault: '要求政府问责的议会质询。', + readFullInterpellation: '阅读完整质询', policyContext: '政策背景', filedBy: '提交者', published: '发布日期', @@ -751,20 +741,15 @@ export const CONTENT_LABELS_PART2: Partial> = { whyItMatters: '为什么重要', committeeBreakdown: (n: number, c: number): string => `这批${n}份委员会报告涵盖${c}个不同委员会。`, propsBreakdown: (n: number): string => `政府提交了${n}项新提案。`, - motionsBreakdown: (n: number): string => `反对党议员提交了${n}项新动议。` -, - noInterpellations: '目前没有质询可用。', - interpellationDefault: '要求政府问责的议会质询。', - interpellationsBreakdown: (n: number): string => `反对党议员提交了${n}项质询,要求追究部长责任。`, + motionsBreakdown: (n: number): string => `反对党议员提交了${n}项新动议。`, + noInterpellations: '目前没有可用的质询。', + interpellationsBreakdown: (n: number): string => `反对党议员向政府部长提交了${n}份质询,要求对紧迫政策问题承担责任并作出正式答复。`, debateDynamics: '辩论动态与问责制', ministerAccountability: '质询对象', accountabilityAnalysis: '问责分析', - readFullInterpellation: '阅读完整质询', interpellationBy: '质询提出者', interpellationsScrutinyContext: (n: number): string => `来自${n}个不同政党的质询展示了对政府部长的广泛议会监督。`, interpellationsAccountabilityContext: (count: number, domains: number): string => `这${count}项质询涵盖${domains}个政策领域,反映了议会对政府问责要求的广度。`, - partyInterpellationsFiled: (party: string, n: number): string => `${party}:提交了${n}项质询`, - committeeCountContext: (n: number): string => `该委员会的${n}份报告表明密集的立法工作。`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `议会委员会在${committees}以及其他${extra}个政策领域积极开展工作。` : `议会委员会在${committees}领域积极开展工作。`, committeeMomentumTakeaway: (n: number): string => `共${n}份报告展示了持续的立法势头。`, @@ -773,6 +758,7 @@ export const CONTENT_LABELS_PART2: Partial> = { policyImplicationsContext: (propCount: number, domainCount: number): string => `这${propCount}项提案涉及${domainCount}个政策领域,展示了政府广泛的立法雄心。`, genericOverview: (n: number): string => `在此期间,议会处理了${n}份文件。`, partyMotionsFiled: (party: string, n: number): string => `${party}:提交了${n}项动议`, + partyInterpellationsFiled: (party: string, n: number): string => `${party}:提交了${n}项质询`, otherCommittee: '其他委员会', otherDocuments: '其他文件', policySignificanceTouches: (domains: string): string => `涉及${domains}领域。`, diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts index 56246fc8465..c3d0cefe287 100644 --- a/scripts/data-transformers/content-generators/interpellations.ts +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -244,5 +244,23 @@ export function generateInterpellationsContent(data: ArticleContentData, lang: L content += `
    \n
    \n`; } + // Government department engagement section (from data pipeline) + const govDeptData = data.govDeptData ?? []; + if (govDeptData.length > 0) { + content += `\n

    ${L(lang, 'govEngagement')}

    \n`; + content += `
    \n
      \n`; + govDeptData.slice(0, 5).forEach(dept => { + const deptName = escapeHtml(String(dept['name'] ?? dept['departement'] ?? dept['department'] ?? '')); + const deptCount = dept['count'] ?? dept['total'] ?? dept['document_count']; + if (deptName) { + const hasDeptCount = deptCount !== null && deptCount !== undefined; + content += hasDeptCount + ? `
    • ${deptName} (${escapeHtml(String(deptCount))})
    • \n` + : `
    • ${deptName}
    • \n`; + } + }); + content += `
    \n
    \n`; + } + return content; } diff --git a/scripts/data-transformers/policy-analysis.ts b/scripts/data-transformers/policy-analysis.ts index 17ea821cb17..a8db2532414 100644 --- a/scripts/data-transformers/policy-analysis.ts +++ b/scripts/data-transformers/policy-analysis.ts @@ -311,205 +311,205 @@ const DOMAIN_ANALYSES: Record = { en: { mot: 'Fiscal policy motions directly challenge the government\'s budget assumptions and signal opposition readiness to contest tax and spending priorities.', bet: 'The Finance Committee\'s position on fiscal matters is usually decisive — the chamber almost always follows its recommendation on budgetary questions.', - ip: 'This interpellation demands ministerial accountability on fiscal policy, compelling the responsible minister to justify tax and spending decisions in open chamber debate.', + ip: 'This interpellation demands government accountability on fiscal policy, compelling the minister to defend budget assumptions or explain spending deviations before parliament.', default: 'Government fiscal proposals must clear rigorous Finance Committee scrutiny and align with Sweden\'s fiscal surplus rule, making the committee\'s verdict pivotal.' }, sv: { mot: 'Finanspolitiska motioner utmanar direkt regeringens budgetantaganden och signalerar oppositionens beredskap att bestrida skatte- och utgiftsprioriteringar.', bet: 'Finansutskottets ståndpunkt i finanspolitiska frågor är i regel avgörande – kammaren följer nästan alltid utskottets rekommendation.', - ip: 'Interpellationen kräver ministerns redovisning av finanspolitiken och tvingar ansvarigt statsråd att försvara skatte- och utgiftsbeslut i öppen kammardebatt.', + ip: 'Denna interpellation kräver regeringens ansvarighet i finanspolitiken och tvingar ministern att försvara budgetantaganden eller förklara utgiftsavvikelser inför riksdagen.', default: 'Regeringens finanspolitiska förslag måste klara finansutskottets granskning och harmonisera med överskottsmålet för att nå bifall.' }, - de: { default: `Haushaltspolitische Vorschläge müssen die strenge Prüfung des Finanzausschusses bestehen und die schwedische Überschussregel einhalten.` }, - fr: { default: `Les propositions fiscales doivent passer l'examen rigoureux de la commission des finances et s'aligner sur la règle d'excédent budgétaire suédoise.` }, - es: { default: `Las propuestas fiscales deben superar el riguroso escrutinio del comité de finanzas y alinearse con la regla de superávit fiscal sueca.` } + de: { default: 'Haushaltspolitische Vorschläge müssen die strenge Prüfung des Finanzausschusses bestehen und die schwedische Überschussregel einhalten.' }, + fr: { default: 'Les propositions fiscales doivent passer l\'examen rigoureux de la commission des finances et s\'aligner sur la règle d\'excédent budgétaire suédoise.' }, + es: { default: 'Las propuestas fiscales deben superar el riguroso escrutinio del comité de finanzas y alinearse con la regla de superávit fiscal sueca.' }, }, 'defence and security policy': { en: { mot: 'Defence motions carry heightened strategic significance following Sweden\'s NATO accession, pressing the government on long-term security commitments.', bet: 'Committee reports on defence shape Sweden\'s military posture and NATO integration trajectory — decisions here have multi-decade consequences.', - ip: 'This interpellation scrutinises the minister\'s defence and security decisions, demanding public accountability on Sweden\'s NATO commitments and military readiness.', + ip: 'This interpellation exercises parliamentary oversight on defence and security, pressing the minister to account for strategic decisions or gaps in military preparedness.', default: 'Defence proposals engage Sweden\'s NATO obligations and cross-party consensus-building mechanisms for national security legislation.' }, sv: { mot: 'Försvarsrelaterade motioner har förhöjd strategisk betydelse efter Sveriges NATO-inträde och pressar regeringen om långsiktiga säkerhetsåtaganden.', bet: 'Utskottsbetänkanden om försvar formar Sveriges militära inriktning och NATO-integration – besluten har konsekvenser i decennier.', - ip: 'Interpellationen granskar ministerns försvars- och säkerhetsbeslut och kräver offentlig redovisning av Sveriges NATO-åtaganden och militära beredskap.', + ip: 'Denna interpellation utövar parlamentarisk kontroll över försvars- och säkerhetspolitiken och kräver att ministern redogör för strategiska beslut eller brister i beredskapen.', default: 'Försvarspropositioner engagerar Sveriges NATO-förpliktelser och mekanismer för brett partistöd inom säkerhetspolitiken.' }, - de: { default: `Verteidigungsvorschläge berühren Schwedens NATO-Verpflichtungen und Mechanismen zur parteiübergreifenden Konsensfindung in der nationalen Sicherheitspolitik.` }, - fr: { default: `Les propositions de défense engagent les obligations de la Suède envers l'OTAN et les mécanismes de consensus multipartite pour la législation de sécurité nationale.` }, - es: { default: `Las propuestas de defensa comprometen las obligaciones de Suecia con la OTAN y los mecanismos de consenso multipartidario para la legislación de seguridad nacional.` } + de: { default: 'Verteidigungsvorschläge berühren Schwedens NATO-Verpflichtungen und Mechanismen zur parteiübergreifenden Konsensfindung in der nationalen Sicherheitspolitik.' }, + fr: { default: 'Les propositions de défense engagent les obligations de la Suède envers l\'OTAN et les mécanismes de consensus multipartite pour la législation de sécurité nationale.' }, + es: { default: 'Las propuestas de defensa comprometen las obligaciones de Suecia con la OTAN y los mecanismos de consenso multipartidario para la legislación de seguridad nacional.' }, }, 'environmental and climate policy': { en: { mot: 'Climate motions reflect growing parliamentary pressure for faster decarbonisation, often targeting specific industries or the pace of policy implementation.', bet: 'The Environment Committee\'s recommendations balance climate ambition against economic competitiveness — its position sets the legislative baseline.', - ip: 'This interpellation holds the minister accountable on environmental and climate commitments, pressing for transparency on emissions targets and implementation timelines.', + ip: 'This interpellation holds the government accountable on climate commitments, demanding the minister explain policy pace, emission targets, or regulatory gaps.', default: 'Environmental proposals must navigate competing interests from industry, regional governments, and EU climate commitments, making parliamentary support critical.' }, sv: { mot: 'Klimatmotioner speglar växande parlamentariskt tryck för snabbare koldioxidminskning och riktar sig ofta mot specifika branscher.', bet: 'Miljöutskottet väger klimatambition mot ekonomisk konkurrenskraft – dess rekommendation sätter lagstiftningens utgångspunkt.', - ip: 'Interpellationen ställer ministern till svars för miljö- och klimatåtaganden och kräver insyn i utsläppsmål och genomförandetider.', + ip: 'Denna interpellation ställer regeringen till svars för klimatåtaganden och kräver att ministern förklarar insatstakten, utsläppsmål eller regleringsbrister.', default: 'Miljöförslag måste navigera konkurrerande intressen från industrin, regionerna och EU:s klimatåtaganden.' }, - de: { default: `Umweltvorschläge müssen konkurrierende Interessen aus Industrie, regionalen Regierungen und EU-Klimaverpflichtungen ausbalancieren.` }, - fr: { default: `Les propositions environnementales doivent naviguer entre les intérêts concurrents de l'industrie, des gouvernements régionaux et des engagements climatiques de l'UE.` }, - es: { default: `Las propuestas medioambientales deben navegar entre los intereses en competencia de la industria, los gobiernos regionales y los compromisos climáticos de la UE.` } + de: { default: 'Umweltvorschläge müssen konkurrierende Interessen aus Industrie, regionalen Regierungen und EU-Klimaverpflichtungen ausbalancieren.' }, + fr: { default: 'Les propositions environnementales doivent naviguer entre les intérêts concurrents de l\'industrie, des gouvernements régionaux et des engagements climatiques de l\'UE.' }, + es: { default: 'Las propuestas medioambientales deben navegar entre los intereses en competencia de la industria, los gobiernos regionales y los compromisos climáticos de la UE.' }, }, 'healthcare policy': { en: { mot: 'Healthcare motions typically target gaps in regional service delivery, pressing for national minimum standards, additional funding, or new patient rights.', bet: 'Social Affairs Committee reports on healthcare set the framework for Sweden\'s regionally delivered but nationally financed health system.', - ip: 'This interpellation challenges the minister on healthcare delivery and patient outcomes, demanding answers on service gaps, waiting times, or regional disparities.', + ip: 'This interpellation demands ministerial accountability on healthcare, pressing for answers on waiting times, staffing shortages, or regional disparities in care quality.', default: 'Healthcare proposals require coordination between national government, regional councils, and professional bodies — a complexity that shapes the legislative timeline.' }, sv: { mot: 'Hälso- och sjukvårdsmotioner riktar sig typiskt mot brister i regionala tjänster och driver på för nationella miniminivåer eller nya patienträttigheter.', bet: 'Socialutskottets betänkanden om hälso- och sjukvård sätter ramarna för det regionalt levererade men nationellt finansierade hälsosystemet.', - ip: 'Interpellationen utmanar ministern om sjukvårdens leverans och patientresultat och kräver svar om tjänstebrister, väntetider eller regionala skillnader.', + ip: 'Denna interpellation kräver ministerns ansvarighet i hälso- och sjukvårdsfrågor och efterfrågar svar om väntetider, personalbrist eller regionala kvalitetsskillnader.', default: 'Hälso- och sjukvårdspropositioner kräver samordning mellan stat, regioner och professioner – en komplexitet som formar lagstiftningens tidslinje.' }, - de: { default: `Gesundheitsvorschläge erfordern Koordination zwischen nationaler Regierung, Regionalräten und Fachverbänden — eine Komplexität, die den Gesetzgebungszeitplan prägt.` }, - fr: { default: `Les propositions en matière de santé nécessitent une coordination entre le gouvernement national, les conseils régionaux et les organismes professionnels.` }, - es: { default: `Las propuestas sanitarias requieren coordinación entre el gobierno nacional, los consejos regionales y los organismos profesionales — una complejidad que determina el calendario legislativo.` } + de: { default: 'Gesundheitsvorschläge erfordern Koordination zwischen nationaler Regierung, Regionalräten und Fachverbänden — eine Komplexität, die den Gesetzgebungszeitplan prägt.' }, + fr: { default: 'Les propositions en matière de santé nécessitent une coordination entre le gouvernement national, les conseils régionaux et les organismes professionnels.' }, + es: { default: 'Las propuestas sanitarias requieren coordinación entre el gobierno nacional, los consejos regionales y los organismos profesionales — una complejidad que determina el calendario legislativo.' }, }, 'migration policy': { en: { mot: 'Migration motions reflect one of Sweden\'s most contested policy areas, with parties divided on asylum rules, integration requirements, and deportation procedures.', bet: 'The Social Insurance Committee\'s migration reports navigate Sweden\'s EU law obligations and UN Refugee Convention commitments alongside domestic political pressures.', - ip: 'This interpellation demands the minister\'s public defence of migration decisions, pressing for accountability on asylum processing, integration outcomes, or deportation policy.', + ip: 'This interpellation scrutinises government migration policy, compelling the minister to defend asylum processing timelines, integration outcomes, or border control measures.', default: 'Migration proposals must balance EU regulatory obligations with national political imperatives, making cross-party support essential for durable legislation.' }, sv: { mot: 'Migrationsmotioner speglar ett av Sveriges mest omtvistade politikområden, med partier delade om asylregler, integrationskrav och återvändanderutiner.', bet: 'Socialförsäkringsutskottets migrationsbetänkanden navigerar Sveriges åtaganden enligt EU-rätten och FN:s flyktingkonvention.', - ip: 'Interpellationen kräver ministerns offentliga försvar av migrationsbeslut och pressar på ansvarsskyldighet för asylprocesser, integrationsresultat eller utvisningspolitik.', + ip: 'Denna interpellation granskar regeringens migrationspolitik och kräver att ministern försvarar handläggningstider, integrationsresultat eller gränskontrollåtgärder.', default: 'Migrationspropositioner måste balansera EU-rättsliga förpliktelser med nationella politiska imperativ.' }, - de: { default: `Migrationsvorschläge müssen EU-Regulierungsverpflichtungen mit nationalen politischen Erfordernissen in Einklang bringen — parteiübergreifende Unterstützung ist für dauerhafte Gesetzgebung unerlässlich.` }, - fr: { default: `Les propositions migratoires doivent équilibrer les obligations réglementaires de l'UE et les impératifs politiques nationaux — le soutien multipartite est essentiel pour une législation durable.` }, - es: { default: `Las propuestas migratorias deben equilibrar las obligaciones regulatorias de la UE con los imperativos políticos nacionales, siendo el apoyo multipartidario esencial para una legislación duradera.` } + de: { default: 'Migrationsvorschläge müssen EU-Regulierungsverpflichtungen mit nationalen politischen Erfordernissen in Einklang bringen — parteiübergreifende Unterstützung ist für dauerhafte Gesetzgebung unerlässlich.' }, + fr: { default: 'Les propositions migratoires doivent équilibrer les obligations réglementaires de l\'UE et les impératifs politiques nationaux — le soutien multipartite est essentiel pour une législation durable.' }, + es: { default: 'Las propuestas migratorias deben equilibrar las obligaciones regulatorias de la UE con los imperativos políticos nacionales, siendo el apoyo multipartidario esencial para una legislación duradera.' }, }, 'EU and foreign affairs': { en: { mot: 'EU and foreign affairs motions signal parliamentary expectations for government negotiating positions — influential despite executive prerogative in external relations.', bet: 'The Foreign Affairs Committee\'s reports on EU matters reflect Sweden\'s positioning within the bloc and may bind future negotiating postures.', - ip: 'This interpellation compels the minister to explain Sweden\'s foreign policy or EU negotiating stance in open debate, reinforcing parliamentary oversight of executive diplomacy.', + ip: 'This interpellation exercises parliamentary oversight on foreign affairs, demanding the minister clarify Sweden\'s diplomatic stance or EU negotiating position on a specific issue.', default: 'EU and foreign affairs proposals engage Sweden\'s treaty obligations and often require coordination with European partners before domestic enactment.' }, sv: { mot: 'EU- och utrikespolitiska motioner signalerar parlamentets förväntningar på regeringens förhandlingspositioner.', bet: 'Utrikesutskottets betänkanden om EU-frågor speglar Sveriges positionering inom unionen och kan binda framtida förhandlingslinjer.', - ip: 'Interpellationen tvingar ministern att redovisa Sveriges utrikespolitiska eller EU-relaterade förhandlingsposition i öppen debatt och stärker riksdagens insyn i regeringens diplomati.', + ip: 'Denna interpellation utövar parlamentarisk kontroll över utrikespolitiken och kräver att ministern klargör Sveriges diplomatiska hållning eller EU-förhandlingsposition.', default: 'EU- och utrikespropositioner engagerar Sveriges fördragsförpliktelser och kräver samordning med europeiska partner.' }, - de: { default: `EU- und Außenpolitikvorschläge berühren Schwedens Vertragserpflichtungen und erfordern oft eine Koordination mit europäischen Partnern.` }, - fr: { default: `Les propositions en matière d'UE et d'affaires étrangères engagent les obligations conventionnelles de la Suède et nécessitent souvent une coordination avec les partenaires européens.` }, - es: { default: `Las propuestas de la UE y de asuntos exteriores comprometen las obligaciones convencionales de Suecia y a menudo requieren coordinación con los socios europeos.` } + de: { default: 'EU- und Außenpolitikvorschläge berühren Schwedens Vertragserpflichtungen und erfordern oft eine Koordination mit europäischen Partnern.' }, + fr: { default: 'Les propositions en matière d\'UE et d\'affaires étrangères engagent les obligations conventionnelles de la Suède et nécessitent souvent une coordination avec les partenaires européens.' }, + es: { default: 'Las propuestas de la UE y de asuntos exteriores comprometen las obligaciones convencionales de Suecia y a menudo requieren coordinación con los socios europeos.' }, }, 'justice policy': { en: { mot: 'Justice motions address crime, sentencing, and policing — areas with high public salience where opposition parties frequently press for tougher or more targeted measures.', bet: 'The Justice Committee shapes the criminal law framework; its reports on sentencing and policing directly affect prosecution practice and enforcement priorities.', - ip: 'This interpellation forces the minister to defend justice policy decisions in the chamber, addressing public concerns about crime, sentencing, or policing effectiveness.', + ip: 'This interpellation holds the justice minister accountable on crime, sentencing, or policing — demanding specific answers on policy outcomes or enforcement gaps.', default: 'Justice proposals balance rule-of-law principles, human rights obligations, and public safety demands — requiring careful drafting to withstand constitutional scrutiny.' }, sv: { mot: 'Rättsliga motioner rör brott, straff och polis – frågor med hög allmän relevans där oppositionen ofta driver på för hårdare åtgärder.', bet: 'Justitieutskottet formar den straffrättsliga ramen; dess betänkanden om straffsatser och polisverksamhet påverkar direkt åklagarnas praxis.', - ip: 'Interpellationen tvingar ministern att försvara rättspolitiska beslut i kammaren och bemöta allmänhetens oro om brottslighet, straffsatser eller polisens effektivitet.', + ip: 'Denna interpellation ställer justitieministern till svars för brotts-, straff- eller polisfrågor och kräver specifika svar om policyresultat eller verkställighetsbrister.', default: 'Rättsliga propositioner balanserar rättsstatsprinciper, mänskliga rättigheter och allmän säkerhet.' }, - de: { default: `Justizvorschläge balancieren Rechtsstaatsprinzipien, Menschenrechtsverpflichtungen und öffentliche Sicherheitsforderungen — sorgfältige Ausarbeitung ist erforderlich, um der verfassungsrechtlichen Prüfung standzuhalten.` }, - fr: { default: `Les propositions de justice équilibrent les principes de l'État de droit, les obligations en matière de droits de l'homme et les exigences de sécurité publique.` }, - es: { default: `Las propuestas de justicia equilibran los principios del Estado de derecho, las obligaciones de derechos humanos y las demandas de seguridad pública — requiriendo una redacción cuidadosa para resistir el escrutinio constitucional.` } + de: { default: 'Justizvorschläge balancieren Rechtsstaatsprinzipien, Menschenrechtsverpflichtungen und öffentliche Sicherheitsforderungen — sorgfältige Ausarbeitung ist erforderlich, um der verfassungsrechtlichen Prüfung standzuhalten.' }, + fr: { default: 'Les propositions de justice équilibrent les principes de l\'État de droit, les obligations en matière de droits de l\'homme et les exigences de sécurité publique.' }, + es: { default: 'Las propuestas de justicia equilibran los principios del Estado de derecho, las obligaciones de derechos humanos y las demandas de seguridad pública — requiriendo una redacción cuidadosa para resistir el escrutinio constitucional.' }, }, 'labour market policy': { en: { mot: 'Labour market motions engage sensitive negotiations between employers, unions, and the state — every motion sends a signal to Sweden\'s social partners.', bet: 'The Labour Committee\'s reports on workplace legislation must navigate collective bargaining autonomy while setting minimum statutory floors.', - ip: 'This interpellation holds the minister accountable on employment conditions, wage policy, or labour market regulation, demanding answers before the social partners and the public.', + ip: 'This interpellation demands government accountability on labour market policy, pressing the minister to explain employment outcomes, working conditions, or regulatory changes.', default: 'Labour market proposals enter an arena where tripartite negotiation shapes the final legislative outcome as much as parliamentary votes.' }, sv: { mot: 'Arbetsmarknadsmotioner engagerar känsliga förhandlingar mellan arbetsgivare, fackförbund och stat – varje motion signalerar till parterna.', bet: 'Arbetsmarknadsutskottets betänkanden om arbetsplatslagar måste navigera kollektivavtalens självständighet.', - ip: 'Interpellationen ställer ministern till svars för anställningsvillkor, lönepolitik eller arbetsmarknadsreglering och kräver svar inför arbetsmarknadens parter och allmänheten.', + ip: 'Denna interpellation kräver regeringens ansvarighet i arbetsmarknadspolitiken och pressar ministern att förklara sysselsättningsresultat, arbetsvillkor eller regeländringar.', default: 'Arbetsmarknadspropositioner träder in i en arena där trepartsförhandlingar formar det slutliga lagstiftningsresultatet.' }, - de: { default: `Arbeitsmarktvorschläge betreten eine Arena, in der Tarifverhandlungen das Gesetzgebungsergebnis ebenso stark prägen wie Parlamentsabstimmungen.` }, - fr: { default: `Les propositions sur le marché du travail entrent dans une arène où la négociation tripartite détermine le résultat législatif autant que les votes parlementaires.` }, - es: { default: `Las propuestas del mercado laboral entran en una arena donde la negociación tripartita determina el resultado legislativo tanto como los votos parlamentarios.` } + de: { default: 'Arbeitsmarktvorschläge betreten eine Arena, in der Tarifverhandlungen das Gesetzgebungsergebnis ebenso stark prägen wie Parlamentsabstimmungen.' }, + fr: { default: 'Les propositions sur le marché du travail entrent dans une arène où la négociation tripartite détermine le résultat législatif autant que les votes parlementaires.' }, + es: { default: 'Las propuestas del mercado laboral entran en una arena donde la negociación tripartita determina el resultado legislativo tanto como los votos parlamentarios.' }, }, 'housing policy': { en: { mot: 'Housing motions reflect structural tension between demand for affordable homes and constraints of planning law, rent regulation, and construction cost pressures.', bet: 'The Civil Affairs Committee\'s housing reports address one of Sweden\'s most persistent policy challenges, where committee decisions unlock or block major regulatory change.', - ip: 'This interpellation presses the minister to account for housing supply shortages, rent regulation, or construction barriers — issues with direct impact on household welfare.', + ip: 'This interpellation scrutinises government housing policy, demanding the minister address affordability crises, construction targets, or rent regulation gaps.', default: 'Housing proposals must reconcile competing interests from municipalities, property owners, tenants, and developers — a coalition rarely achieved quickly.' }, sv: { mot: 'Bostadsmotioner speglar strukturell spänning mellan efterfrågan på prisvärda bostäder och begränsningarna i plan- och hyreslagstiftning.', bet: 'Civilutskottets bostadsbetänkanden hanterar en av Sveriges mest ihållande politiska utmaningar.', - ip: 'Interpellationen pressar ministern att svara för bostadsbrist, hyresreglering eller bygghinder – frågor med direkt påverkan på hushållens välfärd.', + ip: 'Denna interpellation granskar regeringens bostadspolitik och kräver att ministern besvarar frågor om tillgänglighetskriser, byggmål eller brister i hyresreglering.', default: 'Bostadspropositioner måste balansera konkurrerande intressen från kommuner, fastighetsägare, hyresgäster och byggföretag.' }, - de: { default: `Wohnungsvorschläge müssen konkurrierende Interessen von Gemeinden, Eigentümern, Mietern und Bauträgern in Einklang bringen.` }, - fr: { default: `Les propositions de logement doivent concilier les intérêts concurrents des municipalités, propriétaires, locataires et promoteurs.` }, - es: { default: `Las propuestas de vivienda deben conciliar los intereses en competencia de municipios, propietarios, inquilinos y promotores.` } + de: { default: 'Wohnungsvorschläge müssen konkurrierende Interessen von Gemeinden, Eigentümern, Mietern und Bauträgern in Einklang bringen.' }, + fr: { default: 'Les propositions de logement doivent concilier les intérêts concurrents des municipalités, propriétaires, locataires et promoteurs.' }, + es: { default: 'Las propuestas de vivienda deben conciliar los intereses en competencia de municipios, propietarios, inquilinos y promotores.' }, }, 'transport policy': { en: { mot: 'Transport motions address infrastructure investment, road safety, and public transit — areas where regional and national interests frequently diverge.', bet: 'The Transport Committee\'s reports guide Sweden\'s national infrastructure planning cycle, directly affecting long-term investment priorities.', - ip: 'This interpellation challenges the minister on transport infrastructure, road safety, or public transit decisions, demanding transparency on investment priorities and regional equity.', + ip: 'This interpellation demands accountability on transport policy, pressing the minister to justify infrastructure priorities, maintenance backlogs, or transit funding decisions.', default: 'Transport proposals engage the national infrastructure budget, regional equity, and climate transition targets — all must be balanced in committee deliberation.' }, sv: { mot: 'Transportmotioner rör infrastrukturinvesteringar, trafiksäkerhet och kollektivtrafik – frågor där regionala och nationella intressen ofta divergerar.', bet: 'Trafikutskottets betänkanden vägleder Sveriges nationella infrastrukturplanering och påverkar direkt långsiktiga investeringsprioriteringar.', - ip: 'Interpellationen utmanar ministern om transportinfrastruktur, trafiksäkerhet eller kollektivtrafik och kräver insyn i investeringsprioriteringar och regional rättvisa.', + ip: 'Denna interpellation kräver ansvarighet i transportpolitiken och pressar ministern att motivera infrastrukturprioriteringar, underhållsskulder eller kollektivtrafikfinansiering.', default: 'Transportpropositioner engagerar den nationella infrastrukturbudgeten, regional jämlikhet och klimatomställningsmål.' }, - de: { default: `Verkehrsvorschläge betreffen den nationalen Infrastrukturhaushalt, regionale Gerechtigkeit und Klimaübergangsziele.` }, - fr: { default: `Les propositions de transport concernent le budget national d'infrastructure, l'équité régionale et les objectifs de transition climatique.` }, - es: { default: `Las propuestas de transporte implican el presupuesto nacional de infraestructura, la equidad regional y los objetivos de transición climática.` } + de: { default: 'Verkehrsvorschläge betreffen den nationalen Infrastrukturhaushalt, regionale Gerechtigkeit und Klimaübergangsziele.' }, + fr: { default: 'Les propositions de transport concernent le budget national d\'infrastructure, l\'équité régionale et les objectifs de transition climatique.' }, + es: { default: 'Las propuestas de transporte implican el presupuesto nacional de infraestructura, la equidad regional y los objetivos de transición climática.' }, }, 'trade and industry policy': { en: { mot: 'Industry and trade motions often target competitiveness, innovation, or trade agreements — signalling party positions ahead of EU-level or bilateral negotiations.', bet: 'The Committee on Industry and Trade shapes Sweden\'s business environment through reports that set conditions for investment, innovation, and exports.', - ip: 'This interpellation holds the minister accountable on trade, competitiveness, or industrial strategy, pressing for justification of policy choices that affect Sweden\'s economic positioning.', + ip: 'This interpellation demands government accountability on trade and industry, pressing the minister to explain competitiveness measures, innovation spending, or trade negotiation outcomes.', default: 'Industry and trade proposals engage international commitments, EU single-market rules, and domestic competitiveness imperatives simultaneously.' }, sv: { mot: 'Näringspolitiska motioner riktar sig ofta mot konkurrenskraft, innovation eller handelsavtal och signalerar partipositioner inför förhandlingar.', bet: 'Näringsutskottets betänkanden formar Sveriges affärsmiljö och sätter villkoren för investeringar och export.', - ip: 'Interpellationen ställer ministern till svars för handels-, konkurrens- eller industripolitik och kräver motivering av de policyval som påverkar Sveriges ekonomiska ställning.', + ip: 'Denna interpellation kräver regeringens ansvarighet i närings- och handelspolitiken och pressar ministern att förklara konkurrenskraftsåtgärder, innovationssatsningar eller handelsförhandlingsresultat.', default: 'Näringspolitiska propositioner engagerar internationella åtaganden, EU:s inre marknadsregler och inhemsk konkurrenskraft.' }, - de: { default: `Industrie- und Handelsvorschläge berühren gleichzeitig internationale Verpflichtungen, EU-Binnenmarktregeln und inländische Wettbewerbserfordernisse.` }, - fr: { default: `Les propositions industrielles et commerciales engagent simultanément des engagements internationaux, les règles du marché unique européen et les impératifs de compétitivité nationale.` }, - es: { default: `Las propuestas industriales y comerciales comprometen simultáneamente compromisos internacionales, normas del mercado único europeo e imperativos de competitividad nacional.` } + de: { default: 'Industrie- und Handelsvorschläge berühren gleichzeitig internationale Verpflichtungen, EU-Binnenmarktregeln und inländische Wettbewerbserfordernisse.' }, + fr: { default: 'Les propositions industrielles et commerciales engagent simultanément des engagements internationaux, les règles du marché unique européen et les impératifs de compétitivité nationale.' }, + es: { default: 'Las propuestas industriales y comerciales comprometen simultáneamente compromisos internacionales, normas del mercado único europeo e imperativos de competitividad nacional.' }, }, 'education policy': { en: { mot: 'Education motions reflect deep disagreements on school standards, teacher pay, and the role of independent schools — one of Sweden\'s most contested domestic debates.', bet: 'The Education Committee\'s reports directly shape curriculum standards, funding formulas, and school regulation — decisions with long generational consequences.', - ip: 'This interpellation demands the minister explain education policy choices in open debate — whether on school standards, teacher recruitment, or the independent school framework.', + ip: 'This interpellation scrutinises education policy, pressing the minister to account for school quality, teacher shortages, or the regulation of independent schools.', default: 'Education proposals must balance national curriculum standards with municipal delivery autonomy and the contested role of private providers in the Swedish school system.' }, sv: { mot: 'Utbildningsmotioner speglar djupa meningsskiljaktigheter om skolstandard, lärarlöner och friskolornas roll.', bet: 'Utbildningsutskottets betänkanden formar direkt läroplaner, finansieringsmodeller och skolreglering.', - ip: 'Interpellationen kräver att ministern förklarar utbildningspolitiska val i öppen debatt — vare sig det gäller skolstandard, lärarrekrytering eller friskolesystemet.', + ip: 'Denna interpellation granskar utbildningspolitiken och kräver att ministern redovisar skolkvalitet, lärarbrist eller regleringen av fristående skolor.', default: 'Utbildningspropositioner måste balansera nationella läroplaner med kommunalt leveransansvar och de privata aktörernas omstridda roll.' }, - de: { default: `Bildungsvorschläge berühren geteilte Zuständigkeiten zwischen Zentralregierung, Kommunen und unabhängigen Schulträgern.` }, - fr: { default: `Les propositions en matière d\'éducation engagent des responsabilités partagées entre le gouvernement central, les municipalités et les opérateurs scolaires indépendants.` }, - es: { default: `Las propuestas educativas comprometen responsabilidades compartidas entre el gobierno central, los municipios y los operadores escolares independientes.` } + de: { default: 'Bildungsvorschläge müssen nationale Lehrplanstandards mit kommunaler Durchführungsautonomie und der umstrittenen Rolle privater Anbieter im schwedischen Schulsystem ausbalancieren.' }, + fr: { default: 'Les propositions éducatives doivent équilibrer les normes nationales de programme avec l\'autonomie de prestation municipale et le rôle controversé des prestataires privés dans le système scolaire suédois.' }, + es: { default: 'Las propuestas educativas deben equilibrar los estándares curriculares nacionales con la autonomía de prestación municipal y el controvertido papel de los proveedores privados en el sistema escolar sueco.' }, } }; @@ -519,19 +519,23 @@ const EN_DOMAIN_MAP: Record = _LOCALISED_TO_EN; /** * Return a substantive domain-specific and type-specific analysis sentence. * Each of 12 policy domains has tailored text for motions (mot), committee - * reports (bet), interpellations (ip), and propositions/default, in both - * English and Swedish. + * reports (bet), interpellations (ip), and propositions/default. English and + * Swedish are always present; other languages fall back per-key to English. */ export function getDomainSpecificAnalysis(primaryDomain: string, doktyp: string, lang: Language | string): string { - const isSv = lang === 'sv'; - const lookupKey = EN_DOMAIN_MAP[primaryDomain] ?? primaryDomain; const entry = DOMAIN_ANALYSES[lookupKey]; if (!entry) return ''; - const langEntry = isSv ? entry.sv : entry.en; + // Per-key fallback: prefer entry[lang][typeKey], else entry.en[typeKey], + // then entry[lang].default, then entry.en.default. + const langKey = lang as Language; const typeKey = (doktyp === 'mot' || doktyp === 'bet' || doktyp === 'ip') ? doktyp : 'default'; - return langEntry[typeKey] ?? langEntry['default'] ?? ''; + const langEntry = entry[langKey]; + if (langEntry?.[typeKey]) return langEntry[typeKey]; + if (entry.en[typeKey]) return entry.en[typeKey]; + if (langEntry?.['default']) return langEntry['default']; + return entry.en['default'] ?? ''; } /** @@ -542,7 +546,7 @@ export function getDomainSpecificAnalysis(primaryDomain: string, doktyp: string, * when no domain keyword matches but the document's organ field identifies a * known Riksdag committee. * @param impliedDoktyp - document type inferred from the calling context - * ('mot', 'bet', 'prop', 'ip') when doc.doktyp / doc.documentType is absent. + * ('mot', 'bet', 'prop') when doc.doktyp / doc.documentType is absent. */ export function generatePolicySignificance(doc: RawDocument, lang: Language | string, impliedDoktyp?: string): string { const domains = detectPolicyDomains(doc, lang); @@ -589,10 +593,9 @@ export function generatePolicySignificance(doc: RawDocument, lang: Language | st } } - // Interpellation-specific generic: references minister scrutiny/chamber debate - // rather than committee processing - const effectiveDoktyp = doc.doktyp || doc.documentType || impliedDoktyp || ''; - if (effectiveDoktyp === 'ip') { + // Interpellation-specific fallback: references minister obligation, not committee review + const doktyp2 = doc.doktyp || doc.documentType || impliedDoktyp || ''; + if (doktyp2 === 'ip') { return lang === 'sv' ? 'Interpellationen debatteras i kammaren där ministern är skyldig att svara och förklara sina beslut.' : 'The interpellation is debated in the chamber, where the minister is obliged to respond and be held accountable.'; diff --git a/scripts/types/content.ts b/scripts/types/content.ts index 9f707a2b1f2..8e3c919d29b 100644 --- a/scripts/types/content.ts +++ b/scripts/types/content.ts @@ -79,6 +79,12 @@ export interface ContentLabelSet { motionsOverview: (n: number) => string; motionSignificance: string; readFullMotion: string; + /** Default summary for an interpellation document entry */ + interpellationDefault: string; + /** "Read the full interpellation" link text */ + readFullInterpellation: string; + /** Party interpellations filed breakdown (e.g. "SD: 3 interpellations filed") */ + partyInterpellationsFiled: (party: string, n: number) => string; policyContext: string; filedBy: string; /** Label for document publication date (e.g. "Published", "Publicerad") */ @@ -96,24 +102,22 @@ export interface ContentLabelSet { committeeBreakdown: (n: number, c: number) => string; propsBreakdown: (n: number) => string; motionsBreakdown: (n: number) => string; - // Interpellations-specific labels /** Empty-state message for interpellations articles */ noInterpellations: string; - interpellationDefault: string; /** Analytical lede for interpellations articles */ interpellationsBreakdown: (n: number) => string; + /** Section heading for debate dynamics */ debateDynamics: string; /** Label for the target minister (recipient), e.g. "Directed to" */ ministerAccountability: string; + /** Section heading for accountability analysis */ accountabilityAnalysis: string; - readFullInterpellation: string; + /** Author attribution for interpellations (e.g. "Interpellation by") */ interpellationBy: string; /** Debate dynamics context — interpellations-specific (replaces oppositionStrategyContext for interpellations) */ interpellationsScrutinyContext: (n: number) => string; /** Accountability analysis context — interpellations-specific (replaces policyImplicationsContext for interpellations) */ interpellationsAccountabilityContext: (count: number, domains: number) => string; - /** Party interpellations filed — interpellations-specific (replaces partyMotionsFiled for interpellations) */ - partyInterpellationsFiled: (party: string, n: number) => string; // Localized body text labels (avoid hardcoded en/sv fallbacks) committeeCountContext: (n: number) => string; committeeActivityTakeaway: (committees: string, extra: number) => string; From d053fd720da447edf3fd9aadfb73b2bf49d24b39 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 18:29:39 +0000 Subject: [PATCH 14/24] =?UTF-8?q?Merge=20branch=20'main'=20into=20copilot/?= =?UTF-8?q?enhance-content-generators-pipeline=20=E2=80=94=20resolve=206-f?= =?UTF-8?q?ile=20conflicts,=20remove=20duplicate=20mottagare=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- news/2026-03-14-weekly-review-ar.html | 297 ++++++++ news/2026-03-14-weekly-review-da.html | 297 ++++++++ news/2026-03-14-weekly-review-de.html | 297 ++++++++ news/2026-03-14-weekly-review-en.html | 26 +- news/2026-03-14-weekly-review-es.html | 297 ++++++++ news/2026-03-14-weekly-review-fi.html | 297 ++++++++ news/2026-03-14-weekly-review-fr.html | 297 ++++++++ news/2026-03-14-weekly-review-he.html | 933 ++++++++++++++++++++++++++ news/2026-03-14-weekly-review-ja.html | 933 ++++++++++++++++++++++++++ news/2026-03-14-weekly-review-ko.html | 933 ++++++++++++++++++++++++++ news/2026-03-14-weekly-review-nl.html | 297 ++++++++ news/2026-03-14-weekly-review-no.html | 297 ++++++++ news/2026-03-14-weekly-review-sv.html | 26 +- news/2026-03-14-weekly-review-zh.html | 933 ++++++++++++++++++++++++++ news/metadata/batch-status.json | 2 +- news/metadata/last-generation.json | 34 +- styles.css | 118 ++++ 17 files changed, 6292 insertions(+), 22 deletions(-) create mode 100644 news/2026-03-14-weekly-review-ar.html create mode 100644 news/2026-03-14-weekly-review-da.html create mode 100644 news/2026-03-14-weekly-review-de.html create mode 100644 news/2026-03-14-weekly-review-es.html create mode 100644 news/2026-03-14-weekly-review-fi.html create mode 100644 news/2026-03-14-weekly-review-fr.html create mode 100644 news/2026-03-14-weekly-review-he.html create mode 100644 news/2026-03-14-weekly-review-ja.html create mode 100644 news/2026-03-14-weekly-review-ko.html create mode 100644 news/2026-03-14-weekly-review-nl.html create mode 100644 news/2026-03-14-weekly-review-no.html create mode 100644 news/2026-03-14-weekly-review-zh.html diff --git a/news/2026-03-14-weekly-review-ar.html b/news/2026-03-14-weekly-review-ar.html new file mode 100644 index 00000000000..6591d0bd3c3 --- /dev/null +++ b/news/2026-03-14-weekly-review-ar.html @@ -0,0 +1,297 @@ + + + + + + + أسبوع في السياسة السويدية: تشديد شروط الجنسية، التوسع النووي، وإصلاح الهجرة + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← العودة إلى الأخبار + +
    + +
    +
    + + + +
    آخر الأخبار والتحليلات من البرلمان السويدي (Riksdag). استخبارات سياسية مولّدة بالذكاء الاصطناعي استناداً إلى بيانات OSINT/INTOP تغطي البرلمان والحكومة والوكالات بشفافية منهجية.
    +

    أسبوع في السياسة السويدية: تشديد شروط الجنسية، التوسع النووي، وإصلاح الهجرة

    + +
    + +
    +

    أبرز النقاط

    +
      +
    • تشديد شروط الجنسية: يقدم الاقتراح الحكومي 2025/26:175 متطلبات أكثر صرامة بشكل ملحوظ للحصول على الجنسية السويدية — وهو الإصلاح الأشمل لقانون الجنسية منذ عقود
    • +
    • حظر زواج الأقارب: يقترح الاقتراح الحكومي 2025/26:154 حظر الزواج بين أبناء العمومة والأقارب الآخرين، موسعاً موانع الزواج القائمة في السويد
    • +
    • التوسع النووي: ثلاثة اقتراحات حكومية متعلقة بالطاقة النووية (160، 167، 168) تشير إلى رهان الحكومة الكامل على مواقع ساحلية جديدة للطاقة النووية وتحديث لوائح السلامة
    • +
    • إصلاح هجرة العمالة: يوصي تقرير اللجنة SfU12 بالموافقة على قواعد جديدة شاملة لهجرة العمالة، بما في ذلك حدود الرواتب وجرائم جديدة لاستغلال العمال
    • +
    • 15 تقرير لجان: قدمت اللجان قرارات عبر الشؤون الدستورية (KU)، والمالية (FiU)، والتأمينات الاجتماعية (SfU)، والبيئة (MJU)، والنقل (TU)، والشؤون المدنية (CU)
    • +
    +
    + +
    +

    القصة الرئيسية: الجنسية والهوية في صميم النقاش السياسي

    +

    وضعت حكومة Kristersson الجنسية وقانون الأسرة في صميم أجندتها التشريعية قبيل الانتخابات هذا الأسبوع، حيث قدمت اقتراحين حكوميين يمثلان معاً أهم التغييرات في القانون الشخصي السويدي منذ جيل.

    +

    يقدم الاقتراح الحكومي 2025/26:175، الذي عرضه وزير الهجرة Johan Forssell في 12 مارس، متطلبات أكثر صرامة بشكل كبير للحصول على الجنسية السويدية. يشير الإصلاح إلى موقف الحكومة المتشدد بشأن الاندماج، وهو مصمم لضمان أن الجنسية تعكس ارتباطاً مجتمعياً حقيقياً — وهو مطلب سياسي طالما دافع عنه SD ويُنفّذ الآن من قبل ائتلاف Tidö.

    +

    في اليوم نفسه، قدم وزير العدل Gunnar Strömmer الاقتراح الحكومي 2025/26:154، الذي من شأنه حظر الزواج بين أبناء العمومة والأقارب الآخرين بما يتجاوز الحظر الحالي بين الأشقاء. أطّرت الحكومة هذا الإجراء كحماية للحقوق الفردية ومكافحة لثقافة الشرف، وهو إطار تعترض عليه أحزاب المعارضة التي تدعو إلى مقاربات أكثر دقة.

    +

    يؤكد هذان الاقتراحان استراتيجية الحكومة في تعزيز الإصلاحات المحافظة اجتماعياً بوتيرة سريعة مع اقتراب انتخابات سبتمبر 2026، مما يخلق خطوط معركة أيديولوجية واضحة ضد الاشتراكيين الديمقراطيين وحزب البيئة.

    +
    + +
    +

    النتائج التشريعية: 15 تقرير لجان عبر ستة مجالات سياسية

    +

    قدمت اللجان 15 betänkanden (تقريراً) هذا الأسبوع، تغطي نطاقاً واسعاً بشكل استثنائي من المجالات السياسية. حمل العديد منها ثقلاً تشريعياً كبيراً:

    +

    إصلاح هجرة العمالة (SfU12)

    +

    يوصي تقرير لجنة التأمينات الاجتماعية حول قواعد هجرة العمالة الجديدة بأن يوافق البرلمان على أشمل إصلاح حكومي لتصاريح العمل منذ سنوات. تشمل الإجراءات الرئيسية حدوداً إلزامية للرواتب، واشتراط تأمين صحي شامل، وجرائم جديدة لاستغلال العمال الأجانب والاتجار بتصاريح العمل، وتمديد صلاحية البطاقة الزرقاء الأوروبية. تدخل الإصلاحات حيز التنفيذ في 1 يونيو 2026.

    +

    مكافحة الأنواع الغازية (MJU13)

    +

    توصي لجنة البيئة والزراعة بتعزيز اللوائح ضد الأنواع الغازية الدخيلة. بموجب القواعد الجديدة، يصبح إدخال الأنواع الغازية عمداً أو إهمالاً إلى السويد من دولة أوروبية معاقباً بالسجن حتى عامين. يحصل Tullverket على صلاحيات التفتيش عند الحدود الداخلية. يدخل القانون حيز التنفيذ في 1 مايو 2026.

    +

    صلاحيات مراقبة الكلاب (MJU15)

    +

    ستحصل المجالس الإدارية في المحافظات على أدوات موسعة للإشراف على الكلاب، بما في ذلك صلاحية دخول المنازل للتفتيش وسلطة أوسع للمصادرة الفورية للكلاب الخطرة. يمكن للشرطة أيضاً أن تأمر بإعدام الحيوانات المصادرة في حالات معينة. يدخل حيز التنفيذ في 1 مايو 2026.

    +

    الشؤون الدستورية والإدارية (KU35–37)

    +

    قدمت اللجنة الدستورية ثلاثة تقارير: حول الاجتماعات البلدية الرقمية (KU35)، ومراجعة الخصوصية والتكنولوجيا الجديدة 2020–2024 (KU36)، وتركيز أنشطة مجالس المحافظات (KU37). يقترح تقرير الاجتماعات الرقمية تمكين مشاركة أكثر مرونة عن بُعد في الحكم المحلي.

    +

    الأسواق المالية والحوكمة البلدية (FiU22، FiU25، FiU26، FiU34)

    +

    عالجت لجنة المالية أربعة تقارير. يتناول FiU22 أنشطة صندوق النقد الدولي لعام 2025 واستقرار الأسواق المالية. رفض FiU26 تسعة وستين اقتراحاً حول القضايا البلدية. غطى FiU34 المشتريات العامة، وتناول FiU25 الإدارة الحكومية.

    +

    سياسة الأسرة والتأمينات الاجتماعية (SfU10، SfU17)

    +

    رفض SfU17 مئة وأربعة اقتراحات حول السياسة الاقتصادية للأسرة — تشمل التأمين الوالدي وإعانات الأطفال وبدلات السكن. تناول SfU10 حماية التأمينات الاجتماعية للعمال المتنقلين دولياً.

    +
    + +
    +

    مراقبة الحكومة: طموحات نووية وتعميق دفاعي

    +

    بالإضافة إلى الجنسية وقانون الأسرة، دفعت الحكومة بأجندة طاقة كبرى وعززت الوضع الدفاعي للسويد هذا الأسبوع:

    +
      +
    • الثلاثية النووية (اقتراحات 160، 167، 168، 171): أربعة اقتراحات حكومية متعلقة بالطاقة النووية في أسبوع واحد. يفتح الاقتراح 160 مواقع ساحلية جديدة لمحطات الطاقة النووية، بينما تحدّث الاقتراحات 167 و168 و171 الرقابة على المواد النووية ومتطلبات السلامة والإشعاع وعملية المراجعة.
    • +
    • تقرير أنشطة الاتحاد الأوروبي (Skr. 115): قُدمت المراجعة السنوية للحكومة للاتحاد الأوروبي في 12 مارس، تغطي مشاركة السويد في الاتحاد الأوروبي خلال 2025.
    • +
    • نتائج المساعدات لأوكرانيا (Skr. 97): قُدم تقرير مفصل عن نتائج المساعدات الدولية السويدية لأوكرانيا.
    • +
    • فحوصات خلفية المدارس (اقتراح 174): فحوصات سجلات موسعة لموظفي المدارس، تعكس أجندة الحكومة الأمنية في التعليم.
    • +
    • إصلاح عمليات البناء (اقتراح 172): تبسيط عمليات البناء لزيادة الكفاءة والسلامة.
    • +
    • دقة بدل السكن (اقتراح 170): حسابات أكثر استهدافاً ودقة لإعانات السكن.
    • +
    • توزيع فائض المعاشات (اقتراح 169): قواعد لتوزيع الفائض من نظام معاشات الدخل.
    • +
    • التعاون الدفاعي الصناعي مع أوكرانيا: أعلنت السويد عن تعزيز التعاون الدفاعي الصناعي مع أوكرانيا، وشارك وزير الدفاع المدني Carl-Oskar Bohlin في تمرين للدفاع الشامل في ستوكهولم.
    • +
    +

    نُشرت أيضاً ثلاث تحقيقات رسمية (SOU): حول الخث الزراعي والمناخ (SOU 2026:18)، ومبادرات تصدير الدفاع (SOU 2026:16)، ومكافحة وصف الأدوية غير المشروعة (SOU 2026:19).

    +
    + +
    +

    مناقشات القاعة: شفافية الميزانية وإيران والاقتصاد الدائري

    +

    عكست مناقشات القاعة هذا الأسبوع توترات السياسة الخارجية والمساءلة المالية المحلية:

    +

    إيران والدعم الديمقراطي

    +

    أجابت وزيرة الخارجية Maria Malmer Stenergard (M) على استجواب من Daniel Riazat (مستقل، سابقاً V) حول دعم الحكومة لانتفاضة الشعب الإيراني ضد النظام. شارك أيضاً Nima Gholam Ali Pour من SD، مما خلق ديناميكية عابرة للأحزاب غير معتادة.

    +

    آثار الميزانية والشفافية المالية

    +

    واجهت وزيرة المالية Elisabeth Svantesson (M) أسئلة مستمرة من Peder Björk (S) حول الإبلاغ عن آثار الميزانية — مسلطة الضوء على استراتيجية المعارضة في محاسبة الحكومة على الشفافية الاقتصادية قبيل انتخابات 2026.

    +

    ضريبة القيمة المضافة على مواقف السيارات والاقتصاد البلدي

    +

    ناقشت Svantesson أيضاً Marie Olsson (S) حول ضريبة القيمة المضافة على مواقف السيارات — قضية ذات تأثيرات كبيرة على المالية البلدية واقتصاد العقارات التجارية.

    +

    الاقتصاد الدائري والخالي من السموم

    +

    شهدت المناقشة المستمرة حول MJU12 مشاركة من Katarina Luhr (MP)، وStina Larsson (C)، وAndrea Andersson Tay (V)، وLars Johnsson (M) — مما يعكس الأهمية السياسية المتزايدة للاستدامة وسلامة المواد الكيميائية.

    +
    + +
    +

    ديناميكيات المعارضة: التأهل للتأمينات الاجتماعية يصبح ساحة المعركة

    +

    تركز أكثر نشاط معارض حدة حول الاقتراح الحكومي 2025/26:136 بشأن التأهل للتأمينات الاجتماعية. قدمت أربعة أحزاب معارضة اقتراحات منفصلة:

    +
      +
    • الاشتراكيون الديمقراطيون (S): الاقتراح 3936 من Ida Karkiainen يرفض جزئياً المشروع، ويقبل شرط الإقامة القانونية مع رفض قيود التأهيل الأوسع
    • +
    • حزب اليسار (V): الاقتراح 3932 من Tony Haddou يطالب بالرفض الكامل
    • +
    • حزب البيئة (MP): الاقتراح 3942 من Malte Tängmark Roos يدعو أيضاً إلى الرفض الكامل
    • +
    • حزب الوسط (C): الاقتراح 3939 من Anders W Jonsson يسعى لتعديل استثناءات التأهيل
    • +
    +

    تمثل جبهة المعارضة الرباعية هذه حول التأهل للرعاية الاجتماعية اصطفافاً سياسياً مهماً. واصل الاشتراكيون الديمقراطيون هجومهم بالاستجوابات مع التركيز على رعاية المسنين (ثلاث استجوابات منفصلة من Lundh Sammeli وDahlqvist وSundin موجهة للوزيرة Tenje)، والأولويات الاقتصادية، والإغراق الاجتماعي بين البلديات. تشير ثلاثية رعاية المسنين هذه إلى نية S جعل جودة الرعاية قضية انتخابية حاسمة في 2026.

    +

    تحدت Ulrika Westerlund (MP) الأسس القانونية لأجندة الحكومة الجنائية في الاستجواب 382، بينما استهدفت Annika Hirvonen (MP) سياسة ترحيل المراهقين في الاستجواب 381.

    +
    + +
    +

    ما كان الأهم: سباق التشريع قبيل الانتخابات

    +

    يكشف الحجم الهائل ونطاق الإنتاج التشريعي لهذا الأسبوع عن حكومة في وضع سباق كامل قبل الانتخابات. مع اقتراب انتخابات سبتمبر 2026 على بعد ستة أشهر تقريباً، يدفع ائتلاف Kristersson بأولوياته التشريعية المتبقية بوتيرة غير مسبوقة — إصلاح الجنسية وإصلاح الهجرة والتوسع النووي وقانون الأسرة المحافظ اجتماعياً في أسبوع واحد.

    +

    تشير استجابة المعارضة المنسقة بشأن التأهل للتأمينات الاجتماعية إلى أن خطوط معركة حملة 2026 تتصلب: ائتلاف اليمين يبني على الهجرة والجنسية والأمن؛ والمعارضة تدافع عن شمولية الرعاية الاجتماعية والحريات المدنية. يضيف هجوم رعاية المسنين بُعداً محلياً في تقديم الخدمات قد يثبت فعاليته انتخابياً.

    +

    والأكثر أهمية من حيث العواقب، تمثل الثلاثية النووية التزاماً سياسياً لا رجعة فيه. إن فتح مواقع ساحلية جديدة للطاقة النووية، إلى جانب تحديث عمليات السلامة والمراجعة، يضع السويد كواحدة من أكثر الديمقراطيات الأوروبية دعماً للطاقة النووية — موقف له تداعيات على أسواق الطاقة وسياسة المناخ والتوجه الجيوسياسي لعقود قادمة.

    +
    + +
    +

    نظرة مستقبلية

    +

    سيشهد الأسبوع المقبل (16–20 مارس) معالجة البرلمان لعدة تقارير لجان هذا الأسبوع في تصويتات جلسات عامة. تابعوا مناقشة إصلاح هجرة العمالة (SfU12)، التي من المرجح أن تولّد مواجهات عابرة للأحزاب. سيواصل اقتراح التأهل للتأمينات الاجتماعية (اقتراح 136) رحلته في اللجان مع اقتراحات معارضة من أربعة أحزاب تضمن عملية خلافية.

    +

    سيُحال اقتراح الحكومة بشأن الجنسية (اقتراح 175) إلى لجنة التأمينات الاجتماعية، حيث يُتوقع أن تقدم المعارضة المجتمعة مقاومة قوية. في غضون ذلك، تتجه الاقتراحات النووية إلى اللجان ذات الصلة حيث من المرجح جدولة جلسات استماع تقنية.

    +

    على صعيد الحكومة، تشير زيارة وزيرة الخارجية Malmer Stenergard إلى تكساس وفرجينيا إلى استمرار التواصل مع الولايات المتحدة، بينما يُتوقع المزيد من مبادرات السياسة التجارية الأوروبية بعد اجتماعات المفوض Šefçoviç مع الوزير Dousa.

    +
    + +
    +

    مصادر البيانات

    +

    يستند هذا التحليل إلى بيانات من واجهة البيانات المفتوحة للبرلمان السويدي (data.riksdagen.se) ومكاتب الحكومة عبر g0v.se، وتغطي الفترة من 7 إلى 14 مارس 2026. جميع الوثائق البرلمانية والتصويتات والخطابات المشار إليها متاحة للعموم عبر أرشيف وثائق البرلمان. Riksmöte: 2025/26.

    +
    + +
    + +
    +

    نشرته Riksdagsmonitor — استخبارات سياسية مولّدة بالذكاء الاصطناعي استناداً إلى بيانات OSINT/INTOP من واجهات البيانات المفتوحة للبرلمان والحكومة السويدية.

    +

    © 2026 Hack23 AB. Licensed under CC BY-SA 4.0.

    +
    + + + + \ No newline at end of file diff --git a/news/2026-03-14-weekly-review-da.html b/news/2026-03-14-weekly-review-da.html new file mode 100644 index 00000000000..7ec0a357027 --- /dev/null +++ b/news/2026-03-14-weekly-review-da.html @@ -0,0 +1,297 @@ + + + + + + + Ugen i svensk politik: Skærpede medborgerskabskrav, atomkraftekspansion og indvandringsreform + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← Tilbage til nyheder + +
    + +
    +
    + + + +
    Seneste nyheder og analyser fra Sveriges Riksdag. AI-genereret politisk efterretningsjournalistik baseret på OSINT/INTOP-data om parlament, regering og myndigheder med systematisk gennemsigtighed.
    +

    Ugen i svensk politik: Skærpede medborgerskabskrav, atomkraftekspansion og indvandringsreform

    + +
    + +
    +

    Vigtigste pointer

    +
      +
    • Skærpede medborgerskabskrav: Proposition 2025/26:175 indfører markant strengere krav til svensk medborgerskab — den mest omfattende reform af medborgerskabsloven i årtier
    • +
    • Forbud mod fætter-kusine-ægteskaber: Proposition 2025/26:154 foreslår at forbyde ægteskaber mellem fætre og kusiner samt andre nære slægtninge
    • +
    • Atomkraftekspansion: Tre atomkraftrelaterede propositioner (160, 167, 168) signalerer regeringens satsning på nye kystbaserede atomkraftværker og moderniserede sikkerhedsregler
    • +
    • Reform af arbejdskraftindvandring: Udvalgsrapport SfU12 anbefaler godkendelse af nye regler for arbejdskraftindvandring med løngrænser og nye straffebestemmelser mod udnyttelse af arbejdere
    • +
    • 15 udvalgsrapporter: Udvalgene leverede beslutninger inden for forfatningsret (KU), finanser (FiU), socialforsikring (SfU), miljø (MJU), transport (TU) og civilret (CU)
    • +
    +
    + +
    +

    Hovedhistorie: Medborgerskab og identitet i centrum af den politiske debat

    +

    Kristersson-regeringen placerede medborgerskab og familieret i centrum af sin lovgivningsdagsorden i denne uge og præsenterede to propositioner, der tilsammen repræsenterer de mest markante ændringer i svensk personret i en generation.

    +

    Proposition 2025/26:175, fremlagt den 12. marts af migrationsminister Johan Forssell, indfører markant strengere krav for at opnå svensk medborgerskab. Reformen signalerer regeringens hårde kurs på integrationsområdet og er designet til at sikre, at medborgerskab afspejler reel samfundsmæssig tilknytning — et politisk krav længe fremsat af SD og nu gennemført af Tidö-koalitionen.

    +

    Samme dag præsenterede justitsminister Gunnar Strömmer Proposition 2025/26:154, som vil forbyde ægteskaber mellem fætre og kusiner samt andre nære slægtninge ud over det nuværende søskendeforbud. Regeringen præsenterede foranstaltningen som beskyttelse af individuelle rettigheder og bekæmpelse af æresrelateret kultur.

    +

    Disse to propositioner understreger regeringens strategi med at fremme socialt konservative reformer i høj fart, mens valget i september 2026 nærmer sig, og skaber tydelige ideologiske kamplinjer mod Socialdemokraterne og Miljöpartiet.

    +
    + +
    +

    Lovgivningsmæssige resultater: 15 udvalgsrapporter på tværs af seks politikområder

    +

    Udvalgene leverede 15 betänkanden (rapporter) i denne uge, der dækkede et ekstraordinært bredt spektrum af politikområder. Flere havde betydelig lovgivningsmæssig vægt:

    +

    Reform af arbejdskraftindvandring (SfU12)

    +

    Socialforsikringsudvalgets rapport om nye regler for arbejdskraftindvandring anbefaler, at Riksdagen godkender regeringens mest omfattende reform af arbejdstilladelser i årevis. Nøgleelementer inkluderer obligatoriske løngrænser, krav om sundhedsforsikring, nye straffebestemmelser for udnyttelse af udenlandske arbejdere og udvidet EU Blue Card-gyldighed. Reformerne træder i kraft den 1. juni 2026.

    +

    Bekæmpelse af invasive arter (MJU13)

    +

    Miljø- og landbrugsudvalget anbefaler at styrke reguleringen af invasive fremmede arter. Under de nye regler kan forsætlig eller uagtsom indførsel af invasive arter til Sverige fra et EU-land straffes med op til to års fængsel. Tullverket får inspektionsbeføjelser ved indre grænser. Loven træder i kraft den 1. maj 2026.

    +

    Hundekontrol (MJU15)

    +

    Länsstyrelserne får udvidede beføjelser til hundetilsyn, herunder ret til at tilgå boliger for inspektioner og bredere muligheder for øjeblikkelig beslaglæggelse af farlige hunde. Politiet kan også beordre aflivning af beslaglagte dyr i visse tilfælde. Træder i kraft den 1. maj 2026.

    +

    Forfatnings- og forvaltningsret (KU35–37)

    +

    Forfatningsudvalget leverede tre rapporter: om digitale kommunale møder (KU35), gennemgang af privatlivsbeskyttelse og ny teknologi 2020–2024 (KU36) og koncentrationen af länsstyrelsernes aktiviteter (KU37). Rapporten om digitale møder foreslår mere fleksibel fjerndeltagelse i kommunal styring.

    +

    Finansmarkeder og kommunal styring (FiU22, FiU25, FiU26, FiU34)

    +

    Finansudvalget behandlede fire rapporter. FiU22 omhandler IMF's aktiviteter i 2025 og finansiel markedsstabilitet. FiU26 afviste 69 motioner om kommunale spørgsmål. FiU34 dækkede offentlige indkøb, og FiU25 behandlede statsadministration.

    +

    Familiepolitik og socialforsikring (SfU10, SfU17)

    +

    SfU17 afviste 104 motioner om økonomisk familiepolitik — herunder forældreydelser, børnebidrag og boligtilskud. SfU10 behandlede socialforsikringsbeskyttelse for internationalt mobile arbejdstagere.

    +
    + +
    +

    Regeringsøvervågning: Atomkraftambitioner og styrket forsvar

    +

    Ud over medborgerskab og familieret fremrykkede regeringen en større energidagsorden og styrkede Sveriges forsvarsposition denne uge:

    +
      +
    • Atomkraft-tredobling (Prop. 160, 167, 168, 171): Fire atomkraftrelaterede propositioner på en enkelt uge. Prop. 160 åbner nye kystplaceringer for atomkraftværker, mens Prop. 167, 168 og 171 moderniserer kontrollen med nukleære materialer, sikkerheds- og strålingskrav samt godkendelsesprocessen.
    • +
    • EU-aktivitetsrapport (Skr. 115): Regeringens årlige EU-gennemgang blev fremlagt den 12. marts med gennemgang af Sveriges EU-engagement i 2025.
    • +
    • Ukraine-bistandsresultater (Skr. 97): En detaljeret rapport om resultaterne af svensk international bistand til Ukraine blev fremlagt.
    • +
    • Baggrundstjek i skoler (Prop. 174): Udvidede registertjek for skoleansatte, der afspejler regeringens sikkerhedsdagsorden for uddannelse.
    • +
    • Reform af byggeprocesser (Prop. 172): Effektivisering af byggeprocesser for øget effektivitet og sikkerhed.
    • +
    • Boligtilskudsnøjagtighed (Prop. 170): Mere målrettede og korrekte boligstøtteberegninger.
    • +
    • Pensionsoverskudsfordeling (Prop. 169): Regler for fordeling af overskud fra indkomstpensionssystemet.
    • +
    • Forsvarsindustrielt samarbejde med Ukraine: Sverige annoncerede styrket forsvarsindustrielt samarbejde med Ukraine, og civilforsvarsminister Carl-Oskar Bohlin deltog i en totalforsvarøvelse i Stockholm.
    • +
    +

    Tre SOU'er (officielle undersøgelser) blev også offentliggjort: om landbrugstørv og klima (SOU 2026:18), forsvarseksportinitiativer (SOU 2026:16) og bekæmpelse af uretmæssig medicinordinering (SOU 2026:19).

    +
    + +
    +

    Kammerdebatter: Budgettransparens, Iran og den cirkulære økonomi

    +

    Ugens kammerdebatter afspejlede både udenrigspolitiske spændinger og national finansiel ansvarlighed:

    +

    Iran og demokratisk støtte

    +

    Udenrigsminister Maria Malmer Stenergard (M) besvarede en interpellation fra Daniel Riazat (uafhængig, tidligere V) om regeringens støtte til det iranske folks opstand mod regimet. SD's Nima Gholam Ali Pour deltog også, hvilket skabte en usædvanlig tværpolitisk dynamik.

    +

    Budgeteffekter og fiskal transparens

    +

    Finansminister Elisabeth Svantesson (M) blev udsat for vedvarende spørgsmål fra S's Peder Björk om rapportering af budgeteffekter — en del af oppositionens strategi for at holde regeringen ansvarlig for økonomisk transparens forud for valget i 2026.

    +

    Parkerings-moms og kommunal økonomi

    +

    Svantesson debatterede også S's Marie Olsson om moms for parkeringspladser — et emne med betydelige konsekvenser for kommunale finanser og kommerciel ejendomsøkonomi.

    +

    Cirkulær og giftfri økonomi

    +

    Den fortsatte debat om MJU12 involverede engagement fra MP's Katarina Luhr, C's Stina Larsson, V's Andrea Andersson Tay og M's Lars Johnsson — som afspejler den voksende politiske relevans af bæredygtighed og kemikaliesikkerhed.

    +
    + +
    +

    Oppositionsdynamik: Socialforsikringskvalificering bliver slagmarken

    +

    Den mest intense oppositionsaktivitet centrerede sig om Proposition 2025/26:136 om socialforsikringskvalificering. Fire oppositionspartier indgav separate motioner:

    +
      +
    • Socialdemokraterne (S): Motion 3936 af Ida Karkiainen vil delvist afvise propositionen og acceptere lovligt opholdsvilkår, mens bredere kvalifikationsrestriktioner afvises
    • +
    • Vänsterpartiet (V): Motion 3932 af Tony Haddou kræver fuld afvisning
    • +
    • Miljöpartiet (MP): Motion 3942 af Malte Tängmark Roos kræver også komplet afvisning
    • +
    • Centerpartiet (C): Motion 3939 af Anders W Jonsson søger ændringer til kvalifikationsundtagelserne
    • +
    +

    Denne fire-parti-oppositionsfront på velfærdskvalificering repræsenterer en betydelig politisk afstemning. Socialdemokraternes interpellationsoffensiv fortsatte med fokus på ældrepleje (tre separate interpellationer af Lundh Sammeli, Dahlqvist og Sundin rettet mod minister Tenje), økonomiske prioriteter og social dumping mellem kommuner. Denne ældrepleje-triade signalerer, at S har til hensigt at gøre plejekvalitet til et valgkampstema i 2026.

    +

    MP's Ulrika Westerlund udfordrede de juridiske grundlag for regeringens strafferetlige dagsorden i interpellation 382, mens MP's Annika Hirvonen rettede fokus mod teenageudvisningspolitik i interpellation 381.

    +
    + +
    +

    Hvad der betød mest: Den førvalgsmæssige lovgivningssprint

    +

    Den enorme mængde og bredde af ugens lovgivningsoutput afslører en regering i fuld førvalgskampagne. Med valget i september 2026 omkring seks måneder væk presser Kristersson-koalitionen sine resterende lovgivningsprioriteter igennem i et hidtil uset tempo — medborgerskabsreform, indvandringsreform, atomkraftekspansion og socialt konservativ familieret på en enkelt uge.

    +

    Oppositionens koordinerede reaktion på socialforsikringskvalificering antyder, at kampagnefrontlinjerne for 2026 hærdner: højrekoalitionen bygger på migration, medborgerskab og sikkerhed; oppositionen forsvarer velfærdsuniversalitet og borgerlige frihedsrettigheder. Ældreplejeoffensiven tilføjer en hjemlig servicedimension, der kan vise sig valgmæssigt potent.

    +

    Mest konsekvensrigt markerer atomkraft-tredoblingen en uigenkaldelig politisk forpligtelse. Åbningen af nye atomkraftværker ved kysten, kombineret med moderniserede sikkerheds- og godkendelsesprocesser, positionerer Sverige som et af Europas mest pro-atomkraft-demokratier — en holdning med konsekvenser for energimarkeder, klimapolitik og geopolitisk orientering i årtier fremover.

    +
    + +
    +

    Forude

    +

    Næste uge (16.–20. marts) vil Riksdagen behandle flere af ugens udvalgsrapporter i plenarsale afstemninger. Hold øje med debatten om reform af arbejdskraftindvandring (SfU12), som sandsynligvis vil skabe tværpolitiske sammenstød. Socialforsikringskvalificeringsproposition (Prop. 136) fortsætter sin udvalgsrejse med modstående motioner fra fire partier.

    +

    Regeringens medborgerskabsproposition (Prop. 175) bliver henvist til Socialforsikringsudvalget, hvor den samlede opposition forventes at yde kraftig modstand. Atomkraftpropositionerne går videre til de relevante udvalg, hvor tekniske høringer sandsynligvis planlægges.

    +

    På regeringssiden signalerer udenrigsminister Malmer Stenergards besøg i Texas og Virginia fortsat engagement med USA, mens yderligere EU-handelspolitiske initiativer forventes efter kommissær Šefçoviç' møder med minister Dousa.

    +
    + +
    +

    Datakilder

    +

    Denne analyse er baseret på data fra det svenske parlaments åbne data-API (data.riksdagen.se) og Regeringskansliet via g0v.se, der dækker perioden 7.–14. marts 2026. Alle parlamentariske dokumenter, afstemninger og taler er offentligt tilgængelige via Riksdagens dokumentarkiv. Riksmöte: 2025/26.

    +
    + +
    + +
    +

    Udgivet af Riksdagsmonitor — AI-genereret politisk efterretning baseret på OSINT/INTOP-data fra det svenske parlaments og regeringens åbne data-API'er.

    +

    © 2026 Hack23 AB. Licensed under CC BY-SA 4.0.

    +
    + + + + \ No newline at end of file diff --git a/news/2026-03-14-weekly-review-de.html b/news/2026-03-14-weekly-review-de.html new file mode 100644 index 00000000000..22b7b4bdd65 --- /dev/null +++ b/news/2026-03-14-weekly-review-de.html @@ -0,0 +1,297 @@ + + + + + + + Die Woche in der schwedischen Politik: Verschärfte Staatsbürgerschaftsanforderungen, Atomkraftausbau und Einwanderungsreform + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← Zurück zu Nachrichten + +
    + +
    +
    + + + +
    Aktuelle Nachrichten und Analysen aus dem schwedischen Riksdag. KI-generierter politischer Nachrichtendienst basierend auf OSINT/INTOP-Daten über Parlament, Regierung und Behörden mit systematischer Transparenz.
    +

    Die Woche in der schwedischen Politik: Verschärfte Staatsbürgerschaftsanforderungen, Atomkraftausbau und Einwanderungsreform

    + +
    + +
    +

    Wichtigste Erkenntnisse

    +
      +
    • Verschärfte Staatsbürgerschaft: Proposition 2025/26:175 führt deutlich strengere Anforderungen an die schwedische Staatsbürgerschaft ein — die umfassendste Reform des Staatsangehörigkeitsrechts seit Jahrzehnten
    • +
    • Verbot der Cousinenheirat: Proposition 2025/26:154 schlägt ein Verbot von Ehen zwischen Cousins und Cousinen sowie anderen nahen Verwandten vor und erweitert die bestehenden Ehehindernisse Schwedens
    • +
    • Atomkraftausbau: Drei atombezogene Propositionen (160, 167, 168) signalisieren den entschlossenen Einsatz der Regierung für neue Küstenstandorte für Kernkraftwerke und modernisierte Sicherheitsvorschriften
    • +
    • Reform der Arbeitsmigration: Der Ausschussbericht SfU12 empfiehlt die Genehmigung umfassender neuer Regeln für die Arbeitsmigration, einschließlich Gehaltsschwellen und neuer Straftatbestände bei Ausbeutung von Arbeitnehmern
    • +
    • 15 Ausschussberichte: Die Ausschüsse lieferten Entscheidungen in den Bereichen Verfassungsrecht (KU), Finanzen (FiU), Sozialversicherung (SfU), Umwelt (MJU), Verkehr (TU) und Zivilrecht (CU)
    • +
    +
    + +
    +

    Hauptgeschichte: Staatsbürgerschaft und Identität im Zentrum der politischen Debatte

    +

    Die Kristersson-Regierung stellte Staatsbürgerschaft und Familienrecht in dieser Woche ins Zentrum ihrer Gesetzgebungsagenda vor der Wahl und legte zwei Propositionen vor, die zusammen die bedeutendsten Änderungen im schwedischen Personenrecht seit einer Generation darstellen.

    +

    Proposition 2025/26:175, vorgelegt am 12. März von Migrationsminister Johan Forssell, führt deutlich strengere Anforderungen für den Erwerb der schwedischen Staatsbürgerschaft ein. Die Reform signalisiert den harten Kurs der Regierung in der Integrationspolitik und soll sicherstellen, dass die Staatsbürgerschaft eine echte gesellschaftliche Bindung widerspiegelt — eine politische Forderung, die lange von SD vertreten und nun von der Tidö-Koalition umgesetzt wird.

    +

    Am selben Tag legte Justizminister Gunnar Strömmer Proposition 2025/26:154 vor, die Ehen zwischen Cousins und Cousinen sowie anderen nahen Verwandten über das bestehende Geschwisterverbot hinaus verbieten würde. Die Regierung stellte die Maßnahme als Schutz individueller Rechte und Bekämpfung ehrbezogener Kultur dar.

    +

    Diese beiden Propositionen unterstreichen die Strategie der Regierung, sozialkonservative Reformen in hohem Tempo voranzutreiben, während die Wahl im September 2026 näher rückt, und schaffen klare ideologische Kampflinien gegen die Sozialdemokraten und die Grünen.

    +
    + +
    +

    Gesetzgebungsergebnisse: 15 Ausschussberichte in sechs Politikbereichen

    +

    Die Ausschüsse lieferten in dieser Woche 15 betänkanden (Berichte), die ein außergewöhnlich breites Spektrum an Politikbereichen abdeckten. Mehrere hatten erhebliches gesetzgeberisches Gewicht:

    +

    Reform der Arbeitsmigration (SfU12)

    +

    Der Bericht des Sozialversicherungsausschusses über neue Regeln zur Arbeitsmigration empfiehlt dem Riksdag, die umfassendste Reform der Arbeitserlaubnisse seit Jahren zu genehmigen. Zu den Kernmaßnahmen gehören obligatorische Gehaltsschwellen, eine Pflicht zur umfassenden Krankenversicherung, neue Straftatbestände für die Ausbeutung ausländischer Arbeitnehmer und eine erweiterte Gültigkeit der EU Blue Card. Die Reformen treten am 1. Juni 2026 in Kraft.

    +

    Bekämpfung invasiver Arten (MJU13)

    +

    Der Umwelt- und Landwirtschaftsausschuss empfiehlt eine Verschärfung der Vorschriften gegen invasive gebietsfremde Arten. Nach den neuen Regeln kann das vorsätzliche oder fahrlässige Einbringen invasiver Arten nach Schweden aus einem EU-Land mit bis zu zwei Jahren Gefängnis bestraft werden. Tullverket erhält Inspektionsbefugnisse an den Binnengrenzen. Das Gesetz tritt am 1. Mai 2026 in Kraft.

    +

    Hundekontrolle (MJU15)

    +

    Die Länsstyrelser erhalten erweiterte Befugnisse zur Hundeaufsicht, einschließlich des Rechts auf Zugang zu Wohnungen für Inspektionen und breiterer Befugnisse zur sofortigen Beschlagnahme gefährlicher Hunde. Die Polizei kann in bestimmten Fällen auch die Einschläferung beschlagnahmter Tiere anordnen. In Kraft ab 1. Mai 2026.

    +

    Verfassungs- und Verwaltungsrecht (KU35–37)

    +

    Der Verfassungsausschuss legte drei Berichte vor: über digitale Gemeindesitzungen (KU35), eine Überprüfung des Datenschutzes und neuer Technologien 2020–2024 (KU36) sowie die Konzentration der Aktivitäten der Länsstyrelser (KU37). Der Bericht über digitale Sitzungen schlägt eine flexiblere Fernteilnahme an der kommunalen Verwaltung vor.

    +

    Finanzmärkte und Kommunalverwaltung (FiU22, FiU25, FiU26, FiU34)

    +

    Der Finanzausschuss bearbeitete vier Berichte. FiU22 befasst sich mit den Aktivitäten des IWF im Jahr 2025 und der Finanzmarktstabilität. FiU26 lehnte 69 Motionen zu kommunalen Fragen ab. FiU34 behandelte das öffentliche Beschaffungswesen, und FiU25 befasste sich mit der Staatsverwaltung.

    +

    Familienpolitik und Sozialversicherung (SfU10, SfU17)

    +

    SfU17 lehnte 104 Motionen zur wirtschaftlichen Familienpolitik ab — darunter Elternversicherung, Kindergeld und Wohngeld. SfU10 behandelte den Sozialversicherungsschutz für international mobile Arbeitnehmer.

    +
    + +
    +

    Regierungsbeobachtung: Atomkraftambitionen und vertiefte Verteidigung

    +

    Über Staatsbürgerschaft und Familienrecht hinaus trieb die Regierung in dieser Woche eine umfangreiche Energieagenda voran und vertiefte Schwedens Verteidigungsposition:

    +
      +
    • Atomkraft-Dreifachschlag (Prop. 160, 167, 168, 171): Vier atombezogene Propositionen in einer einzigen Woche. Prop. 160 eröffnet neue Küstenstandorte für Kernkraftwerke, während Prop. 167, 168 und 171 die Kontrolle nuklearer Materialien, Sicherheits- und Strahlenschutzanforderungen sowie das Genehmigungsverfahren modernisieren.
    • +
    • EU-Tätigkeitsbericht (Skr. 115): Der jährliche EU-Bericht der Regierung wurde am 12. März vorgelegt und behandelt Schwedens EU-Engagement im Jahr 2025.
    • +
    • Ergebnisse der Ukraine-Hilfe (Skr. 97): Ein detaillierter Bericht über die Ergebnisse der schwedischen internationalen Hilfe für die Ukraine wurde vorgelegt.
    • +
    • Hintergrundüberprüfungen an Schulen (Prop. 174): Erweiterte Registerüberprüfungen für Schulangestellte, die die Sicherheitsagenda der Regierung im Bildungsbereich widerspiegeln.
    • +
    • Reform der Bauprozesse (Prop. 172): Straffung der Bauprozesse für mehr Effizienz und Sicherheit.
    • +
    • Genauigkeit beim Wohngeld (Prop. 170): Präzisere und korrektere Berechnung des Wohngeldes.
    • +
    • Verteilung des Rentenüberschusses (Prop. 169): Regeln für die Verteilung von Überschüssen aus dem Einkommenspensionssystem.
    • +
    • Verteidigungsindustrielle Zusammenarbeit mit der Ukraine: Schweden kündigte eine verstärkte verteidigungsindustrielle Zusammenarbeit mit der Ukraine an, und Zivilschutzminister Carl-Oskar Bohlin nahm an einer Totalverteidigungsübung in Stockholm teil.
    • +
    +

    Drei SOU (offizielle Untersuchungen) wurden ebenfalls veröffentlicht: zu Agrartorf und Klima (SOU 2026:18), Verteidigungsexportinitiativen (SOU 2026:16) und der Bekämpfung unrechtmäßiger Medikamentenverschreibung (SOU 2026:19).

    +
    + +
    +

    Kammerdebatten: Haushaltstransparenz, Iran und die Kreislaufwirtschaft

    +

    Die Kammerdebatten dieser Woche spiegelten sowohl außenpolitische Spannungen als auch innenpolitische Haushaltsverantwortung wider:

    +

    Iran und demokratische Unterstützung

    +

    Außenministerin Maria Malmer Stenergard (M) beantwortete eine Interpellation von Daniel Riazat (unabhängig, ehemals V) zur Unterstützung der Regierung für den Aufstand des iranischen Volkes gegen das Regime. SDs Nima Gholam Ali Pour beteiligte sich ebenfalls, was eine ungewöhnliche parteiübergreifende Dynamik erzeugte.

    +

    Haushaltsauswirkungen und fiskalische Transparenz

    +

    Finanzministerin Elisabeth Svantesson (M) wurde von S-Abgeordnetem Peder Björk zu den Berichten über Haushaltsauswirkungen nachhaltig befragt — ein Teil der Oppositionsstrategie, die Regierung vor der Wahl 2026 für wirtschaftliche Transparenz zur Rechenschaft zu ziehen.

    +

    Parkplatz-Mehrwertsteuer und Kommunalökonomie

    +

    Svantesson debattierte auch mit S-Abgeordneter Marie Olsson über die Mehrwertsteuer für Parkplätze — ein Thema mit erheblichen Auswirkungen auf die kommunalen Finanzen und die Gewerbeimmobilienwirtschaft.

    +

    Kreislauf- und giftfreie Wirtschaft

    +

    Die fortgesetzte Debatte zu MJU12 sah die Beteiligung von MPs Katarina Luhr, Cs Stina Larsson, Vs Andrea Andersson Tay und Ms Lars Johnsson — ein Ausdruck der wachsenden politischen Bedeutung von Nachhaltigkeit und Chemikaliensicherheit.

    +
    + +
    +

    Oppositionsdynamik: Sozialversicherungsqualifizierung wird zum Schlachtfeld

    +

    Die intensivste Oppositionsaktivität konzentrierte sich auf Proposition 2025/26:136 zur Sozialversicherungsqualifizierung. Vier Oppositionsparteien reichten separate Motionen ein:

    +
      +
    • Sozialdemokraten (S): Motion 3936 von Ida Karkiainen würde die Proposition teilweise ablehnen und die Anforderung des rechtmäßigen Aufenthalts akzeptieren, während umfassendere Qualifikationsbeschränkungen abgelehnt werden
    • +
    • Vänsterpartiet (V): Motion 3932 von Tony Haddou fordert eine vollständige Ablehnung
    • +
    • Miljöpartiet (MP): Motion 3942 von Malte Tängmark Roos fordert ebenfalls eine vollständige Ablehnung
    • +
    • Centerpartiet (C): Motion 3939 von Anders W Jonsson strebt Änderungen an den Qualifikationsausnahmen an
    • +
    +

    Diese Vier-Parteien-Oppositionsfront bei der Sozialleistungsqualifizierung stellt eine bedeutende politische Ausrichtung dar. Die Interpellationsoffensive der Sozialdemokraten setzte sich fort, mit Schwerpunkt auf Altenpflege (drei separate Interpellationen von Lundh Sammeli, Dahlqvist und Sundin an Ministerin Tenje), wirtschaftlichen Prioritäten und sozialem Dumping zwischen Gemeinden. Diese Altenpflege-Trias signalisiert, dass S beabsichtigt, die Pflegequalität zu einem Wahlkampfthema 2026 zu machen.

    +

    MPs Ulrika Westerlund stellte die rechtlichen Grundlagen der strafrechtlichen Agenda der Regierung in Interpellation 382 in Frage, während MPs Annika Hirvonen die Abschiebungspolitik für Jugendliche in Interpellation 381 thematisierte.

    +
    + +
    +

    Was am wichtigsten war: Der Gesetzgebungssprint vor der Wahl

    +

    Das schiere Volumen und die Breite der gesetzgeberischen Ergebnisse dieser Woche offenbaren eine Regierung im vollen Vorwahlsprint. Mit der Wahl im September 2026 in etwa sechs Monaten drückt die Kristersson-Koalition ihre verbleibenden Gesetzgebungsprioritäten in beispiellosem Tempo durch — Staatsbürgerschaftsreform, Einwanderungsreform, Atomkraftausbau und sozialkonservatives Familienrecht in einer einzigen Woche.

    +

    Die koordinierte Reaktion der Opposition auf die Sozialversicherungsqualifizierung deutet darauf hin, dass sich die Kampflinien für den Wahlkampf 2026 verhärten: die rechte Koalition setzt auf Migration, Staatsbürgerschaft und Sicherheit; die Opposition verteidigt die Universalität des Sozialstaats und die bürgerlichen Freiheiten. Die Altenpflege-Interpellationsoffensive fügt eine Dimension der öffentlichen Daseinsvorsorge hinzu, die sich als wahlentscheidend erweisen könnte.

    +

    Am folgenreichsten markiert der Atomkraft-Dreifachschlag eine unwiderrufliche politische Festlegung. Die Eröffnung neuer Küstenstandorte für Kernkraftwerke, kombiniert mit modernisierten Sicherheits- und Genehmigungsverfahren, positioniert Schweden als eine der atomkraftfreundlichsten Demokratien Europas — eine Haltung mit Auswirkungen auf Energiemärkte, Klimapolitik und geopolitische Ausrichtung für die kommenden Jahrzehnte.

    +
    + +
    +

    Ausblick

    +

    Nächste Woche (16.–20. März) wird der Riksdag mehrere der dieswöchigen Ausschussberichte in Plenarabstimmungen behandeln. Beobachten Sie die Debatte zur Reform der Arbeitsmigration (SfU12), die voraussichtlich parteiübergreifende Auseinandersetzungen auslösen wird. Die Proposition zur Sozialversicherungsqualifizierung (Prop. 136) setzt ihren Ausschussweg mit entgegengesetzten Motionen von vier Parteien fort.

    +

    Die Staatsbürgerschaftsproposition der Regierung (Prop. 175) wird an den Sozialversicherungsausschuss verwiesen, wo die gesamte Opposition voraussichtlich heftigen Widerstand leisten wird. Die Atomkraftpropositionen gehen an die zuständigen Ausschüsse, wo technische Anhörungen wahrscheinlich angesetzt werden.

    +

    Auf Regierungsseite signalisiert der Besuch von Außenministerin Malmer Stenergard in Texas und Virginia ein anhaltendes Engagement gegenüber den Vereinigten Staaten, während nach den Gesprächen von Kommissar Šefçoviç mit Minister Dousa weitere EU-handelspolitische Initiativen erwartet werden.

    +
    + +
    +

    Datenquellen

    +

    Diese Analyse basiert auf Daten aus der offenen Daten-API des schwedischen Parlaments (data.riksdagen.se) und des Regierungskanzlei über g0v.se, die den Zeitraum 7.–14. März 2026 abdecken. Alle parlamentarischen Dokumente, Abstimmungen und Reden sind öffentlich über das Dokumentenarchiv des Riksdag zugänglich. Riksmöte: 2025/26.

    +
    + +
    + +
    +

    Veröffentlicht von Riksdagsmonitor — KI-generierter politischer Nachrichtendienst basierend auf OSINT/INTOP-Daten aus den offenen Daten-APIs des schwedischen Parlaments und der Regierung.

    +

    © 2026 Hack23 AB. Licensed under CC BY-SA 4.0.

    +
    + + + + diff --git a/news/2026-03-14-weekly-review-en.html b/news/2026-03-14-weekly-review-en.html index 0473ebf098f..c73fce77eb6 100644 --- a/news/2026-03-14-weekly-review-en.html +++ b/news/2026-03-14-weekly-review-en.html @@ -3,7 +3,7 @@ - + The Week in Swedish Politics: Citizenship Crackdown, Nuclear Expansion, and Immigration Reform @@ -43,6 +43,18 @@ + + + + + + + + + + + + @@ -189,6 +201,18 @@
    diff --git a/news/2026-03-14-weekly-review-es.html b/news/2026-03-14-weekly-review-es.html new file mode 100644 index 00000000000..1b967407302 --- /dev/null +++ b/news/2026-03-14-weekly-review-es.html @@ -0,0 +1,297 @@ + + + + + + + La semana en la política sueca: endurecimiento de la ciudadanía, expansión nuclear y reforma migratoria + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← Volver a noticias + +
    + +
    +
    + + + +
    Últimas noticias y análisis del Riksdag de Suecia. Periodismo de inteligencia política generado por IA basado en datos OSINT/INTOP sobre parlamento, gobierno y organismos públicos con transparencia sistemática.
    +

    La semana en la política sueca: endurecimiento de la ciudadanía, expansión nuclear y reforma migratoria

    + +
    + +
    +

    Puntos clave

    +
      +
    • Ciudadanía más estricta: La proposición 2025/26:175 introduce requisitos significativamente más estrictos para la ciudadanía sueca — la reforma más amplia de la ley de nacionalidad en décadas
    • +
    • Prohibición del matrimonio entre primos: La proposición 2025/26:154 propone prohibir los matrimonios entre primos y otros parientes cercanos, ampliando los impedimentos matrimoniales existentes en Suecia
    • +
    • Expansión nuclear: Tres proposiciones nucleares (160, 167, 168) señalan la apuesta decidida del gobierno por nuevos emplazamientos nucleares costeros y normativas de seguridad modernizadas
    • +
    • Reforma de la inmigración laboral: El informe del comité SfU12 recomienda aprobar nuevas normas amplias para la inmigración laboral, incluyendo umbrales salariales y nuevos delitos por explotación de trabajadores
    • +
    • 15 informes de comité: Los comités emitieron decisiones en asuntos constitucionales (KU), finanzas (FiU), seguridad social (SfU), medio ambiente (MJU), transporte (TU) y asuntos civiles (CU)
    • +
    +
    + +
    +

    Historia principal: la ciudadanía y la identidad en el centro del debate político

    +

    El gobierno de Kristersson situó la ciudadanía y el derecho de familia en el centro de su agenda legislativa preelectoral esta semana, presentando dos proposiciones que juntas representan los cambios más significativos en el derecho personal sueco en una generación.

    +

    La proposición 2025/26:175, presentada el 12 de marzo por el ministro de Migración Johan Forssell, introduce requisitos drásticamente más estrictos para obtener la ciudadanía sueca. La reforma, que señala la postura firme del gobierno en materia de integración, está diseñada para garantizar que la ciudadanía refleje un vínculo social genuino — una demanda política impulsada desde hace tiempo por SD y ahora promulgada por la coalición Tidö.

    +

    Ese mismo día, el ministro de Justicia Gunnar Strömmer presentó la proposición 2025/26:154, que prohibiría los matrimonios entre primos y otros parientes cercanos más allá de la actual prohibición entre hermanos. El gobierno presentó la medida como una protección de los derechos individuales y un combate contra la cultura basada en el honor, un enfoque cuestionado por los partidos de la oposición, que abogan por planteamientos más matizados.

    +

    Estas dos proposiciones subrayan la estrategia del gobierno de impulsar reformas socialmente conservadoras a gran velocidad a medida que se acercan las elecciones de septiembre de 2026, creando claras líneas ideológicas de batalla frente a los Socialdemócratas y el Partido Verde.

    +
    + +
    +

    Resultados legislativos: 15 informes de comité en seis áreas políticas

    +

    Los comités presentaron 15 betänkanden (informes) esta semana, cubriendo un espectro excepcionalmente amplio de áreas políticas. Varios tuvieron un peso legislativo significativo:

    +

    Reforma de la inmigración laboral (SfU12)

    +

    El informe del Comité de Seguridad Social sobre las nuevas normas de inmigración laboral recomienda que el Riksdag apruebe la reforma más completa del gobierno en materia de permisos de trabajo en años. Las medidas clave incluyen umbrales salariales obligatorios, requisito de seguro médico integral, nuevos delitos penales por explotación de trabajadores extranjeros y tráfico de permisos de trabajo, y ampliación de la validez de la Tarjeta Azul UE. Las reformas entran en vigor el 1 de junio de 2026.

    +

    Lucha contra las especies invasoras (MJU13)

    +

    El Comité de Medio Ambiente y Agricultura recomienda reforzar la regulación contra las especies alóctonas invasoras. Según las nuevas normas, la introducción intencionada o negligente de especies invasoras en Suecia desde un país de la UE será sancionable con hasta dos años de prisión. Tullverket obtiene facultades de inspección en las fronteras internas. La ley entra en vigor el 1 de mayo de 2026, junto con el rechazo de aproximadamente 200 mociones sobre conservación de la naturaleza.

    +

    Control de perros (MJU15)

    +

    Los länsstyrelser obtendrán herramientas ampliadas para la supervisión canina, incluyendo la potestad de acceder a domicilios para inspecciones y mayor autoridad para la incautación inmediata de perros peligrosos. La policía también podrá ordenar el sacrificio de animales incautados en determinados casos. En vigor desde el 1 de mayo de 2026.

    +

    Asuntos constitucionales y administrativos (KU35–37)

    +

    El Comité Constitucional presentó tres informes: sobre reuniones municipales digitales (KU35), una revisión de la privacidad y las nuevas tecnologías 2020–2024 (KU36) y la concentración de actividades de los länsstyrelser (KU37). El informe sobre reuniones digitales propone una participación remota más flexible en la gobernanza local — una reforma heredada de las prácticas de la era pandémica.

    +

    Mercados financieros y gobernanza municipal (FiU22, FiU25, FiU26, FiU34)

    +

    El Comité de Finanzas tramitó cuatro informes. FiU22 aborda las actividades del FMI en 2025 y la estabilidad de los mercados financieros. FiU26 rechazó 69 mociones sobre asuntos municipales. FiU34 trató la contratación pública, y FiU25 abordó la administración estatal.

    +

    Política familiar y seguridad social (SfU10, SfU17)

    +

    SfU17 rechazó 104 mociones sobre política económica familiar — cubriendo permisos parentales, prestaciones por hijo y subsidios de vivienda. SfU10 abordó la protección de la seguridad social para trabajadores con movilidad internacional.

    +
    + +
    +

    Vigilancia gubernamental: ambiciones nucleares y profundización de la defensa

    +

    Más allá de la ciudadanía y el derecho de familia, el gobierno impulsó una importante agenda energética y reforzó la postura de defensa de Suecia esta semana:

    +
      +
    • Triple jugada nuclear (Props. 160, 167, 168, 171): Cuatro proposiciones nucleares en una sola semana. La Prop. 160 abre nuevos emplazamientos costeros para centrales nucleares, mientras que las Props. 167, 168 y 171 modernizan el control de materiales nucleares, los requisitos de seguridad y radiación, y el proceso de revisión de instalaciones nucleares.
    • +
    • Informe de actividades de la UE (Skr. 115): La revisión anual del gobierno sobre la UE fue presentada el 12 de marzo, cubriendo la participación de Suecia en la UE durante 2025.
    • +
    • Resultados de la ayuda a Ucrania (Skr. 97): Se presentó un informe detallado sobre los resultados de la ayuda internacional sueca a Ucrania.
    • +
    • Verificación de antecedentes escolares (Prop. 174): Controles ampliados de registros para empleados escolares, en línea con la agenda de seguridad educativa del gobierno.
    • +
    • Reforma del proceso de construcción (Prop. 172): Agilización de los procesos constructivos para mayor eficiencia y seguridad.
    • +
    • Precisión en subsidios de vivienda (Prop. 170): Cálculos de ayudas a la vivienda más precisos y focalizados.
    • +
    • Distribución del superávit de pensiones (Prop. 169): Normas para la distribución del superávit del sistema de pensiones contributivas.
    • +
    • Cooperación industrial de defensa con Ucrania: Suecia anunció una cooperación industrial de defensa reforzada con Ucrania, y el ministro de Defensa Civil Carl-Oskar Bohlin participó en un ejercicio de defensa total en Estocolmo.
    • +
    +

    También se publicaron tres SOU (investigaciones oficiales): sobre turba agrícola y clima (SOU 2026:18), iniciativas de exportación de defensa (SOU 2026:16) y lucha contra la prescripción indebida de medicamentos (SOU 2026:19).

    +
    + +
    +

    Debates en cámara: transparencia presupuestaria, Irán y la economía circular

    +

    Los debates en cámara de esta semana reflejaron tanto tensiones de política exterior como responsabilidad fiscal nacional:

    +

    Irán y apoyo democrático

    +

    La ministra de Asuntos Exteriores Maria Malmer Stenergard (M) respondió a una interpelación de Daniel Riazat (independiente, anteriormente V) sobre el apoyo del gobierno al levantamiento del pueblo iraní contra el régimen. Nima Gholam Ali Pour de SD también participó, creando una dinámica interpartidaria inusual.

    +

    Efectos presupuestarios y transparencia fiscal

    +

    La ministra de Finanzas Elisabeth Svantesson (M) enfrentó un cuestionamiento sostenido de Peder Björk de S sobre la comunicación de los efectos presupuestarios — parte de la estrategia opositora para exigir al gobierno transparencia económica de cara a las elecciones de 2026.

    +

    IVA de aparcamientos y economía municipal

    +

    Svantesson también debatió con Marie Olsson de S sobre el IVA de plazas de aparcamiento — un tema con implicaciones significativas para las finanzas municipales y la economía inmobiliaria comercial.

    +

    Economía circular y libre de tóxicos

    +

    El debate continuado sobre MJU12 contó con la participación de Katarina Luhr de MP, Stina Larsson de C, Andrea Andersson Tay de V y Lars Johnsson de M — reflejando la creciente relevancia política de la sostenibilidad y la seguridad química.

    +
    + +
    +

    Dinámica de la oposición: la cualificación para la seguridad social se convierte en campo de batalla

    +

    La actividad opositora más intensa se centró en la proposición 2025/26:136 sobre cualificación para la seguridad social. Cuatro partidos de la oposición presentaron mociones por separado:

    +
      +
    • Socialdemócratas (S): La moción 3936 de Ida Karkiainen rechazaría parcialmente la proposición, aceptando el requisito de residencia legal pero oponiéndose a restricciones de cualificación más amplias
    • +
    • Partido de Izquierda (V): La moción 3932 de Tony Haddou exige el rechazo total
    • +
    • Partido Verde (MP): La moción 3942 de Malte Tängmark Roos también exige el rechazo completo
    • +
    • Partido del Centro (C): La moción 3939 de Anders W Jonsson busca modificaciones a las excepciones de cualificación
    • +
    +

    Este frente opositor de cuatro partidos sobre la cualificación para las prestaciones sociales representa una alineación política significativa. La ofensiva de interpelaciones de los Socialdemócratas continuó centrándose en la atención a personas mayores (tres interpelaciones separadas de Lundh Sammeli, Dahlqvist y Sundin dirigidas a la ministra Tenje), prioridades económicas y dumping social entre municipios. Esta triple ofensiva sobre atención a mayores indica que S pretende convertir la calidad asistencial en un tema clave de las elecciones de 2026.

    +

    Ulrika Westerlund de MP cuestionó los fundamentos jurídicos de la agenda penal del gobierno en la interpelación 382, mientras que Annika Hirvonen de MP dirigió su atención hacia la política de deportación de adolescentes en la interpelación 381.

    +
    + +
    +

    Lo que más importó: el sprint legislativo preelectoral

    +

    El enorme volumen y alcance de la producción legislativa de esta semana revela un gobierno en pleno sprint preelectoral. Con las elecciones de septiembre de 2026 a aproximadamente seis meses de distancia, la coalición Kristersson impulsa sus prioridades legislativas restantes a un ritmo sin precedentes — reforma de la ciudadanía, reforma migratoria, expansión nuclear y derecho de familia socialmente conservador en una sola semana.

    +

    La respuesta coordinada de la oposición sobre la cualificación para la seguridad social sugiere que las líneas de batalla electorales de 2026 se están endureciendo: la coalición de derechas se apoya en migración, ciudadanía y seguridad; la oposición defiende la universalidad del bienestar y las libertades civiles. La ofensiva de interpelaciones sobre atención a mayores añade una dimensión de servicios públicos que puede resultar electoralmente potente.

    +

    Lo más trascendental es que la triple jugada nuclear marca un compromiso político irreversible. La apertura de nuevos emplazamientos nucleares costeros, combinada con procesos de seguridad y revisión modernizados, posiciona a Suecia como una de las democracias más pronucleares de Europa — una postura con implicaciones para los mercados energéticos, la política climática y la alineación geopolítica durante décadas.

    +
    + +
    +

    Perspectivas

    +

    La próxima semana (16–20 de marzo) el Riksdag tramitará varios de los informes de comité de esta semana en votaciones plenarias. Conviene seguir de cerca el debate sobre la reforma de la inmigración laboral (SfU12), que probablemente generará enfrentamientos interpartidarios. La proposición sobre cualificación para la seguridad social (Prop. 136) continuará su tramitación en comité con mociones opuestas de cuatro partidos que aseguran un proceso contencioso.

    +

    La proposición de ciudadanía del gobierno (Prop. 175) será remitida al Comité de Seguridad Social, donde se espera que la oposición conjunta presente una resistencia vigorosa. Mientras tanto, las proposiciones nucleares se dirigen a los comités competentes, donde probablemente se programarán audiencias técnicas.

    +

    En el ámbito gubernamental, la visita de la ministra de Asuntos Exteriores Malmer Stenergard a Texas y Virginia señala un compromiso continuado con Estados Unidos, mientras se esperan más iniciativas de política comercial de la UE tras las reuniones del comisario Šefçoviç con el ministro Dousa.

    +
    + +
    +

    Fuentes de datos

    +

    Este análisis se basa en datos de la API de datos abiertos del Parlamento sueco (data.riksdagen.se) y de las Oficinas del Gobierno a través de g0v.se, cubriendo el período del 7 al 14 de marzo de 2026. Todos los documentos parlamentarios, votaciones y discursos referenciados están disponibles públicamente a través del archivo documental del Riksdag. Riksmöte: 2025/26.

    +
    + +
    + +
    +

    Publicado por Riksdagsmonitor — inteligencia política generada por IA basada en datos OSINT/INTOP de las API de datos abiertos del Parlamento y el Gobierno de Suecia.

    +

    © 2026 Hack23 AB. Licensed under CC BY-SA 4.0.

    +
    + + + + diff --git a/news/2026-03-14-weekly-review-fi.html b/news/2026-03-14-weekly-review-fi.html new file mode 100644 index 00000000000..b6b2ab44a35 --- /dev/null +++ b/news/2026-03-14-weekly-review-fi.html @@ -0,0 +1,297 @@ + + + + + + + Viikko Ruotsin politiikassa: Kansalaisuusvaatimusten kiristys, ydinvoiman laajentaminen ja maahanmuuttouudistus + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← Takaisin uutisiin + +
    + +
    +
    + + + +
    Viimeisimmät uutiset ja analyysit Ruotsin valtakunnansalista. Tekoälyn tuottama poliittinen tiedustelujournalismi, joka perustuu OSINT/INTOP-tietoihin parlamentista, hallituksesta ja viranomaisista järjestelmällisellä avoimuudella.
    +

    Viikko Ruotsin politiikassa: Kansalaisuusvaatimusten kiristys, ydinvoiman laajentaminen ja maahanmuuttouudistus

    + +
    + +
    +

    Keskeiset havainnot

    +
      +
    • Tiukemmat kansalaisuusvaatimukset: Propositio 2025/26:175 asettaa merkittävästi tiukemmat vaatimukset Ruotsin kansalaisuuden saamiselle — kansalaisuuslain laajin uudistus vuosikymmeniin
    • +
    • Serkusavioliittokielto: Propositio 2025/26:154 ehdottaa serkusten ja muiden lähisukulaisten välisten avioliittojen kieltämistä, laajentaen Ruotsin nykyisiä avioesteitä
    • +
    • Ydinvoiman laajentaminen: Kolme ydinvoimaan liittyvää propositiota (160, 167, 168) osoittavat hallituksen panostavan täysillä uusiin rannikon ydinvoimalaitoksiin ja nykyaikaistettuihin turvallisuussäännöksiin
    • +
    • Työperäisen maahanmuuton uudistus: Valiokunnan mietintö SfU12 suosittaa uusien työperäistä maahanmuuttoa koskevien sääntöjen hyväksymistä, mukaan lukien palkkarajat ja uudet rikosoikeudelliset säännökset työntekijöiden hyväksikäytöstä
    • +
    • 15 valiokunnan mietintöä: Valiokunnat tuottivat päätöksiä perustuslakiasioista (KU), rahoituksesta (FiU), sosiaalivakuutuksesta (SfU), ympäristöstä (MJU), liikenteestä (TU) ja siviiliasioista (CU)
    • +
    +
    + +
    +

    Pääuutinen: Kansalaisuus ja identiteetti poliittisen keskustelun ytimessä

    +

    Kristerssonin hallitus asetti kansalaisuuden ja perhelainsäädännön vaalikampanjaa edeltävän lainsäädäntöohjelmansa keskipisteeseen tällä viikolla esittelemällä kaksi propositiota, jotka yhdessä edustavat merkittävimpiä muutoksia Ruotsin henkilöoikeuteen sukupolveen.

    +

    Propositio 2025/26:175, jonka maahanmuuttoministeri Johan Forssell esitteli 12. maaliskuuta, asettaa dramaattisesti tiukemmat vaatimukset Ruotsin kansalaisuuden saamiselle. Uudistus ilmaisee hallituksen jyrän linjan kotouttamisessa ja on suunniteltu varmistamaan, että kansalaisuus heijastaa aitoa yhteiskunnallista sidettä — poliittinen vaatimus, jota SD on pitkään ajanut ja jonka Tidö-koalitio nyt toteuttaa.

    +

    Samana päivänä oikeusministeri Gunnar Strömmer esitteli Proposition 2025/26:154, joka kieltäisi avioliitot serkusten ja muiden lähisukulaisten välillä nykyisen sisaruskiellon lisäksi. Hallitus peräänkuulutti toimenpiteen suojelevan yksilönoikeuksia ja torjuvan kunniaan liittyvää kulttuuria.

    +

    Nämä kaksi propositiota korostavat hallituksen strategiaa edistää sosiaalisesti konservatiivisia uudistuksia vauhdikkaasti syyskuun 2026 vaalien lähestyessä, luoden selkeät ideologiset taistelulinjat Sosiaalidemokraatteja ja Ympäristöpuoluetta vastaan.

    +
    + +
    +

    Lainsäädännölliset tulokset: 15 valiokunnan mietintöä kuudelta politiikan alalta

    +

    Valiokunnat tuottivat 15 betänkandea (mietintöä) tällä viikolla, kattaen poikkeuksellisen laajan politiikka-alueiden kirjon. Useilla oli merkittävä lainsäädännöllinen painoarvo:

    +

    Työperäisen maahanmuuton uudistus (SfU12)

    +

    Sosiaalivakuutusvaliokunnan mietintö uusista työperäisen maahanmuuton säännöistä suosittaa, että Riksdag hyväksyy hallituksen laajimman työlupauudistuksen vuosiin. Keskeisiä toimenpiteitä ovat pakolliset palkkarajat, kattavan sairausvakuutuksen vaatimus, uudet rikossäännökset ulkomaisten työntekijöiden hyväksikäytöstä sekä EU:n sinisen kortin pidennetty voimassaolo. Uudistukset tulevat voimaan 1. kesäkuuta 2026.

    +

    Vieraslajien torjunta (MJU13)

    +

    Ympäristö- ja maatalousvaliokunta suosittaa vieraslajeja koskevien säännösten tiukentamista. Uusien sääntöjen mukaan vieraslajien tahallinen tai huolimaton tuominen Ruotsiin EU-maasta on rangaistavaa enintään kahdella vuodella vankeutta. Tullverket saa tarkastusvaltuudet sisärajoilla. Laki tulee voimaan 1. toukokuuta 2026.

    +

    Koirien valvontavaltuudet (MJU15)

    +

    Lääninhallitukset saavat laajennetut valtuudet koirien valvontaan, mukaan lukien oikeuden päästä koteihin tarkastuksia varten ja laajemmat valtuudet vaarallisten koirien välittömään takavarikointiin. Poliisi voi myös määrätä takavarikoitujen eläinten lopettamisen tietyissä tapauksissa. Voimaan 1. toukokuuta 2026.

    +

    Perustuslaki- ja hallintovaliokunta (KU35–37)

    +

    Perustuslakivaliokunta tuotti kolme mietintöä: kunnallisista etäkokouksista (KU35), yksityisyydensuojan ja uuden teknologian tarkastelusta 2020–2024 (KU36) ja lääninhallitusten toimintojen keskittämisestä (KU37). Etäkokouksia käsittelevä mietintö ehdottaa joustavampaa etäosallistumista kunnallishallinnossa.

    +

    Rahoitusmarkkinat ja kunnallishallinto (FiU22, FiU25, FiU26, FiU34)

    +

    Valtiovarainvaliokunta käsitteli neljä mietintöä. FiU22 käsittelee IMF:n toimintaa vuonna 2025 ja rahoitusmarkkinoiden vakautta. FiU26 hylkäsi 69 aloitetta kunnallisista kysymyksistä. FiU34 käsitteli julkisia hankintoja ja FiU25 valtion hallintoa.

    +

    Perhepolitiikka ja sosiaalivakuutus (SfU10, SfU17)

    +

    SfU17 hylkäsi 104 aloitetta taloudellisesta perhepolitiikasta — mukaan lukien vanhempainvakuutus, lapsillisät ja asumistuki. SfU10 käsitteli sosiaalivakuutussuojaa kansainvälisesti liikkuville työntekijöille.

    +
    + +
    +

    Hallituksen seuranta: Ydinvoimatavoitteet ja puolustuksen syventyminen

    +

    Kansalaisuuden ja perheoikeuden lisäksi hallitus edisti merkittävää energia-agendaa ja syvensi Ruotsin puolustusasemaa tällä viikolla:

    +
      +
    • Ydinvoiman kolmoisiskut (Prop. 160, 167, 168, 171): Neljä ydinvoimaan liittyvää propositiota yhdellä viikolla. Prop. 160 avaa uusia rannikkosijainteja ydinvoimalaitoksille, kun taas Prop. 167, 168 ja 171 modernisoivat ydinmateriaalien valvontaa, turvallisuus- ja säteilyvaatimuksia sekä ydinlaitosten tarkastusprosessia.
    • +
    • EU-toimintaraportti (Skr. 115): Hallituksen vuotuinen EU-katsaus esiteltiin 12. maaliskuuta, kattaen Ruotsin EU-toiminnan vuonna 2025.
    • +
    • Ukraina-avun tulokset (Skr. 97): Yksityiskohtainen raportti Ruotsin kansainvälisen Ukraina-avun tuloksista esiteltiin.
    • +
    • Taustatarkistukset kouluissa (Prop. 174): Laajennetut rekisteritarkistukset kouluhenkilöstölle, heijastaen hallituksen turvallisuusagendaa koulutuksessa.
    • +
    • Rakennusprosessin uudistus (Prop. 172): Rakennusprosessien tehostaminen tehokkuuden ja turvallisuuden parantamiseksi.
    • +
    • Asumistuen tarkkuus (Prop. 170): Kohdennetummat ja tarkemmat asumistuen laskelmat.
    • +
    • Eläkeylijäämän jakaminen (Prop. 169): Säännöt tuloseläkejärjestelmän ylijäämän jakamisesta.
    • +
    • Puolustusteollinen Ukraina-yhteistyö: Ruotsi ilmoitti vahvistetusta puolustusteollisesta yhteistyöstä Ukrainan kanssa, ja siviilipuolustusministeri Carl-Oskar Bohlin osallistui kokonaismaanpuolustusharjoitukseen Tukholmassa.
    • +
    +

    Kolme SOU:ta (virallisia selvityksiä) julkaistiin myös: maatalousturpeesta ja ilmastosta (SOU 2026:18), puolustusvientihankkeista (SOU 2026:16) ja asiattomien lääkemääräysten torjunnasta (SOU 2026:19).

    +
    + +
    +

    Täysistuntokeskustelut: Budjettien läpinäkyvyys, Iran ja kiertotalous

    +

    Viikon täysistuntokeskustelut heijastivat sekä ulkopoliittisia jännitteitä että kotimaista taloudellista vastuullisuutta:

    +

    Iran ja demokratiatuki

    +

    Ulkoministeri Maria Malmer Stenergard (M) vastasi Daniel Riazatin (sitoutumaton, aiemmin V) interpellaatioon hallituksen tuesta Iranin kansan kapinalle hallintoa vastaan. SD:n Nima Gholam Ali Pour osallistui myös, luoden epätavallisen puoluerajat ylittävän dynamiikan.

    +

    Budjettivaikutukset ja finanssipoliittinen läpinäkyvyys

    +

    Valtiovarainministeri Elisabeth Svantesson (M) joutui S:n Peder Björkin jatkuvan kyselyn kohteeksi budjettivaikutusten raportoinnista — osa opposition strategiaa pitää hallitus vastuullisena taloudellisesta läpinäkyvyydestä ennen vuoden 2026 vaaleja.

    +

    Pysäköinnin arvonlisävero ja kunnallistalous

    +

    Svantesson keskusteli myös S:n Marie Olssonin kanssa pysäköintipaikkojen arvonlisäverosta — aihe, jolla on merkittäviä vaikutuksia kunnallistalouteen ja liikekiinteistöjen talouteen.

    +

    Kiertotalous ja myrkytön talous

    +

    MJU12:sta käyty jatkokeskustelu näki osallistumisen MP:n Katarina Luhrilta, C:n Stina Larssonilta, V:n Andrea Andersson Taylta ja M:n Lars Johnssonilta — heijastaen kestävän kehityksen ja kemikaaliturvallisuuden kasvavaa poliittista merkitystä.

    +
    + +
    +

    Oppositiodynamiikka: Sosiaalivakuutuksen kelpoisuudesta tulee taistelukenttä

    +

    Vilkkain oppositiotoiminta keskittyi Propositioon 2025/26:136 sosiaalivakuutuksen kelpoisuudesta. Neljä oppositiopuoluetta jätti erilliset aloitteet:

    +
      +
    • Sosiaalidemokraatit (S): Aloite 3936, tekijänä Ida Karkiainen, hylkäisi proposition osittain hyväksyen laillisen oleskelun vaatimuksen mutta vastustaen laajempia kelpoisuusrajoituksia
    • +
    • Vasemmistopuolue (V): Aloite 3932, tekijänä Tony Haddou, vaatii täydellistä hylkäämistä
    • +
    • Ympäristöpuolue (MP): Aloite 3942, tekijänä Malte Tängmark Roos, vaatii myös täydellistä hylkäämistä
    • +
    • Keskustapuolue (C): Aloite 3939, tekijänä Anders W Jonsson, pyrkii muuttamaan kelpoisuuspoikkeuksia
    • +
    +

    Tämä neljän puolueen oppositiorintama sosiaaliturvakelpoisuudessa edustaa merkittävää poliittista yhteenliittymää. Sosiaalidemokraattien interpellaatio-offensiivi jatkui keskittyen vanhusten hoitoon (kolme erillistä interpellaatiota Lundh Sammelilta, Dahlqvistilta ja Sundinilta ministeri Tenjelle), taloudellisiin prioriteetteihin ja kuntien väliseen sosiaaliseen polkumyyntiin. Tämä vanhusten hoidon kolmoishyökkäys viestii, että S aikoo tehdä hoidon laadusta vuoden 2026 vaaliteeman.

    +

    MP:n Ulrika Westerlund haastoi hallituksen rikoslainsäädännön oikeudelliset perusteet interpellaatiossa 382, kun taas MP:n Annika Hirvonen kohdisti huomionsa nuorten karkotuspolitiikkaan interpellaatiossa 381.

    +
    + +
    +

    Mikä merkitsi eniten: Vaaleja edeltävä lainsäädäntöspurtti

    +

    Viikon lainsäädäntötuotannon valtava määrä ja laajuus paljastaa hallituksen olevan täydessä vaalikampanjavauhdissa. Syyskuun 2026 vaaleihin on noin kuusi kuukautta, ja Kristerssonin koalitio ajaa jäljellä olevia lainsäädäntöprioriteettejaan läpi ennennäkemättömällä vauhdilla — kansalaisuusuudistus, maahanmuuttouudistus, ydinvoiman laajentaminen ja sosiaalisesti konservatiivinen perheoikeus yhdellä viikolla.

    +

    Opposition koordinoitu vastaus sosiaalivakuutuksen kelpoisuuteen viittaa siihen, että vuoden 2026 kampanjalinjat kovettuvat: oikeistokoalitio rakentaa maahanmuuton, kansalaisuuden ja turvallisuuden varaan; oppositio puolustaa hyvinvoinnin universaaliutta ja kansalaisvapauksia. Vanhusten hoidon interpellaatio-offensiivi lisää kotimaisen palvelutuotannon ulottuvuuden, joka voi osoittautua vaalien kannalta vaikuttavaksi.

    +

    Merkittävintä on, että ydinvoiman kolmoisiskut merkitsevät peruuttamatonta poliittista sitoutumista. Uusien rannikolla sijaitsevien ydinvoimalaitosten avaaminen yhdistettynä nykyaikaistettuihin turvallisuus- ja tarkastusprosesseihin asemoi Ruotsin yhdeksi Euroopan ydinvoimamyönteisimmistä demokratioista — kanta, jolla on vaikutuksia energiamarkkinoihin, ilmastopolitiikkaan ja geopoliittiseen suuntautumiseen vuosikymmeniksi eteenpäin.

    +
    + +
    +

    Näkymät eteenpäin

    +

    Ensi viikolla (16.–20. maaliskuuta) Riksdag käsittelee useita tämän viikon valiokunnan mietintöjä täysistuntoäänestyksin. Seuraa työperäisen maahanmuuton uudistusta (SfU12) koskevaa keskustelua, joka todennäköisesti synnyttää puoluerajat ylittäviä yhteenottoja. Sosiaalivakuutuksen kelpoisuuspropositio (Prop. 136) jatkaa valiokuntakäsittelyään neljän puolueen vastakkaisilla aloitteilla.

    +

    Hallituksen kansalaisuuspropositio (Prop. 175) lähetetään sosiaalivakuutusvaliokuntaan, jossa yhdistyneen opposition odotetaan esittävän voimakasta vastarintaa. Ydinvoimapropositiot etenevät asianomaisiin valiokuntiin, joissa tekniset kuulemiset todennäköisesti suunnitellaan.

    +

    Hallituksen puolella ulkoministeri Malmer Stenergardin vierailu Texasiin ja Virginiaan viestii jatkuvasta sitoutumisesta Yhdysvaltoihin, kun taas lisää EU:n kauppapolitiikan aloitteita odotetaan komissaari Šefçoviçin tapaamisten jälkeen ministeri Dousan kanssa.

    +
    + +
    +

    Tietolähteet

    +

    Tämä analyysi perustuu Ruotsin parlamentin avoimen datan rajapinnasta (data.riksdagen.se) ja hallituksen kansliasta g0v.se:n kautta saatuihin tietoihin ajalta 7.–14. maaliskuuta 2026. Kaikki viitatut parlamenttiasiakirjat, äänestykset ja puheet ovat julkisesti saatavilla Riksdagin asiakirja-arkistosta. Riksmöte: 2025/26.

    +
    + +
    + +
    +

    Julkaisija Riksdagsmonitor — tekoälyn tuottama poliittinen tiedustelu, joka perustuu OSINT/INTOP-tietoihin Ruotsin parlamentin ja hallituksen avoimen datan rajapinnoista.

    +

    © 2026 Hack23 AB. Licensed under CC BY-SA 4.0.

    +
    + + + + \ No newline at end of file diff --git a/news/2026-03-14-weekly-review-fr.html b/news/2026-03-14-weekly-review-fr.html new file mode 100644 index 00000000000..27be133bc37 --- /dev/null +++ b/news/2026-03-14-weekly-review-fr.html @@ -0,0 +1,297 @@ + + + + + + + La semaine en politique suédoise : durcissement de la citoyenneté, expansion nucléaire et réforme de l'immigration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← Retour aux actualités + +
    + +
    +
    + + + +
    Dernières actualités et analyses du Riksdag suédois. Journalisme de renseignement politique généré par IA basé sur des données OSINT/INTOP couvrant le parlement, le gouvernement et les agences avec une transparence systématique.
    +

    La semaine en politique suédoise : durcissement de la citoyenneté, expansion nucléaire et réforme de l'immigration

    + +
    + +
    +

    Points clés

    +
      +
    • Citoyenneté renforcée : La proposition 2025/26:175 introduit des exigences nettement plus strictes pour la citoyenneté suédoise — la réforme la plus profonde du droit de la nationalité depuis des décennies
    • +
    • Interdiction du mariage entre cousins : La proposition 2025/26:154 propose d'interdire les mariages entre cousins et autres proches parents, élargissant les empêchements matrimoniaux existants en Suède
    • +
    • Expansion nucléaire : Trois propositions liées au nucléaire (160, 167, 168) signalent l'engagement total du gouvernement en faveur de nouveaux sites nucléaires côtiers et de réglementations de sécurité modernisées
    • +
    • Réforme de l'immigration de travail : Le rapport de commission SfU12 recommande l'approbation de nouvelles règles complètes pour l'immigration de travail, incluant des seuils salariaux et de nouvelles infractions pénales pour l'exploitation des travailleurs
    • +
    • 15 rapports de commission : Les commissions ont rendu des décisions dans les domaines constitutionnel (KU), financier (FiU), assurance sociale (SfU), environnement (MJU), transport (TU) et affaires civiles (CU)
    • +
    +
    + +
    +

    Article principal : citoyenneté et identité au cœur du débat politique

    +

    Le gouvernement Kristersson a placé la citoyenneté et le droit de la famille au cœur de son agenda législatif préélectoral cette semaine, présentant deux propositions qui représentent ensemble les changements les plus significatifs du droit suédois des personnes depuis une génération.

    +

    La proposition 2025/26:175, présentée le 12 mars par le ministre de la Migration Johan Forssell, introduit des exigences considérablement plus strictes pour l'obtention de la citoyenneté suédoise. La réforme, signalant la position intransigeante du gouvernement sur l'intégration, vise à garantir que la citoyenneté reflète un véritable attachement à la société — une exigence politique longtemps défendue par SD et désormais mise en œuvre par la coalition Tidö.

    +

    Le même jour, le ministre de la Justice Gunnar Strömmer a présenté la proposition 2025/26:154, qui interdirait les mariages entre cousins et autres proches parents au-delà de l'interdiction actuelle entre frères et sœurs. Le gouvernement a présenté cette mesure comme une protection des droits individuels et une lutte contre la culture liée à l'honneur.

    +

    Ces deux propositions soulignent la stratégie du gouvernement consistant à faire avancer des réformes socialement conservatrices à un rythme soutenu alors que l'élection de septembre 2026 approche, créant des lignes de démarcation idéologiques claires face aux Sociaux-démocrates et au Parti Vert.

    +
    + +
    +

    Résultats législatifs : 15 rapports de commission couvrant six domaines politiques

    +

    Les commissions ont produit 15 betänkanden (rapports) cette semaine, couvrant un éventail exceptionnellement large de domaines politiques. Plusieurs avaient un poids législatif considérable :

    +

    Réforme de l'immigration de travail (SfU12)

    +

    Le rapport de la commission des Assurances sociales sur les nouvelles règles d'immigration de travail recommande que le Riksdag approuve la réforme la plus complète des permis de travail depuis des années. Les mesures clés comprennent des seuils salariaux obligatoires, l'exigence d'une assurance maladie complète, de nouvelles infractions pénales pour l'exploitation des travailleurs étrangers et le trafic de permis de travail, ainsi qu'une validité étendue de la Carte bleue européenne. Les réformes entrent en vigueur le 1er juin 2026.

    +

    Lutte contre les espèces invasives (MJU13)

    +

    La commission de l'Environnement et de l'Agriculture recommande de renforcer la réglementation contre les espèces exotiques envahissantes. Selon les nouvelles règles, l'introduction intentionnelle ou négligente d'espèces invasives en Suède depuis un pays de l'UE devient passible d'une peine pouvant aller jusqu'à deux ans d'emprisonnement. Tullverket obtient des pouvoirs d'inspection aux frontières intérieures. La loi entre en vigueur le 1er mai 2026.

    +

    Contrôle des chiens (MJU15)

    +

    Les Länsstyrelserna obtiennent des pouvoirs élargis de surveillance canine, incluant le droit d'accéder aux domiciles pour des inspections et une autorité plus large pour la saisie immédiate de chiens dangereux. La police peut également ordonner l'euthanasie d'animaux saisis dans certains cas. Entrée en vigueur le 1er mai 2026.

    +

    Affaires constitutionnelles et administratives (KU35–37)

    +

    La commission constitutionnelle a rendu trois rapports : sur les réunions municipales numériques (KU35), un examen de la protection de la vie privée et des nouvelles technologies 2020–2024 (KU36) et la concentration des activités des Länsstyrelserna (KU37). Le rapport sur les réunions numériques propose une participation à distance plus flexible dans la gouvernance locale.

    +

    Marchés financiers et gouvernance municipale (FiU22, FiU25, FiU26, FiU34)

    +

    La commission des Finances a traité quatre rapports. FiU22 porte sur les activités du FMI en 2025 et la stabilité des marchés financiers. FiU26 a rejeté 69 motions sur des questions municipales. FiU34 couvrait les marchés publics, et FiU25 traitait de l'administration de l'État.

    +

    Politique familiale et assurance sociale (SfU10, SfU17)

    +

    SfU17 a rejeté 104 motions sur la politique familiale économique — couvrant l'assurance parentale, les allocations familiales et les aides au logement. SfU10 a traité de la protection de l'assurance sociale pour les travailleurs en mobilité internationale.

    +
    + +
    +

    Surveillance gouvernementale : ambitions nucléaires et renforcement de la défense

    +

    Au-delà de la citoyenneté et du droit de la famille, le gouvernement a fait avancer un agenda énergétique majeur et renforcé la posture de défense de la Suède cette semaine :

    +
      +
    • Triple jeu nucléaire (Prop. 160, 167, 168, 171) : Quatre propositions liées au nucléaire en une seule semaine. La Prop. 160 ouvre de nouveaux sites côtiers pour les centrales nucléaires, tandis que les Prop. 167, 168 et 171 modernisent le contrôle des matières nucléaires, les exigences de sécurité et de radioprotection, ainsi que le processus d'examen des installations nucléaires.
    • +
    • Rapport d'activités UE (Skr. 115) : La revue annuelle de l'UE par le gouvernement a été déposée le 12 mars, couvrant l'engagement de la Suède dans l'UE tout au long de 2025.
    • +
    • Résultats de l'aide à l'Ukraine (Skr. 97) : Un rapport détaillé sur les résultats de l'aide internationale suédoise à l'Ukraine a été présenté.
    • +
    • Vérification des antécédents dans les écoles (Prop. 174) : Extension des contrôles de registres pour les employés scolaires, reflétant l'agenda sécuritaire du gouvernement dans l'éducation.
    • +
    • Réforme du processus de construction (Prop. 172) : Rationalisation des processus de construction pour plus d'efficacité et de sécurité.
    • +
    • Précision des allocations logement (Prop. 170) : Des calculs d'allocations logement plus ciblés et plus justes.
    • +
    • Distribution des excédents de pension (Prop. 169) : Règles de distribution des excédents du système de pension par répartition.
    • +
    • Coopération industrielle de défense avec l'Ukraine : La Suède a annoncé un renforcement de la coopération industrielle de défense avec l'Ukraine, et le ministre de la Défense civile Carl-Oskar Bohlin a participé à un exercice de défense totale à Stockholm.
    • +
    +

    Trois SOU (enquêtes officielles) ont également été publiées : sur la tourbe agricole et le climat (SOU 2026:18), les initiatives d'exportation de défense (SOU 2026:16) et la lutte contre les prescriptions médicamenteuses abusives (SOU 2026:19).

    +
    + +
    +

    Débats en chambre : transparence budgétaire, Iran et économie circulaire

    +

    Les débats en chambre de cette semaine ont reflété à la fois les tensions de politique étrangère et la responsabilité fiscale nationale :

    +

    Iran et soutien démocratique

    +

    La ministre des Affaires étrangères Maria Malmer Stenergard (M) a répondu à une interpellation de Daniel Riazat (indépendant, anciennement V) sur le soutien du gouvernement au soulèvement du peuple iranien contre le régime. Nima Gholam Ali Pour de SD a également participé, créant une dynamique transpartisane inhabituelle.

    +

    Effets budgétaires et transparence fiscale

    +

    La ministre des Finances Elisabeth Svantesson (M) a fait face à un questionnement soutenu de Peder Björk (S) sur le rapport des effets budgétaires — illustrant la stratégie de l'opposition visant à tenir le gouvernement responsable sur la transparence économique avant l'élection de 2026.

    +

    TVA sur le stationnement et finances municipales

    +

    Svantesson a également débattu avec Marie Olsson (S) sur la TVA applicable aux places de stationnement — un sujet aux implications significatives pour les finances municipales et l'économie de l'immobilier commercial.

    +

    Économie circulaire et sans substances toxiques

    +

    Le débat continu sur MJU12 a vu la participation de Katarina Luhr (MP), Stina Larsson (C), Andrea Andersson Tay (V) et Lars Johnsson (M) — reflétant l'importance politique croissante de la durabilité et de la sécurité chimique.

    +
    + +
    +

    Dynamique de l'opposition : la qualification à l'assurance sociale devient le champ de bataille

    +

    L'activité d'opposition la plus intense s'est concentrée sur la proposition 2025/26:136 relative à la qualification à l'assurance sociale. Quatre partis d'opposition ont déposé des motions séparées :

    +
      +
    • Sociaux-démocrates (S) : La motion 3936 d'Ida Karkiainen propose un rejet partiel de la proposition, acceptant l'exigence de résidence légale tout en s'opposant aux restrictions de qualification plus larges
    • +
    • Parti de gauche (V) : La motion 3932 de Tony Haddou exige un rejet total
    • +
    • Parti Vert (MP) : La motion 3942 de Malte Tängmark Roos demande également un rejet complet
    • +
    • Parti du Centre (C) : La motion 3939 d'Anders W Jonsson cherche à modifier les exceptions de qualification
    • +
    +

    Ce front d'opposition à quatre partis sur la qualification aux prestations sociales représente un alignement politique significatif. L'offensive d'interpellations des Sociaux-démocrates s'est poursuivie avec un accent sur les soins aux personnes âgées (trois interpellations séparées de Lundh Sammeli, Dahlqvist et Sundin ciblant la ministre Tenje), les priorités économiques et le dumping social entre municipalités. Cette triple offensive sur les soins aux personnes âgées indique que S entend faire de la qualité des soins un enjeu clé de la campagne électorale de 2026.

    +

    Ulrika Westerlund (MP) a contesté les fondements juridiques de l'agenda pénal du gouvernement dans l'interpellation 382, tandis qu'Annika Hirvonen (MP) a ciblé la politique d'expulsion des adolescents dans l'interpellation 381.

    +
    + +
    +

    Ce qui a compté le plus : le sprint législatif préélectoral

    +

    Le volume et l'ampleur considérables de la production législative de cette semaine révèlent un gouvernement en mode sprint préélectoral. Avec l'élection de septembre 2026 à environ six mois, la coalition Kristersson fait passer ses priorités législatives restantes à un rythme sans précédent — réforme de la citoyenneté, refonte de l'immigration, expansion nucléaire et droit de la famille socialement conservateur en une seule semaine.

    +

    La réponse coordonnée de l'opposition sur la qualification à l'assurance sociale suggère que les lignes de bataille pour la campagne de 2026 se durcissent : la coalition de droite misant sur la migration, la citoyenneté et la sécurité ; l'opposition défendant l'universalité de la protection sociale et les libertés civiles. L'offensive sur les soins aux personnes âgées ajoute une dimension de services publics qui pourrait s'avérer électoralement décisive.

    +

    Le plus lourd de conséquences est le triple jeu nucléaire, qui marque un engagement politique irréversible. L'ouverture de nouveaux sites nucléaires côtiers, combinée à des processus de sécurité et d'examen modernisés, positionne la Suède parmi les démocraties les plus pro-nucléaires d'Europe — une position aux implications durables pour les marchés de l'énergie, la politique climatique et l'orientation géopolitique pour les décennies à venir.

    +
    + +
    +

    Perspectives

    +

    La semaine prochaine (16–20 mars), le Riksdag traitera plusieurs des rapports de commission de cette semaine lors de votes en séance plénière. Suivez de près le débat sur la réforme de l'immigration de travail (SfU12), qui devrait générer des affrontements transpartisans. La proposition sur la qualification à l'assurance sociale (Prop. 136) poursuivra son parcours en commission avec des motions opposées de quatre partis garantissant un processus conflictuel.

    +

    La proposition sur la citoyenneté du gouvernement (Prop. 175) sera renvoyée à la commission des Assurances sociales, où l'opposition unie devrait opposer une résistance vigoureuse. Pendant ce temps, les propositions nucléaires seront transmises aux commissions compétentes où des auditions techniques seront probablement programmées.

    +

    Côté gouvernemental, la visite de la ministre des Affaires étrangères Malmer Stenergard au Texas et en Virginie témoigne d'un engagement continu avec les États-Unis, tandis que de nouvelles initiatives de politique commerciale européenne sont attendues à la suite des rencontres du commissaire Šefçoviç avec le ministre Dousa.

    +
    + +
    +

    Sources de données

    +

    Cette analyse est basée sur les données de l'API de données ouvertes du Parlement suédois (data.riksdagen.se) et du Gouvernement suédois via g0v.se, couvrant la période du 7 au 14 mars 2026. Tous les documents parlementaires, votes et discours référencés sont publiquement accessibles via les archives documentaires du Riksdag. Riksmöte : 2025/26.

    +
    + +
    + +
    +

    Publié par Riksdagsmonitor — Renseignement politique généré par IA basé sur des données OSINT/INTOP provenant des API de données ouvertes du Parlement et du Gouvernement suédois.

    +

    © 2026 Hack23 AB. Licensed under CC BY-SA 4.0.

    +
    + + + + \ No newline at end of file diff --git a/news/2026-03-14-weekly-review-he.html b/news/2026-03-14-weekly-review-he.html new file mode 100644 index 00000000000..f13e313d0e7 --- /dev/null +++ b/news/2026-03-14-weekly-review-he.html @@ -0,0 +1,933 @@ + + + + + + + סקירה שבועית: הפרלמנט בראי + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← חזרה לחדשות + +
    + +
    +
    + + + +
    חדשות ניתוחים אחרונים מהריקסדאג השוודי. עיתונות מודיעין פוליטי מבוססת AI ונתוני OSINT/INTOP המכסה פרלמנט, ממשלה וסוכנויות עם שקיפות שיטתית.
    +

    סקירה שבועית: הפרלמנט בראי

    + +
    + + + +
    +

    + ניתוח 50 התפתחויות מרכזיות מהשבוע שעבר +

    + +

    הפרלמנט דן ב-17 הצעת חוק ממשלתיתות — כולל "Verksamheten i Europeiska unionen under 2025" בתקופה זו.

    + +

    ניתוח נושאי

    + +

    הצעות ממשלה (17)

    +
    +

    Verksamheten i Europeiska unionen under 2025

    +

    פורסם:

    +

    מסמך ממשלתי 2025/26:115 Verksamheten i Europeiska unionen under 2025 Skr. 2025/26:115 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Jessica Rosencrantz (Statsrådsberedningen) Skrivelsens huvudsakliga innehåll I skrivelsen redogör regeringen för verksamheten מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03115

    +
    +
    +

    Förbud mot kusinäktenskap och äktenskap mellan andra nära släktingar

    +

    פורסם:

    +

    הצעת חוק ממשלתית 2025/26:154 Förbud mot kusinäktenskap och äktenskap Prop. mellan andra nära släktingar 2025/26:154 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 12 mars 2026 Ulf Kristersson Gunnar Strömmer (Justitiedepartementet) Propositionens huvudsakliga innehåll I propositionen מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03154

    +
    +
    +

    Skärpta krav för svenskt medborgarskap

    +

    פורסם:

    +

    Skärpta krav för svenskt medborgarskap Regeringens proposition 2025/26:175 Skärpta krav för svenskt medborgarskap Prop. 2025/26:175 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 12 mars 2026 Ulf Kristersson Johan Forssell (Justitiedepartementet) Propositionens huvudsakliga innehåll Det מה זה אומר: נוגע בתחומי מדיניות הגירה. הצעות הגירה חייבות לאזן בין חובות רגולטוריות של האיחוד האירופי לבין ציוויים פוליטיים לאומיים, מה שהופך תמיכה חוצת-מפלגות לחיונית לחקיקה עמידה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03175

    +
    +
    +

    Kärnämneskontroll och bedömningar av överensstämmelse med säkerhets- och strålskyddskrav i verksamhet med strålning

    +

    פורסם:

    +

    הצעת חוק ממשלתית 2025/26:167 Kärnämneskontroll och bedömningar av Prop. överensstämmelse med säkerhets- och 2025/26:167 strålskyddskrav i verksamhet med strålning Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03167

    +
    +
    +

    Utökade registerkontroller i skolväsendet

    +

    פורסם:

    +

    הצעת חוק ממשלתית 2025/26:174 Utökade registerkontroller i skolväsendet Prop. 2025/26:174 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Propositionens huvudsakliga innehåll Förskolan och skolan ska vara trygga מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03174

    +
    +
    +

    Fördjupad resultatredovisning av internationellt bistånd – Ukraina

    +

    פורסם:

    +

    מסמך ממשלתי 2025/26:97 Fördjupad resultatredovisning av internationellt Skr. bistånd Ukraina 2025/26:97 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Benjamin Dousa (Utrikesdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen ges en fördjupad מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD0397

    +
    +
    +

    Effektiv och säker byggprocess

    +

    פורסם:

    +

    Effektiv och säker byggprocess Regeringens proposition 2025/26:172 Effektiv och säker byggprocess Prop. 2025/26:172 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Andreas Carlson (Landsbygds- och infrastrukturdepartementet) Propositionens huvudsakliga innehåll מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03172

    +
    +
    +

    חוק om avgift för områdessamverkan

    +

    פורסם:

    +

    הצעת חוק ממשלתית 2025/26:157 Lag om avgift för områdessamverkan Prop. 2025/26:157 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Andreas Carlson (Landsbygds- och infrastrukturdepartementet) Propositionens huvudsakliga innehåll Regeringen föreslår en ny מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03157

    +
    +
    +

    דוח מבקר המדינה הסוודי om Statens fastighetsverks fastighetsförvaltning

    +

    פורסם:

    +

    מסמך ממשלתי 2025/26:152 Riksrevisionens rapport om Statens Skr. fastighetsverks fastighetsförvaltning 2025/26:152 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Niklas Wykman (Finansdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen behandlar מה זה אומר: נוגע בתחומי מדיניות דיור. הצעות דיור חייבות ליישב אינטרסים מתחרים של עיריות, בעלי נכסים, שוכרים ויזמים — קואליציה שלעתים נדירות מושגת במהירות. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03152

    +
    +
    +

    Ny kärnkraft i Sverige – fler möjliga platser vid kusten

    +

    פורסם:

    +

    הצעת חוק ממשלתית 2025/26:160 Ny kärnkraft i Sverige fler möjliga platser vid Prop. kusten 2025/26:160 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) Propositionens huvudsakliga innehåll Propositionen innehåller מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03160

    +
    +
    +

    En mer ändamålsenlig prövning av kärntekniska anläggningar

    +

    פורסם:

    +

    En mer ändamålsenlig prövning av kärntekniska anläggningar Regeringens proposition 2025/26:171 En mer ändamålsenlig prövning av kärntekniska Prop. anläggningar 2025/26:171 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03171

    +
    +
    +

    Ändamålsenliga säkerhets- och strålskyddskrav för utvinning och bearbetning av kärnämnen

    +

    פורסם:

    +

    הצעת חוק ממשלתית 2025/26:168 Ändamålsenliga säkerhets- och strålskyddskrav Prop. för utvinning och bearbetning av kärnämnen 2025/26:168 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) Propositionens huvudsakliga מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03168

    +
    +
    +

    Ett mer träffsäkert och korrekt bostadsbidrag

    +

    פורסם:

    +

    Ett mer träffsäkert och korrekt bostadsbidrag Regeringens proposition 2025/26:170 Ett mer träffsäkert och korrekt bostadsbidrag Prop. 2025/26:170 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Anna Tenje (Socialdepartementet) Propositionens huvudsakliga innehåll מה זה אומר: נוגע בתחומי מדיניות דיור. הצעות דיור חייבות ליישב אינטרסים מתחרים של עיריות, בעלי נכסים, שוכרים ויזמים — קואליציה שלעתים נדירות מושגת במהירות. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03170

    +
    +
    +

    דוח מבקר המדינה הסוודי om förvar i migrationsprocessen

    +

    פורסם:

    +

    מסמך ממשלתי 2025/26:137 Riksrevisionens rapport om förvar i Skr. migrationsprocessen 2025/26:137 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Gunnar Strömmer (Justitiedepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen redovisar regeringen מה זה אומר: נוגע בתחומי מדיניות הגירה. הצעות הגירה חייבות לאזן בין חובות רגולטוריות של האיחוד האירופי לבין ציוויים פוליטיים לאומיים, מה שהופך תמיכה חוצת-מפלגות לחיונית לחקיקה עמידה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03137

    +
    +
    +

    Utdelning av överskott i inkomstpensionssystemet

    +

    פורסם:

    +

    הצעת חוק ממשלתית 2025/26:169 Utdelning av överskott i Prop. inkomstpensionssystemet 2025/26:169 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Anna Tenje (Socialdepartementet) Propositionens huvudsakliga innehåll I propositionen föreslås ett regelverk מה זה אומר: מחייב בחינה בוועדה ודיון במליאה לפני קבלת החלטה. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03169

    +
    +
    +

    Framtidens yrkeshögskola

    +

    פורסם:

    +

    הצעת חוק ממשלתית 2025/26:173 Framtidens yrkeshögskola Prop. 2025/26:173 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Propositionens huvudsakliga innehåll Propositionen innehåller förslag till ändringar i lagen מה זה אומר: נוגע בתחומי מדיניות חינוך. הצעות חינוך חייבות לאזן בין תקני תוכנית לימודים ארצית, אוטונומיית יישום מוניציפלית ותפקידם השנוי במחלוקת של ספקים פרטיים במערכת החינוך השוודית. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03173

    +
    +
    +

    דוח מבקר המדינה הסוודי om utbildning på vetenskaplig grund

    +

    פורסם:

    +

    מסמך ממשלתי 2025/26:166 Riksrevisionens rapport om utbildning på Skr. vetenskaplig grund 2025/26:166 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen redovisar regeringen מה זה אומר: נוגע בתחומי מדיניות חינוך. הצעות חינוך חייבות לאזן בין תקני תוכנית לימודים ארצית, אוטונומיית יישום מוניציפלית ותפקידם השנוי במחלוקת של ספקים פרטיים במערכת החינוך השוודית. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, הצעת חוק ממשלתית זו עומדת בפני סיכון מוגבר לדחייה.

    +

    HD03166

    +
    + +

    דוחות ועדה (18)

    +
    +

    Koncentration av viss länsstyrelseverksamhet

    +

    פורסם:

    +

    KU דוח ועדה (bet). מה זה אומר: הנושא הועבר לועדת החוקה לבחינה פרלמנטרית. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01KU37

    +
    +
    +

    Integritet och ny teknik 2020–2024

    +

    פורסם:

    +

    KU דוח ועדה (bet). מה זה אומר: הנושא הועבר לועדת החוקה לבחינה פרלמנטרית. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01KU36

    +
    +
    +

    Bättre förutsättningar för digitala kommunala sammanträden och förbättrad kontroll och uppföljning av privata utförare i kommuner och regioner

    +

    פורסם:

    +

    KU דוח ועדה (bet). מה זה אומר: הנושא הועבר לועדת החוקה לבחינה פרלמנטרית. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01KU35

    +
    +
    +

    Offentlig upphandling

    +

    פורסם:

    +

    FiU דוח ועדה (bet). מה זה אומר: נוגע בתחומי מדיניות פיסקלית. עמדת ועדת הכספים בנושאים פיסקליים היא בדרך כלל מכרעת — המליאה כמעט תמיד פועלת לפי המלצתה בשאלות תקציביות. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01FiU34

    +
    +
    +

    Statlig förvaltning och statistikfrågor

    +

    פורסם:

    +

    FiU דוח ועדה (bet). מה זה אומר: נוגע בתחומי מדיניות פיסקלית. עמדת ועדת הכספים בנושאים פיסקליים היא בדרך כלל מכרעת — המליאה כמעט תמיד פועלת לפי המלצתה בשאלות תקציביות. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01FiU25

    +
    +
    +

    Kommunala frågor

    +

    פורסם:

    +

    ועדת האוצר föreslår att riksdagen säger nej till 69 förslag om kommunala frågor. Alla utom ett förslag har lämnats in under den allmänna motionstiden 2025 och handlar bland annat om det kommunalekonomiska utjämningssystemet, riktade statsbidrag, vinster inom välfärden och social dumpning. Utskottet hänvisar till befintlig lagstiftning, pågående utredningar och arbete inom de områden som motionärerna lyfter. מה זה אומר: נוגע בתחומי מדיניות פיסקלית. עמדת ועדת הכספים בנושאים פיסקליים היא בדרך כלל מכרעת — המליאה כמעט תמיד פועלת לפי המלצתה בשאלות תקציביות. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01FiU26

    +
    +
    +

    Ekonomisk familjepolitik

    +

    פורסם:

    +

    ועדת הביטוח הסוציאלי föreslår att riksdagen säger nej till 104 förslag i motioner från den allmänna motionstiden 2025. Motionerna handlar bland annat om familjepolitikens inriktning, föräldraförsäkring, barnbidrag och bostadsbidrag. Utskottet hänvisar främst till pågående arbete inom de frågor som motionerna tar upp. מה זה אומר: נוגע בתחומי מדיניות הגירה. דוחות ההגירה של ועדת הביטוח הלאומי מנווטים בין חובות חוק האיחוד האירופי של שוודיה, התחייבויות אמנת הפליטים של האו"ם ולחצים פוליטיים פנימיים. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01SfU17

    +
    +
    +

    Skärpningar av regelverket för invasiva främmande arter och naturvård

    +

    פורסם:

    +

    ועדת הסביבה והחקלאות föreslår att riksdagen säger ja till regeringens förslag om att skärpa regelverket för invasiva främmande arter. Lagförslaget innebär bland annat att den som uppsåtligen eller av grov oaktsamhet för in en invasiv främmande art till Sverige från ett annat EU-land kan dömas till böter eller fängelse i högst två år. Förslaget innebär också att den som för in invasiva främmande arter från ett annat EU-land ska vara skyldig att anmäla arten till Tullverket. מה זה אומר: נוגע בתחומי מדיניות סביבה ואקלים, מדיניות בריאות. המלצות ועדת הסביבה מאזנות בין שאפתנות אקלימית לתחרותיות כלכלית — עמדתה קובעת את קו הבסיס החקיקתי. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01MJU13

    +
    +
    +

    Finansiell stabilitet och finansmarknadsfrågor

    +

    פורסם:

    +

    ועדת האוצר har behandlat en skrivelse från regeringen om verksamheten i Internationella valutafonden (IMF) 2025. Skrivelsen behandlar bland annat institutionens arbete med övervakning, utlåning och kapacitetsutveckling för att främja ekonomisk utveckling och finansiell stabilitet. Av skrivelsen framgår även regeringens prioriteringar för IMF:s verksamhet. Liksom regeringen anser utskottet att IMF har en viktig roll i att stödja Ukraina. מה זה אומר: נוגע בתחומי מדיניות פיסקלית. עמדת ועדת הכספים בנושאים פיסקליים היא בדרך כלל מכרעת — המליאה כמעט תמיד פועלת לפי המלצתה בשאלות תקציביות. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01FiU22

    +
    +
    +

    Luftfartsfrågor

    +

    פורסם:

    +

    TU דוח ועדה (bet). מה זה אומר: נוגע בתחומי מדיניות תחבורה. דוחות ועדת התחבורה מנחים את מחזור תכנון התשתיות הלאומי של שוודיה, ומשפיעים ישירות על סדרי עדיפויות השקעה ארוכי טווח. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01TU12

    +
    +
    +

    דוח מבקר המדינה הסוודי om socialförsäkringsskyddet vid internationell rörlighet

    +

    פורסם:

    +

    SfU דוח ועדה (bet). מה זה אומר: נוגע בתחומי מדיניות הגירה. דוחות ההגירה של ועדת הביטוח הלאומי מנווטים בין חובות חוק האיחוד האירופי של שוודיה, התחייבויות אמנת הפליטים של האו"ם ולחצים פוליטיים פנימיים. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01SfU10

    +
    +
    +

    Cykelfrågor

    +

    פורסם:

    +

    TU דוח ועדה (bet). מה זה אומר: נוגע בתחומי מדיניות תחבורה. דוחות ועדת התחבורה מנחים את מחזור תכנון התשתיות הלאומי של שוודיה, ומשפיעים ישירות על סדרי עדיפויות השקעה ארוכי טווח. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01TU13

    +
    +
    +

    Hund under kontroll

    +

    פורסם:

    +

    Länsstyrelserna ska få fler verktyg för att utöva tillsyn över hundar för att förebygga och hindra att hundar orsakar skador. Som exempel kan nämnas att länsstyrelserna får tillträde till bostäder och andra utrymmen för tillsynen och att de i större utsträckning får besluta om omedelbara omhändertaganden. מה זה אומר: נוגע בתחומי מדיניות סביבה ואקלים. המלצות ועדת הסביבה מאזנות בין שאפתנות אקלימית לתחרותיות כלכלית — עמדתה קובעת את קו הבסיס החקיקתי. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01MJU15

    +
    +
    +

    Nya regler för arbetskraftsinvandring

    +

    פורסם:

    +

    ועדת הביטוח הסוציאלי föreslår att riksdagen säger ja till regeringens lagförslag där villkoren för arbetskraftsinvandring skärps, samtidigt som invandring av högkvalificerad arbetskraft främjas. Bland annat införs det ett krav på heltäckande sjukförsäkring och ett lönekrav som villkor för arbetskraftsinvandring. Vissa yrkesgrupper ska kunna undantas från lönekravet medan andra yrkesgrupper ska kunna uteslutas från möjligheten att beviljas arbetstillstånd. מה זה אומר: נוגע בתחומי מדיניות הגירה. דוחות ההגירה של ועדת הביטוח הלאומי מנווטים בין חובות חוק האיחוד האירופי של שוודיה, התחייבויות אמנת הפליטים של האו"ם ולחצים פוליטיים פנימיים. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01SfU12

    +
    +
    +

    Ändring av detaljplaner

    +

    פורסם:

    +

    CU דוח ועדה (bet). מה זה אומר: נוגע בתחומי מדיניות דיור. דוחות הדיור של ועדת ענייני אזרחים מתמודדים עם אחד האתגרים המדיניים העקשניים ביותר של שוודיה, שבו החלטות הוועדה מאפשרות או חוסמות שינויים רגולטוריים משמעותיים. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01CU44

    +
    +
    +

    Mineralpolitik

    +

    פורסם:

    +

    ועדת התעשייה והמסחר föreslår att riksdagen säger nej till 45 förslag i motioner från den allmänna motionstiden 2025. Förslagen handlar bland annat om allmänna frågor om gruv- och mineralnäring, kritiska metaller och mineral, tillståndsprocesser samt utvinning av uran. Utskottet hänvisar bland annat till pågående och utförda insatser samt till regeringens arbete med att ta fram en mineralstrategi. מה זה אומר: נוגע בתחומי מדיניות מסחר ותעשייה. ועדת התעשייה והמסחר מעצבת את הסביבה העסקית של שוודיה באמצעות דוחות שקובעים תנאים להשקעה, חדשנות ויצוא. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01NU16

    +
    +
    +

    דוח מבקר המדינה הסוודי om Arbetsförmedlingens stöd till personer med funktionsnedsättning, m.m.

    +

    פורסם:

    +

    ועדת שוק העבודה föreslår ett tillkännagivande, en uppmaning, till regeringen om att se över förordningen om särskilda insatser för personer med funktionsnedsättning som medför nedsatt arbetsförmåga. Exempel på särskilda insatser är lönebidrag, bidrag till hjälpmedel på arbetsplatsen samt tolk för personer med syn- eller hörselnedsättning. Skälet till översynen är att säkerställa att villkoren för insatserna bidrar till att syftet med det arbetsmarknadspolitiska programmet uppfylls. מה זה אומר: נוגע בתחומי מדיניות שוק העבודה. דוחות ועדת העבודה על חקיקת מקום עבודה חייבים לנווט בין אוטונומיית משא ומתן קיבוצי לבין קביעת רצפות סטטוטוריות מינימליות. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01AU6

    +
    +
    +

    Inriktning för omhändertagandet av använt kärnbränsle och radioaktivt avfall

    +

    פורסם:

    +

    FöU דוח ועדה (bet). מה זה אומר: נוגע בתחומי מדיניות ביטחון והגנה. דוחות ועדה על הגנה מעצבים את עמדת שוודיה הצבאית ומסלול ההשתלבות בנאט"ו — להחלטות כאן יש השלכות רב-עשורית. ⚠ רוב דק: עם רוב של 0 מושבים בלבד, דוח ועדה זה עומד בפני סיכון מוגבר לדחייה.

    +

    HD01FöU22

    +
    + +

    הצעות (15)

    +
    +

    בתגובה להצעה 2025/26:146 Bättre migrationsrättsliga regler för forskare och doktorander samt åtgärder för att motverka missbruk av uppehållstillstånd för studier

    +

    פורסם:

    +

    Bättre migrationsrättsliga regler för forskare och doktorander samt åtgärder för att motverka missbruk av uppehållstillstånd för studier מה זה אומר: נוגע בתחומי מדיניות הגירה. הצעות הגירה משקפות את אחד מתחומי המדיניות המתוחים ביותר בשוודיה, כאשר מפלגות חלוקות בנושאי כללי מקלט, דרישות שילוב ונהלי גירוש. הקשר היסטורי: להצעות ה-MP שיעור אישור של 29.6% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023945

    +
    +
    +

    בתגובה להצעה 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    פורסם:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap מה זה אומר: נוגע בתחומי מדיניות ביטחון והגנה. הצעות הגנה נושאות משמעות אסטרטגית מוגברת בעקבות הצטרפות שוודיה לנאט"ו, ולוחצות על הממשלה בנוגע להתחייבויות ביטחוניות ארוכות טווח. הקשר היסטורי: להצעות ה-MP שיעור אישור של 29.6% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023944

    +
    +
    +

    בתגובה להצעה 2025/26:138 En särskild straffbestämmelse för psykiskt våld

    +

    פורסם:

    +

    En särskild straffbestämmelse för psykiskt våld מה זה אומר: נוגע בתחומי מדיניות משפט. הצעות משפט עוסקות בפשיעה, ענישה ושיטור — תחומים עם בולטות ציבורית גבוהה שבהם מפלגות אופוזיציה לוחצות לעתים קרובות לצעדים מחמירים או ממוקדים יותר. הקשר היסטורי: להצעות ה-MP שיעור אישור של 29.6% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023943

    +
    +
    +

    בתגובה להצעה 2025/26:136 Kvalificering till socialförsäkringen

    +

    פורסם:

    +

    Kvalificering till socialförsäkringen מה זה אומר: נוגע בתחומי מדיניות הגירה. הצעות הגירה משקפות את אחד מתחומי המדיניות המתוחים ביותר בשוודיה, כאשר מפלגות חלוקות בנושאי כללי מקלט, דרישות שילוב ונהלי גירוש. הקשר היסטורי: להצעות ה-MP שיעור אישור של 29.6% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023942

    +
    +
    +

    בתגובה להצעה 2025/26:133 Stärkt säkerhet vid allmänna sammankomster och offentliga tillställningar

    +

    פורסם:

    +

    הצעה till riksdagen 2025/26:3941 av Lorena Delgado Varas och Daniel Riazat (båda med anledning av prop. 2025/26:133 Stärkt säkerhet vid allmänna sammankomster och offentliga tillställningar מה זה אומר: נוגע בתחומי מדיניות משפט. הצעות משפט עוסקות בפשיעה, ענישה ושיטור — תחומים עם בולטות ציבורית גבוהה שבהם מפלגות אופוזיציה לוחצות לעתים קרובות לצעדים מחמירים או ממוקדים יותר.

    +

    HD023941

    +
    +
    +

    בתגובה להצעה 2025/26:127 Slopat krav på introduktionsutbildning för övningskörning

    +

    פורסם:

    +

    Slopat krav på introduktionsutbildning för övningskörning מה זה אומר: נוגע בתחומי מדיניות חינוך, מדיניות תחבורה. הצעות חינוך משקפות חילוקי דעות עמוקים בנושאי תקני בתי ספר, שכר מורים ותפקיד בתי ספר עצמאיים — אחד הדיונים הפנימיים המתוחים ביותר בשוודיה. הקשר היסטורי: להצעות ה-C שיעור אישור של 44.6% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023940

    +
    +
    +

    בתגובה להצעה 2025/26:136 Kvalificering till socialförsäkringen

    +

    פורסם:

    +

    Kvalificering till socialförsäkringen מה זה אומר: נוגע בתחומי מדיניות הגירה. הצעות הגירה משקפות את אחד מתחומי המדיניות המתוחים ביותר בשוודיה, כאשר מפלגות חלוקות בנושאי כללי מקלט, דרישות שילוב ונהלי גירוש. הקשר היסטורי: להצעות ה-C שיעור אישור של 44.6% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023939

    +
    +
    +

    בתגובה להצעה 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    פורסם:

    +

    הצעה till riksdagen 2025/26:3938 av Mikael Larsson och Kerstin Lundgren (båda C) med anledning av prop. 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap מה זה אומר: נוגע בתחומי מדיניות ביטחון והגנה. הצעות הגנה נושאות משמעות אסטרטגית מוגברת בעקבות הצטרפות שוודיה לנאט"ו, ולוחצות על הממשלה בנוגע להתחייבויות ביטחוניות ארוכות טווח.

    +

    HD023938

    +
    +
    +

    בתגובה להצעה 2025/26:131 Sysselsättning och boende på landsbygden

    +

    פורסם:

    +

    Sysselsättning och boende på landsbygden מה זה אומר: נוגע בתחומי מדיניות דיור. הצעות דיור משקפות מתח מבני בין הביקוש לדיור בר-השגה לבין מגבלות חוק התכנון, רגולציית שכר דירה ולחצי עלויות בנייה. הקשר היסטורי: להצעות ה-C שיעור אישור של 44.6% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023937

    +
    +
    +

    בתגובה להצעה 2025/26:136 Kvalificering till socialförsäkringen

    +

    פורסם:

    +

    Kvalificering till socialförsäkringen מה זה אומר: נוגע בתחומי מדיניות הגירה. הצעות הגירה משקפות את אחד מתחומי המדיניות המתוחים ביותר בשוודיה, כאשר מפלגות חלוקות בנושאי כללי מקלט, דרישות שילוב ונהלי גירוש. הקשר היסטורי: להצעות ה-S שיעור אישור של 43.4% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023936

    +
    +
    +

    בתגובה להצעה 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    פורסם:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap מה זה אומר: נוגע בתחומי מדיניות ביטחון והגנה. הצעות הגנה נושאות משמעות אסטרטגית מוגברת בעקבות הצטרפות שוודיה לנאט"ו, ולוחצות על הממשלה בנוגע להתחייבויות ביטחוניות ארוכות טווח. הקשר היסטורי: להצעות ה-S שיעור אישור של 43.4% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023935

    +
    +
    +

    בתגובה להצעה 2025/26:134 ILO:s konvention om avskaffande av våld och trakasserier i arbetslivet och ILO:s konvention om en säker och hälsosam arbetsmiljö

    +

    פורסם:

    +

    ILO:s konvention om avskaffande av våld och trakasserier i arbetslivet och ILO:s konvention om en säker och hälsosam arbetsmiljö מה זה אומר: נוגע בתחומי מדיניות סביבה ואקלים, מדיניות שוק העבודה. הצעות אקלים משקפות לחץ פרלמנטרי גובר לדה-קרבוניזציה מהירה יותר, לעתים קרובות מכוונות לתעשיות ספציפיות או לקצב יישום המדיניות. הקשר היסטורי: להצעות ה-S שיעור אישור של 43.4% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023934

    +
    +
    +

    בתגובה להצעה 2025/26:138 En särskild straffbestämmelse för psykiskt våld

    +

    פורסם:

    +

    En särskild straffbestämmelse för psykiskt våld מה זה אומר: נוגע בתחומי מדיניות משפט. הצעות משפט עוסקות בפשיעה, ענישה ושיטור — תחומים עם בולטות ציבורית גבוהה שבהם מפלגות אופוזיציה לוחצות לעתים קרובות לצעדים מחמירים או ממוקדים יותר. הקשר היסטורי: להצעות ה-S שיעור אישור של 43.4% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023933

    +
    +
    +

    בתגובה להצעה 2025/26:136 Kvalificering till socialförsäkringen

    +

    פורסם:

    +

    Kvalificering till socialförsäkringen מה זה אומר: נוגע בתחומי מדיניות הגירה. הצעות הגירה משקפות את אחד מתחומי המדיניות המתוחים ביותר בשוודיה, כאשר מפלגות חלוקות בנושאי כללי מקלט, דרישות שילוב ונהלי גירוש. הקשר היסטורי: להצעות ה-V שיעור אישור של 34.8% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023932

    +
    +
    +

    בתגובה להצעה 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    פורסם:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap מה זה אומר: נוגע בתחומי מדיניות ביטחון והגנה. הצעות הגנה נושאות משמעות אסטרטגית מוגברת בעקבות הצטרפות שוודיה לנאט"ו, ולוחצות על הממשלה בנוגע להתחייבויות ביטחוניות ארוכות טווח. הקשר היסטורי: להצעות ה-V שיעור אישור של 34.8% (96% מכל הצעות האופוזיציה נדחות). הצעה זו מסמנת עמדה מדינית ולא שינוי חקיקתי קרוב.

    +

    HD023931

    +
    + +

    פעילות ועדות

    +
    +
      +
    • Committee on Finance: 4
    • +
    • ועדת החוקה: 3
    • +
    • Committee on Social Insurance: 3
    • +
    • Committee on Environment and Agriculture: 2
    • +
    • Committee on Transport: 2
    • +
    • Committee on Civil Affairs: 1
    • +
    • Committee on Industry and Trade: 1
    • +
    • Committee on Labour Market Affairs: 1
    • +
    • Committee on Defence: 1
    • +
    +
    + +

    השלכות מדיניות

    +

    Klimat- och näringslivsdepartementet מקבל 4 הצעות — אות לעדיפות ממשלתית.

    +

    הדינמיקות המתחרות הללו מכינות את הקרקע לעסקי הפרלמנט של מחר.

    + +
    +

    ניתוח מעמיק

    +

    מה קרה

    +

    מדיניות הגירה (10), מדיניות ביטחון והגנה (5), מדיניות פיסקלית (4), מדיניות דיור (4), מדיניות סביבה ואקלים (3), מדיניות תחבורה (3)

    +

    דוחות ועדה: 18, הצעות חוק: 17, הצעות: 15

    +

    ציר זמן והקשר

    +

    50 פריטים פרלמנטריים ב-18 ועדות פעילות מגדירים את הנוף החקיקתי הנוכחי. קצב הפעילות מסמן את הדחיפות הפוליטית המניעה הליכים אלה.

    +

    למה זה חשוב

    +

    עם 11 תחומי מדיניות בפעולה, זה מייצג דחיפה חקיקתית רחבה שתעצב היבטים רבים של החברה השוודית. היקף הפעילות הופך תקופה זו לקריטית להבנת הכיוון האסטרטגי של הממשלה.

    +

    מנצחים ומפסידים

    +

    הקואליציה שומרת על היוזמה עם 35 סעיפי חקיקה מול 15 הצעות אופוזיציה.

    +

    השפעה פוליטית

    +

    17 הצעות ממשלה ישנו, אם יאומצו, ישירות את החוק והמדיניות השוודיים, ויגעו באזרחים, עסקים ומוסדות. 18 דוחות ועדה מייצגים את שיאה של הביקורת החקיקתית עם המלצות המנחות את הצבעות הפלנום. 15 הצעות אופוזיציה מאתגרות את עמדת הממשלה, למרות שרוב ההצעות נדחות היסטורית; הן מסמנות זירות בחירות עתידיות. מרווח הרוב הדק של 0 מושבים משמעו שכל עריקות יכולה להביס צעדי ממשלה, מה שמגביר את ההימור הפוליטי.

    +

    פעולות והשלכות

    +

    אם יאומצו, 17 הצעות אלה יפעילו יישום ברשויות הממשלה ויחייבו שינויים רגולטוריים, הקצאות תקציביות והתאמות מינהליות. כישלון בהעברה יסמן חולשה קואליציונית ויעודד את האופוזיציה. 15 הצעות האופוזיציה, למרות שסביר שיידחו, מבססות את החלופות המדיניות שמפלגות האופוזיציה ידגלו בהן במחזור הבחירות הבא. דחייה אינה מפחיתה את ערכן האסטרטגי כתחמושת בקמפיין.

    +

    הערכה ביקורתית

    +

    לא קיימים נתוני דיון פלנום לפריטים אלה, מה שמגביל את יכולתנו להעריך את עומק הדיון הפרלמנטרי. יש לעקוב אחר פער מידע זה — איכות הדמוקרטיה תלויה בדיון מהותי, לא רק במעבר פרוצדורלי.

    +
    + +

    מסקנות מרכזיות

    +
    +
      +
    • 17 הצעות ממשלה, 18 דוחות ועדה, 15 הצעות עובדו בתקופה זו
    • +
    • הקשר מדיני: מדיניות פיסקלית; מדיניות הגירה; מדיניות סביבה ואקלים; מדיניות בריאות
    • +
    • עומק הניתוח: 50 מתוך 50
    • +
    • הקשר היסטורי: הרוב הנוכחי של 0 מושבים משמעו שעריקות אחת יכולה להפוך את התוצאה.
    • +
    +
    + +

    דינמיקת קואליציה

    +
    +

    הקואליציה מציגה מדדי סיכון מוגברים. ציון היציבות הוא 83, אך הערכת הסיכון של ה-CIA מצביעה על סיכון גבוה בשל שוליים של אפס מושבים.

    +
      +
    • ציון סיכון: 37/100
    • +
    • רמת סיכון: גבוה
    • +
    +
      +
    • שוליים של הרוב של הקואליציה הם רק 0 מושב(ות) — כל עריקה מסכנת את תבוסת הממשלה.
    • +
    • זוהתה הצבעה חוצת-מפלגות גבוהה במיוחד (86.99%) בנושא: יישור KD-M.
    • +
    • זוהתה הצבעה חוצת-מפלגות גבוהה במיוחד (86.34%) בנושא: יישור L-M.
    • +
    +
    + +

    פעילות שבועית

    +
    +
      +
    • מסמכים: 50
    • +
    • נאומים: 100
    • +
    • מגמת יציבות CIA: יורד ↓
    • +
    +

    תובנות מגמה: יציבות הקואליציה תנודתית מאוד — זוהו שינויים משמעותיים במסלול.

    +
    + + + + +
    +

    🇸🇪 סקירת מודיעין פרלמנטרי

    + +
    +
    +

    יציבות הקואליציה

    +
    + 83/100 + HIGH +
    +
    +
    +
    +
    +
    רמת סיכון
    high
    +
    הסתברות עריקה
    23%
    +
    מרווח הרוב
    0 מושבים
    +
    +
    +
    +

    📊 ביצועי המפלגות

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IDמפלגהמושביםהצלחה%לכידותמגמה
    SArbetarepartiet-Socialdemokraterna10643.4%94%
    SDSverigedemokraterna7163.8%99%
    MModeraterna6886.5%100%
    CCenterpartiet2444.6%95%
    VVänsterpartiet2234.8%94%
    KDKristdemokraterna1981.9%99%
    MPMiljöpartiet de gröna1829.6%96%
    LLiberalerna1678.1%100%
    +
    +
    +
    +

    התאמת הצבעות לפי נושא

    +
    +
    + KD-M alignment +
    +
    + קואליציה +
    +
    +
    + 84% +
    +
    + אופוזיציה +
    +
    +
    + 16% +
    +
    +
    +
    + L-M alignment +
    +
    + קואליציה +
    +
    +
    + 83% +
    +
    + אופוזיציה +
    +
    +
    + 17% +
    +
    +
    +
    + KD-L alignment +
    +
    + קואליציה +
    +
    +
    + 83% +
    +
    + אופוזיציה +
    +
    +
    + 17% +
    +
    +
    +
    + C-L alignment +
    +
    + קואליציה +
    +
    +
    + 78% +
    +
    + אופוזיציה +
    +
    +
    + 22% +
    +
    +
    +
    + C-KD alignment +
    +
    + קואליציה +
    +
    +
    + 77% +
    +
    + אופוזיציה +
    +
    +
    + 23% +
    +
    +
    +
    +
    +
    +

    מקור: פלטפורמת CIA — נתוני מודיעין ריקסדאג שוודי

    +
    +
    + +
    +
    +

    מקורות ונתונים

    +

    מקורות נתונים: riksdag-regering-mcp, Committee Reports, Government Propositions, Parliamentary Motions, Riksdagen Documents, Riksdagen Document Content, Riksdagen Voting Records, Riksdagen Speeches

    +

    נוצר על ידי: מערכת חדשות אוטומטית עם riksdag-regering-mcp

    +

    כלי ניתוח: עיתונות בסיוע AI עם פיקוח עריכתי

    +
    + +
    + + ← חזרה לחדשות + +
    +
    +
    + + + + + + + \ No newline at end of file diff --git a/news/2026-03-14-weekly-review-ja.html b/news/2026-03-14-weekly-review-ja.html new file mode 100644 index 00000000000..9ac1e661406 --- /dev/null +++ b/news/2026-03-14-weekly-review-ja.html @@ -0,0 +1,933 @@ + + + + + + + 週間レビュー:議会の振り返り + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← ニュースに戻る + +
    + +
    +
    + + + +
    スウェーデン議会リクスダーグの最新ニュースと分析。OSINT/INTOPデータに基づくAI生成の政治インテリジェンスジャーナリズムで、議会、政府、機関を体系的な透明性で報道。
    +

    週間レビュー:議会の振り返り

    + +
    + + + +
    +

    + 先週の50件の主要な動向の分析 +

    + +

    議会はこの期間中に17本の政府提出法案を審議しています、「Verksamheten i Europeiska unionen under 2025」を含む。

    + +

    テーマ別分析

    + +

    政府提案 (17)

    +
    +

    Verksamheten i Europeiska unionen under 2025

    +

    公開日:

    +

    政府通知 2025/26:115 Verksamheten i Europeiska unionen under 2025 Skr. 2025/26:115 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Jessica Rosencrantz (Statsrådsberedningen) Skrivelsens huvudsakliga innehåll I skrivelsen redogör regeringen för verksamheten これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03115

    +
    +
    +

    Förbud mot kusinäktenskap och äktenskap mellan andra nära släktingar

    +

    公開日:

    +

    政府法案 2025/26:154 Förbud mot kusinäktenskap och äktenskap Prop. mellan andra nära släktingar 2025/26:154 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 12 mars 2026 Ulf Kristersson Gunnar Strömmer (Justitiedepartementet) Propositionens huvudsakliga innehåll I propositionen これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03154

    +
    +
    +

    Skärpta krav för svenskt medborgarskap

    +

    公開日:

    +

    Skärpta krav för svenskt medborgarskap Regeringens proposition 2025/26:175 Skärpta krav för svenskt medborgarskap Prop. 2025/26:175 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 12 mars 2026 Ulf Kristersson Johan Forssell (Justitiedepartementet) Propositionens huvudsakliga innehåll Det これが意味すること: 移民政策に関連します。 移民提案はEUの規制義務と国内の政治的要請のバランスを取る必要があり、持続可能な立法のためには超党派の支持が不可欠です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03175

    +
    +
    +

    Kärnämneskontroll och bedömningar av överensstämmelse med säkerhets- och strålskyddskrav i verksamhet med strålning

    +

    公開日:

    +

    政府法案 2025/26:167 Kärnämneskontroll och bedömningar av Prop. överensstämmelse med säkerhets- och 2025/26:167 strålskyddskrav i verksamhet med strålning Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03167

    +
    +
    +

    Utökade registerkontroller i skolväsendet

    +

    公開日:

    +

    政府法案 2025/26:174 Utökade registerkontroller i skolväsendet Prop. 2025/26:174 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Propositionens huvudsakliga innehåll Förskolan och skolan ska vara trygga これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03174

    +
    +
    +

    Fördjupad resultatredovisning av internationellt bistånd – Ukraina

    +

    公開日:

    +

    政府通知 2025/26:97 Fördjupad resultatredovisning av internationellt Skr. bistånd Ukraina 2025/26:97 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Benjamin Dousa (Utrikesdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen ges en fördjupad これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD0397

    +
    +
    +

    Effektiv och säker byggprocess

    +

    公開日:

    +

    Effektiv och säker byggprocess Regeringens proposition 2025/26:172 Effektiv och säker byggprocess Prop. 2025/26:172 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Andreas Carlson (Landsbygds- och infrastrukturdepartementet) Propositionens huvudsakliga innehåll これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03172

    +
    +
    +

    法律 om avgift för områdessamverkan

    +

    公開日:

    +

    政府法案 2025/26:157 Lag om avgift för områdessamverkan Prop. 2025/26:157 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Andreas Carlson (Landsbygds- och infrastrukturdepartementet) Propositionens huvudsakliga innehåll Regeringen föreslår en ny これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03157

    +
    +
    +

    スウェーデン国家監査院報告 om Statens fastighetsverks fastighetsförvaltning

    +

    公開日:

    +

    政府通知 2025/26:152 Riksrevisionens rapport om Statens Skr. fastighetsverks fastighetsförvaltning 2025/26:152 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Niklas Wykman (Finansdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen behandlar これが意味すること: 住宅政策に関連します。 住宅提案は、自治体、不動産所有者、賃借人、開発業者からの競合する利益を調整する必要があります — 迅速に達成されることは稀な連合です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03152

    +
    +
    +

    Ny kärnkraft i Sverige – fler möjliga platser vid kusten

    +

    公開日:

    +

    政府法案 2025/26:160 Ny kärnkraft i Sverige fler möjliga platser vid Prop. kusten 2025/26:160 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) Propositionens huvudsakliga innehåll Propositionen innehåller これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03160

    +
    +
    +

    En mer ändamålsenlig prövning av kärntekniska anläggningar

    +

    公開日:

    +

    En mer ändamålsenlig prövning av kärntekniska anläggningar Regeringens proposition 2025/26:171 En mer ändamålsenlig prövning av kärntekniska Prop. anläggningar 2025/26:171 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03171

    +
    +
    +

    Ändamålsenliga säkerhets- och strålskyddskrav för utvinning och bearbetning av kärnämnen

    +

    公開日:

    +

    政府法案 2025/26:168 Ändamålsenliga säkerhets- och strålskyddskrav Prop. för utvinning och bearbetning av kärnämnen 2025/26:168 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) Propositionens huvudsakliga これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03168

    +
    +
    +

    Ett mer träffsäkert och korrekt bostadsbidrag

    +

    公開日:

    +

    Ett mer träffsäkert och korrekt bostadsbidrag Regeringens proposition 2025/26:170 Ett mer träffsäkert och korrekt bostadsbidrag Prop. 2025/26:170 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Anna Tenje (Socialdepartementet) Propositionens huvudsakliga innehåll これが意味すること: 住宅政策に関連します。 住宅提案は、自治体、不動産所有者、賃借人、開発業者からの競合する利益を調整する必要があります — 迅速に達成されることは稀な連合です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03170

    +
    +
    +

    スウェーデン国家監査院報告 om förvar i migrationsprocessen

    +

    公開日:

    +

    政府通知 2025/26:137 Riksrevisionens rapport om förvar i Skr. migrationsprocessen 2025/26:137 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Gunnar Strömmer (Justitiedepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen redovisar regeringen これが意味すること: 移民政策に関連します。 移民提案はEUの規制義務と国内の政治的要請のバランスを取る必要があり、持続可能な立法のためには超党派の支持が不可欠です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03137

    +
    +
    +

    Utdelning av överskott i inkomstpensionssystemet

    +

    公開日:

    +

    政府法案 2025/26:169 Utdelning av överskott i Prop. inkomstpensionssystemet 2025/26:169 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Anna Tenje (Socialdepartementet) Propositionens huvudsakliga innehåll I propositionen föreslås ett regelverk これが意味すること: 決定前に委員会審査と本会議討論が必要です。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03169

    +
    +
    +

    Framtidens yrkeshögskola

    +

    公開日:

    +

    政府法案 2025/26:173 Framtidens yrkeshögskola Prop. 2025/26:173 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Propositionens huvudsakliga innehåll Propositionen innehåller förslag till ändringar i lagen これが意味すること: 教育政策に関連します。 教育提案は、国の教育課程基準と地方自治体の実施自律性、およびスウェーデンの学校制度における民間事業者の議論の多い役割とのバランスを取る必要があります。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03173

    +
    +
    +

    スウェーデン国家監査院報告 om utbildning på vetenskaplig grund

    +

    公開日:

    +

    政府通知 2025/26:166 Riksrevisionens rapport om utbildning på Skr. vetenskaplig grund 2025/26:166 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen redovisar regeringen これが意味すること: 教育政策に関連します。 教育提案は、国の教育課程基準と地方自治体の実施自律性、およびスウェーデンの学校制度における民間事業者の議論の多い役割とのバランスを取る必要があります。 ⚠ 僅差の過半数: わずか0議席の過半数で、この政府法案は否決リスクが高まっています。

    +

    HD03166

    +
    + +

    委員会報告 (18)

    +
    +

    Koncentration av viss länsstyrelseverksamhet

    +

    公開日:

    +

    KU 委員会報告 (bet). これが意味すること: この件は憲法委員会に付託され、議会審議が行われます。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01KU37

    +
    +
    +

    Integritet och ny teknik 2020–2024

    +

    公開日:

    +

    KU 委員会報告 (bet). これが意味すること: この件は憲法委員会に付託され、議会審議が行われます。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01KU36

    +
    +
    +

    Bättre förutsättningar för digitala kommunala sammanträden och förbättrad kontroll och uppföljning av privata utförare i kommuner och regioner

    +

    公開日:

    +

    KU 委員会報告 (bet). これが意味すること: この件は憲法委員会に付託され、議会審議が行われます。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01KU35

    +
    +
    +

    Offentlig upphandling

    +

    公開日:

    +

    FiU 委員会報告 (bet). これが意味すること: 財政政策に関連します。 財政委員会の財政問題に関する立場は通常決定的です — 議場はほぼ常に予算問題に関するその勧告に従います。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01FiU34

    +
    +
    +

    Statlig förvaltning och statistikfrågor

    +

    公開日:

    +

    FiU 委員会報告 (bet). これが意味すること: 財政政策に関連します。 財政委員会の財政問題に関する立場は通常決定的です — 議場はほぼ常に予算問題に関するその勧告に従います。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01FiU25

    +
    +
    +

    Kommunala frågor

    +

    公開日:

    +

    財政委員会 föreslår att riksdagen säger nej till 69 förslag om kommunala frågor. Alla utom ett förslag har lämnats in under den allmänna motionstiden 2025 och handlar bland annat om det kommunalekonomiska utjämningssystemet, riktade statsbidrag, vinster inom välfärden och social dumpning. Utskottet hänvisar till befintlig lagstiftning, pågående utredningar och arbete inom de områden som motionärerna lyfter. これが意味すること: 財政政策に関連します。 財政委員会の財政問題に関する立場は通常決定的です — 議場はほぼ常に予算問題に関するその勧告に従います。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01FiU26

    +
    +
    +

    Ekonomisk familjepolitik

    +

    公開日:

    +

    社会保険委員会 föreslår att riksdagen säger nej till 104 förslag i motioner från den allmänna motionstiden 2025. Motionerna handlar bland annat om familjepolitikens inriktning, föräldraförsäkring, barnbidrag och bostadsbidrag. Utskottet hänvisar främst till pågående arbete inom de frågor som motionerna tar upp. これが意味すること: 移民政策に関連します。 社会保険委員会の移民報告書は、スウェーデンのEU法上の義務、国連難民条約の約束、および国内の政治的圧力の間を navigateしています。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01SfU17

    +
    +
    +

    Skärpningar av regelverket för invasiva främmande arter och naturvård

    +

    公開日:

    +

    環境農業委員会 föreslår att riksdagen säger ja till regeringens förslag om att skärpa regelverket för invasiva främmande arter. Lagförslaget innebär bland annat att den som uppsåtligen eller av grov oaktsamhet för in en invasiv främmande art till Sverige från ett annat EU-land kan dömas till böter eller fängelse i högst två år. Förslaget innebär också att den som för in invasiva främmande arter från ett annat EU-land ska vara skyldig att anmäla arten till Tullverket. これが意味すること: 環境・気候政策, 医療政策に関連します。 環境委員会の勧告は気候への野心と経済的競争力のバランスを取っています — その立場が立法の基準を設定します。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01MJU13

    +
    +
    +

    Finansiell stabilitet och finansmarknadsfrågor

    +

    公開日:

    +

    財政委員会 har behandlat en skrivelse från regeringen om verksamheten i Internationella valutafonden (IMF) 2025. Skrivelsen behandlar bland annat institutionens arbete med övervakning, utlåning och kapacitetsutveckling för att främja ekonomisk utveckling och finansiell stabilitet. Av skrivelsen framgår även regeringens prioriteringar för IMF:s verksamhet. Liksom regeringen anser utskottet att IMF har en viktig roll i att stödja Ukraina. これが意味すること: 財政政策に関連します。 財政委員会の財政問題に関する立場は通常決定的です — 議場はほぼ常に予算問題に関するその勧告に従います。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01FiU22

    +
    +
    +

    Luftfartsfrågor

    +

    公開日:

    +

    TU 委員会報告 (bet). これが意味すること: 交通政策に関連します。 交通委員会の報告書はスウェーデンの国家インフラ計画サイクルを導き、長期的な投資優先事項に直接影響します。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01TU12

    +
    +
    +

    スウェーデン国家監査院報告 om socialförsäkringsskyddet vid internationell rörlighet

    +

    公開日:

    +

    SfU 委員会報告 (bet). これが意味すること: 移民政策に関連します。 社会保険委員会の移民報告書は、スウェーデンのEU法上の義務、国連難民条約の約束、および国内の政治的圧力の間を navigateしています。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01SfU10

    +
    +
    +

    Cykelfrågor

    +

    公開日:

    +

    TU 委員会報告 (bet). これが意味すること: 交通政策に関連します。 交通委員会の報告書はスウェーデンの国家インフラ計画サイクルを導き、長期的な投資優先事項に直接影響します。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01TU13

    +
    +
    +

    Hund under kontroll

    +

    公開日:

    +

    Länsstyrelserna ska få fler verktyg för att utöva tillsyn över hundar för att förebygga och hindra att hundar orsakar skador. Som exempel kan nämnas att länsstyrelserna får tillträde till bostäder och andra utrymmen för tillsynen och att de i större utsträckning får besluta om omedelbara omhändertaganden. これが意味すること: 環境・気候政策に関連します。 環境委員会の勧告は気候への野心と経済的競争力のバランスを取っています — その立場が立法の基準を設定します。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01MJU15

    +
    +
    +

    Nya regler för arbetskraftsinvandring

    +

    公開日:

    +

    社会保険委員会 föreslår att riksdagen säger ja till regeringens lagförslag där villkoren för arbetskraftsinvandring skärps, samtidigt som invandring av högkvalificerad arbetskraft främjas. Bland annat införs det ett krav på heltäckande sjukförsäkring och ett lönekrav som villkor för arbetskraftsinvandring. Vissa yrkesgrupper ska kunna undantas från lönekravet medan andra yrkesgrupper ska kunna uteslutas från möjligheten att beviljas arbetstillstånd. これが意味すること: 移民政策に関連します。 社会保険委員会の移民報告書は、スウェーデンのEU法上の義務、国連難民条約の約束、および国内の政治的圧力の間を navigateしています。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01SfU12

    +
    +
    +

    Ändring av detaljplaner

    +

    公開日:

    +

    CU 委員会報告 (bet). これが意味すること: 住宅政策に関連します。 市民問題委員会の住宅報告書は、スウェーデンの最も根強い政策課題の一つに取り組んでおり、委員会の決定が重要な規制変更を解放または阻止します。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01CU44

    +
    +
    +

    Mineralpolitik

    +

    公開日:

    +

    産業貿易委員会 föreslår att riksdagen säger nej till 45 förslag i motioner från den allmänna motionstiden 2025. Förslagen handlar bland annat om allmänna frågor om gruv- och mineralnäring, kritiska metaller och mineral, tillståndsprocesser samt utvinning av uran. Utskottet hänvisar bland annat till pågående och utförda insatser samt till regeringens arbete med att ta fram en mineralstrategi. これが意味すること: 通商・産業政策に関連します。 産業・貿易委員会は、投資、革新、輸出の条件を設定する報告書を通じてスウェーデンのビジネス環境を形成しています。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01NU16

    +
    +
    +

    スウェーデン国家監査院報告 om Arbetsförmedlingens stöd till personer med funktionsnedsättning, m.m.

    +

    公開日:

    +

    労働市場委員会 föreslår ett tillkännagivande, en uppmaning, till regeringen om att se över förordningen om särskilda insatser för personer med funktionsnedsättning som medför nedsatt arbetsförmåga. Exempel på särskilda insatser är lönebidrag, bidrag till hjälpmedel på arbetsplatsen samt tolk för personer med syn- eller hörselnedsättning. Skälet till översynen är att säkerställa att villkoren för insatserna bidrar till att syftet med det arbetsmarknadspolitiska programmet uppfylls. これが意味すること: 労働市場政策に関連します。 労働委員会の職場立法に関する報告書は、団体交渉の自律性と法定最低基準の設定の間を navigateする必要があります。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01AU6

    +
    +
    +

    Inriktning för omhändertagandet av använt kärnbränsle och radioaktivt avfall

    +

    公開日:

    +

    FöU 委員会報告 (bet). これが意味すること: 防衛・安全保障政策に関連します。 防衛に関する委員会報告書はスウェーデンの軍事態勢とNATO統合の軌道を形成する — ここでの決定は数十年にわたる影響を持つ。 ⚠ 僅差の過半数: わずか0議席の過半数で、この委員会報告は否決リスクが高まっています。

    +

    HD01FöU22

    +
    + +

    動議 (15)

    +
    +

    法案への対応として 2025/26:146 Bättre migrationsrättsliga regler för forskare och doktorander samt åtgärder för att motverka missbruk av uppehållstillstånd för studier

    +

    公開日:

    +

    Bättre migrationsrättsliga regler för forskare och doktorander samt åtgärder för att motverka missbruk av uppehållstillstånd för studier これが意味すること: 移民政策に関連します。 移民動議はスウェーデンの最も論争の多い政策分野の一つを反映しており、亡命規則、統合要件、強制送還手続きについて政党間で意見が分かれています。 歴史的文脈:MP動議の可決率は29.6%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023945

    +
    +
    +

    法案への対応として 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    公開日:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap これが意味すること: 防衛・安全保障政策に関連します。 防衛動議はスウェーデンのNATO加盟後に戦略的重要性が高まり、政府に長期的な安全保障の約束を迫っています。 歴史的文脈:MP動議の可決率は29.6%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023944

    +
    +
    +

    法案への対応として 2025/26:138 En särskild straffbestämmelse för psykiskt våld

    +

    公開日:

    +

    En särskild straffbestämmelse för psykiskt våld これが意味すること: 司法政策に関連します。 司法動議は犯罪、量刑、治安維持を扱っています — 野党が頻繁により厳格またはより的を絞った措置を求める、社会的関心が高い分野です。 歴史的文脈:MP動議の可決率は29.6%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023943

    +
    +
    +

    法案への対応として 2025/26:136 Kvalificering till socialförsäkringen

    +

    公開日:

    +

    Kvalificering till socialförsäkringen これが意味すること: 移民政策に関連します。 移民動議はスウェーデンの最も論争の多い政策分野の一つを反映しており、亡命規則、統合要件、強制送還手続きについて政党間で意見が分かれています。 歴史的文脈:MP動議の可決率は29.6%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023942

    +
    +
    +

    法案への対応として 2025/26:133 Stärkt säkerhet vid allmänna sammankomster och offentliga tillställningar

    +

    公開日:

    +

    動議 till riksdagen 2025/26:3941 av Lorena Delgado Varas och Daniel Riazat (båda med anledning av prop. 2025/26:133 Stärkt säkerhet vid allmänna sammankomster och offentliga tillställningar これが意味すること: 司法政策に関連します。 司法動議は犯罪、量刑、治安維持を扱っています — 野党が頻繁により厳格またはより的を絞った措置を求める、社会的関心が高い分野です。

    +

    HD023941

    +
    +
    +

    法案への対応として 2025/26:127 Slopat krav på introduktionsutbildning för övningskörning

    +

    公開日:

    +

    Slopat krav på introduktionsutbildning för övningskörning これが意味すること: 教育政策, 交通政策に関連します。 教育動議は、学校基準、教員給与、独立学校の役割に関する深い意見の相違を反映しています — スウェーデンの最も論争の多い国内議論の一つです。 歴史的文脈:C動議の可決率は44.6%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023940

    +
    +
    +

    法案への対応として 2025/26:136 Kvalificering till socialförsäkringen

    +

    公開日:

    +

    Kvalificering till socialförsäkringen これが意味すること: 移民政策に関連します。 移民動議はスウェーデンの最も論争の多い政策分野の一つを反映しており、亡命規則、統合要件、強制送還手続きについて政党間で意見が分かれています。 歴史的文脈:C動議の可決率は44.6%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023939

    +
    +
    +

    法案への対応として 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    公開日:

    +

    動議 till riksdagen 2025/26:3938 av Mikael Larsson och Kerstin Lundgren (båda C) med anledning av prop. 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap これが意味すること: 防衛・安全保障政策に関連します。 防衛動議はスウェーデンのNATO加盟後に戦略的重要性が高まり、政府に長期的な安全保障の約束を迫っています。

    +

    HD023938

    +
    +
    +

    法案への対応として 2025/26:131 Sysselsättning och boende på landsbygden

    +

    公開日:

    +

    Sysselsättning och boende på landsbygden これが意味すること: 住宅政策に関連します。 住宅動議は、手頃な住宅への需要と都市計画法、家賃規制、建設コスト圧力の制約との間の構造的な緊張を反映しています。 歴史的文脈:C動議の可決率は44.6%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023937

    +
    +
    +

    法案への対応として 2025/26:136 Kvalificering till socialförsäkringen

    +

    公開日:

    +

    Kvalificering till socialförsäkringen これが意味すること: 移民政策に関連します。 移民動議はスウェーデンの最も論争の多い政策分野の一つを反映しており、亡命規則、統合要件、強制送還手続きについて政党間で意見が分かれています。 歴史的文脈:S動議の可決率は43.4%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023936

    +
    +
    +

    法案への対応として 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    公開日:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap これが意味すること: 防衛・安全保障政策に関連します。 防衛動議はスウェーデンのNATO加盟後に戦略的重要性が高まり、政府に長期的な安全保障の約束を迫っています。 歴史的文脈:S動議の可決率は43.4%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023935

    +
    +
    +

    法案への対応として 2025/26:134 ILO:s konvention om avskaffande av våld och trakasserier i arbetslivet och ILO:s konvention om en säker och hälsosam arbetsmiljö

    +

    公開日:

    +

    ILO:s konvention om avskaffande av våld och trakasserier i arbetslivet och ILO:s konvention om en säker och hälsosam arbetsmiljö これが意味すること: 環境・気候政策, 労働市場政策に関連します。 気候動議は、より速い脱炭素化に向けた議会の圧力の高まりを反映しており、特定の産業や政策実施のペースを標的にすることが多い。 歴史的文脈:S動議の可決率は43.4%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023934

    +
    +
    +

    法案への対応として 2025/26:138 En särskild straffbestämmelse för psykiskt våld

    +

    公開日:

    +

    En särskild straffbestämmelse för psykiskt våld これが意味すること: 司法政策に関連します。 司法動議は犯罪、量刑、治安維持を扱っています — 野党が頻繁により厳格またはより的を絞った措置を求める、社会的関心が高い分野です。 歴史的文脈:S動議の可決率は43.4%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023933

    +
    +
    +

    法案への対応として 2025/26:136 Kvalificering till socialförsäkringen

    +

    公開日:

    +

    Kvalificering till socialförsäkringen これが意味すること: 移民政策に関連します。 移民動議はスウェーデンの最も論争の多い政策分野の一つを反映しており、亡命規則、統合要件、強制送還手続きについて政党間で意見が分かれています。 歴史的文脈:V動議の可決率は34.8%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023932

    +
    +
    +

    法案への対応として 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    公開日:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap これが意味すること: 防衛・安全保障政策に関連します。 防衛動議はスウェーデンのNATO加盟後に戦略的重要性が高まり、政府に長期的な安全保障の約束を迫っています。 歴史的文脈:V動議の可決率は34.8%(野党動議の96%は否決されます)。 この動議は、差し迫った法改正ではなく、政策立場を示すものです。

    +

    HD023931

    +
    + +

    委員会活動

    +
    +
      +
    • Committee on Finance: 4
    • +
    • 憲法委員会: 3
    • +
    • Committee on Social Insurance: 3
    • +
    • Committee on Environment and Agriculture: 2
    • +
    • Committee on Transport: 2
    • +
    • Committee on Civil Affairs: 1
    • +
    • Committee on Industry and Trade: 1
    • +
    • Committee on Labour Market Affairs: 1
    • +
    • Committee on Defence: 1
    • +
    +
    + +

    政策への影響

    +

    Klimat- och näringslivsdepartementetは4件の提案を受け取り、政府の重点分野を示しています。

    +

    これらの競合するダイナミクスが明日の議会業務の舞台を整えています。

    + +
    +

    深層分析

    +

    何が起きたか

    +

    移民政策 (10), 防衛・安全保障政策 (5), 財政政策 (4), 住宅政策 (4), 環境・気候政策 (3), 交通政策 (3)

    +

    委員会報告書: 18, 法案: 17, 動議: 15

    +

    タイムライン&コンテキスト

    +

    18の活動委員会にわたる50の議会議題が現在の立法環境を定義しています。 活動のペースは、これらの議事を推進する政治的緊急性を示しています。

    +

    なぜ重要か

    +

    11の政策分野が関与するこの状況は、スウェーデン社会の多くの側面を形成する広範な立法推進を表しています。 活動の幅広さは、政府の戦略的方向性を理解するための重要な期間としています。

    +

    勝者と敗者

    +

    与党連合は35の立法項目で主導権を握り、野党の15動議に対して優位に立っています。

    +

    政治的影響

    +

    17の政府提案は採択されれば、スウェーデンの法律と政策を直接変更し、市民・企業・機関に影響を与えます。18の委員会報告書は立法審査の頂点を表し、その勧告が本会議投票を導きます。15の野党動議は政府の立場に挑戦しており、歴史的にほとんどが否決されますが、将来の選挙戦場を示しています。 0議席の僅差の過半数マージンは、いかなる離反も政府の施策を破る可能性があることを意味し、政治的リスクを増幅させています。

    +

    行動と結果

    +

    採択されれば、これら17の提案は政府機関全体での実施を開始し、規制変更・予算配分・行政適応が必要となります。 通過の失敗は連立の弱さを示し、野党を勢いづかせるでしょう。 15件の野党動議は、否決される可能性が高いものの、野党が次の選挙サイクルで推進する政策代替案を確立します。否決されてもキャンペーン弾薬としての戦略的価値は損なわれません。

    +

    批判的評価

    +

    これらの議題に関する本会議討論データが入手できず、議会審議の深さを評価する能力が制限されています。 この情報格差は監視されるべきです — 民主主義の質は、手続き上の通過だけでなく、実質的な議論に依存しています。

    +
    + +

    主要ポイント

    +
    +
      +
    • 17 政府提案, 18 委員会報告, 15 動議 この期間に処理
    • +
    • 政策的背景: 財政政策; 移民政策; 環境・気候政策; 医療政策
    • +
    • 分析の深さ: 50 / 50
    • +
    • 歴史的背景:現在の0議席差は、1人の離反で結果が覆る可能性を意味します。
    • +
    +
    + +

    連立の動向

    +
    +

    連立は高まったリスク指標を示しています。安定スコアは83ですが、CIA危険評価は議席差ゼロによるHIGHリスクを示しています。

    +
      +
    • リスクスコア: 37/100
    • +
    • リスクレベル:
    • +
    +
      +
    • 連立の多数議席差はわずか0議席 — 離脱があれば政府敗北のリスクがあります。
    • +
    • KD-M連携のテーマで異常に高い党超越投票率(86.99%)を検出しました。
    • +
    • L-M連携のテーマで異常に高い党超越投票率(86.34%)を検出しました。
    • +
    +
    + +

    今週の活動

    +
    +
      +
    • 文書: 50
    • +
    • 演説: 100
    • +
    • CIA安定性トレンド: 低下中 ↓
    • +
    +

    トレンド考察: 連立の安定性は非常に不安定です — 軌跡に重大な変動が検出されました。

    +
    + + + + +
    +

    🇸🇪 議会インテリジェンス概要

    + +
    +
    +

    連立安定性

    +
    + 83/100 + HIGH +
    +
    +
    +
    +
    +
    リスクレベル
    high
    +
    離脱確率
    23%
    +
    過半数マージン
    0 議席
    +
    +
    +
    +

    📊 政党パフォーマンス

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ID政党議席成功率%結束度トレンド
    SArbetarepartiet-Socialdemokraterna10643.4%94%
    SDSverigedemokraterna7163.8%99%
    MModeraterna6886.5%100%
    CCenterpartiet2444.6%95%
    VVänsterpartiet2234.8%94%
    KDKristdemokraterna1981.9%99%
    MPMiljöpartiet de gröna1829.6%96%
    LLiberalerna1678.1%100%
    +
    +
    +
    +

    課題別投票一致率

    +
    +
    + KD-M alignment +
    +
    + 連立 +
    +
    +
    + 84% +
    +
    + 野党 +
    +
    +
    + 16% +
    +
    +
    +
    + L-M alignment +
    +
    + 連立 +
    +
    +
    + 83% +
    +
    + 野党 +
    +
    +
    + 17% +
    +
    +
    +
    + KD-L alignment +
    +
    + 連立 +
    +
    +
    + 83% +
    +
    + 野党 +
    +
    +
    + 17% +
    +
    +
    +
    + C-L alignment +
    +
    + 連立 +
    +
    +
    + 78% +
    +
    + 野党 +
    +
    +
    + 22% +
    +
    +
    +
    + C-KD alignment +
    +
    + 連立 +
    +
    +
    + 77% +
    +
    + 野党 +
    +
    +
    + 23% +
    +
    +
    +
    +
    +
    +

    出典: CIAプラットフォーム — スウェーデン議会インテリジェンスデータ

    +
    +
    + +
    +
    +

    ソースとデータ

    +

    データソース: riksdag-regering-mcp, Committee Reports, Government Propositions, Parliamentary Motions, Riksdagen Documents, Riksdagen Document Content, Riksdagen Voting Records, Riksdagen Speeches

    +

    生成者: riksdag-regering-mcpによる自動ニュースシステム

    +

    分析ツール: AI支援ジャーナリズム(編集監督付き)

    +
    + +
    + + ← ニュースに戻る + +
    +
    +
    + + + + + + + \ No newline at end of file diff --git a/news/2026-03-14-weekly-review-ko.html b/news/2026-03-14-weekly-review-ko.html new file mode 100644 index 00000000000..1b0fefb3886 --- /dev/null +++ b/news/2026-03-14-weekly-review-ko.html @@ -0,0 +1,933 @@ + + + + + + + 주간 리뷰: 의회 회고 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← 뉴스로 돌아가기 + +
    + +
    +
    + + + +
    스웨덴 의회 릭스다그의 최신 뉴스와 분석. OSINT/INTOP 데이터 기반 AI 생성 정치 인텔리전스 저널리즘으로 의회, 정부, 기관을 체계적인 투명성으로 보도.
    +

    주간 리뷰: 의회 회고

    + +
    + + + +
    +

    + 지난 주 50건의 주요 동향 분석 +

    + +

    의회는 이 기간 동안 17건의 정부 법안을 심의하고 있습니다, "Verksamheten i Europeiska unionen under 2025" 포함.

    + +

    주제별 분석

    + +

    정부 법안 (17)

    +
    +

    Verksamheten i Europeiska unionen under 2025

    +

    게시일:

    +

    정부 서한 2025/26:115 Verksamheten i Europeiska unionen under 2025 Skr. 2025/26:115 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Jessica Rosencrantz (Statsrådsberedningen) Skrivelsens huvudsakliga innehåll I skrivelsen redogör regeringen för verksamheten 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03115

    +
    +
    +

    Förbud mot kusinäktenskap och äktenskap mellan andra nära släktingar

    +

    게시일:

    +

    정부 법안 2025/26:154 Förbud mot kusinäktenskap och äktenskap Prop. mellan andra nära släktingar 2025/26:154 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 12 mars 2026 Ulf Kristersson Gunnar Strömmer (Justitiedepartementet) Propositionens huvudsakliga innehåll I propositionen 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03154

    +
    +
    +

    Skärpta krav för svenskt medborgarskap

    +

    게시일:

    +

    Skärpta krav för svenskt medborgarskap Regeringens proposition 2025/26:175 Skärpta krav för svenskt medborgarskap Prop. 2025/26:175 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 12 mars 2026 Ulf Kristersson Johan Forssell (Justitiedepartementet) Propositionens huvudsakliga innehåll Det 이것이 의미하는 바: 이민 정책 분야에 관련됩니다. 이민 제안은 EU 규제 의무와 국가 정치적 요구 사이의 균형을 맞춰야 하며, 지속 가능한 입법을 위해 초당적 지원이 필수적입니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03175

    +
    +
    +

    Kärnämneskontroll och bedömningar av överensstämmelse med säkerhets- och strålskyddskrav i verksamhet med strålning

    +

    게시일:

    +

    정부 법안 2025/26:167 Kärnämneskontroll och bedömningar av Prop. överensstämmelse med säkerhets- och 2025/26:167 strålskyddskrav i verksamhet med strålning Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03167

    +
    +
    +

    Utökade registerkontroller i skolväsendet

    +

    게시일:

    +

    정부 법안 2025/26:174 Utökade registerkontroller i skolväsendet Prop. 2025/26:174 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Propositionens huvudsakliga innehåll Förskolan och skolan ska vara trygga 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03174

    +
    +
    +

    Fördjupad resultatredovisning av internationellt bistånd – Ukraina

    +

    게시일:

    +

    정부 서한 2025/26:97 Fördjupad resultatredovisning av internationellt Skr. bistånd Ukraina 2025/26:97 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Benjamin Dousa (Utrikesdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen ges en fördjupad 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD0397

    +
    +
    +

    Effektiv och säker byggprocess

    +

    게시일:

    +

    Effektiv och säker byggprocess Regeringens proposition 2025/26:172 Effektiv och säker byggprocess Prop. 2025/26:172 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Andreas Carlson (Landsbygds- och infrastrukturdepartementet) Propositionens huvudsakliga innehåll 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03172

    +
    +
    +

    법률 om avgift för områdessamverkan

    +

    게시일:

    +

    정부 법안 2025/26:157 Lag om avgift för områdessamverkan Prop. 2025/26:157 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Andreas Carlson (Landsbygds- och infrastrukturdepartementet) Propositionens huvudsakliga innehåll Regeringen föreslår en ny 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03157

    +
    +
    +

    스웨덴 국가감사원 보고서 om Statens fastighetsverks fastighetsförvaltning

    +

    게시일:

    +

    정부 서한 2025/26:152 Riksrevisionens rapport om Statens Skr. fastighetsverks fastighetsförvaltning 2025/26:152 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Niklas Wykman (Finansdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen behandlar 이것이 의미하는 바: 주택 정책 분야에 관련됩니다. 주택 제안은 지방자치단체, 부동산 소유자, 세입자, 개발업자의 경쟁적 이해관계를 조정해야 합니다 — 빠르게 달성되는 것이 드문 연합입니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03152

    +
    +
    +

    Ny kärnkraft i Sverige – fler möjliga platser vid kusten

    +

    게시일:

    +

    정부 법안 2025/26:160 Ny kärnkraft i Sverige fler möjliga platser vid Prop. kusten 2025/26:160 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) Propositionens huvudsakliga innehåll Propositionen innehåller 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03160

    +
    +
    +

    En mer ändamålsenlig prövning av kärntekniska anläggningar

    +

    게시일:

    +

    En mer ändamålsenlig prövning av kärntekniska anläggningar Regeringens proposition 2025/26:171 En mer ändamålsenlig prövning av kärntekniska Prop. anläggningar 2025/26:171 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03171

    +
    +
    +

    Ändamålsenliga säkerhets- och strålskyddskrav för utvinning och bearbetning av kärnämnen

    +

    게시일:

    +

    정부 법안 2025/26:168 Ändamålsenliga säkerhets- och strålskyddskrav Prop. för utvinning och bearbetning av kärnämnen 2025/26:168 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) Propositionens huvudsakliga 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03168

    +
    +
    +

    Ett mer träffsäkert och korrekt bostadsbidrag

    +

    게시일:

    +

    Ett mer träffsäkert och korrekt bostadsbidrag Regeringens proposition 2025/26:170 Ett mer träffsäkert och korrekt bostadsbidrag Prop. 2025/26:170 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Anna Tenje (Socialdepartementet) Propositionens huvudsakliga innehåll 이것이 의미하는 바: 주택 정책 분야에 관련됩니다. 주택 제안은 지방자치단체, 부동산 소유자, 세입자, 개발업자의 경쟁적 이해관계를 조정해야 합니다 — 빠르게 달성되는 것이 드문 연합입니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03170

    +
    +
    +

    스웨덴 국가감사원 보고서 om förvar i migrationsprocessen

    +

    게시일:

    +

    정부 서한 2025/26:137 Riksrevisionens rapport om förvar i Skr. migrationsprocessen 2025/26:137 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Gunnar Strömmer (Justitiedepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen redovisar regeringen 이것이 의미하는 바: 이민 정책 분야에 관련됩니다. 이민 제안은 EU 규제 의무와 국가 정치적 요구 사이의 균형을 맞춰야 하며, 지속 가능한 입법을 위해 초당적 지원이 필수적입니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03137

    +
    +
    +

    Utdelning av överskott i inkomstpensionssystemet

    +

    게시일:

    +

    정부 법안 2025/26:169 Utdelning av överskott i Prop. inkomstpensionssystemet 2025/26:169 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Anna Tenje (Socialdepartementet) Propositionens huvudsakliga innehåll I propositionen föreslås ett regelverk 이것이 의미하는 바: 결정 전에 위원회 심사와 본회의 토론이 필요합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03169

    +
    +
    +

    Framtidens yrkeshögskola

    +

    게시일:

    +

    정부 법안 2025/26:173 Framtidens yrkeshögskola Prop. 2025/26:173 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Propositionens huvudsakliga innehåll Propositionen innehåller förslag till ändringar i lagen 이것이 의미하는 바: 교육 정책 분야에 관련됩니다. 교육 제안은 국가 교육과정 기준과 지방자치단체의 실행 자율성, 그리고 스웨덴 학교 시스템에서의 민간 제공자의 논쟁적 역할 사이의 균형을 맞춰야 합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03173

    +
    +
    +

    스웨덴 국가감사원 보고서 om utbildning på vetenskaplig grund

    +

    게시일:

    +

    정부 서한 2025/26:166 Riksrevisionens rapport om utbildning på Skr. vetenskaplig grund 2025/26:166 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen redovisar regeringen 이것이 의미하는 바: 교육 정책 분야에 관련됩니다. 교육 제안은 국가 교육과정 기준과 지방자치단체의 실행 자율성, 그리고 스웨덴 학교 시스템에서의 민간 제공자의 논쟁적 역할 사이의 균형을 맞춰야 합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 정부 법안은 부결 위험이 높아졌습니다.

    +

    HD03166

    +
    + +

    위원회 보고서 (18)

    +
    +

    Koncentration av viss länsstyrelseverksamhet

    +

    게시일:

    +

    KU 위원회 보고서 (bet). 이것이 의미하는 바: 이 안건은 헌법위원회에 회부되어 의회 심의를 받습니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01KU37

    +
    +
    +

    Integritet och ny teknik 2020–2024

    +

    게시일:

    +

    KU 위원회 보고서 (bet). 이것이 의미하는 바: 이 안건은 헌법위원회에 회부되어 의회 심의를 받습니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01KU36

    +
    +
    +

    Bättre förutsättningar för digitala kommunala sammanträden och förbättrad kontroll och uppföljning av privata utförare i kommuner och regioner

    +

    게시일:

    +

    KU 위원회 보고서 (bet). 이것이 의미하는 바: 이 안건은 헌법위원회에 회부되어 의회 심의를 받습니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01KU35

    +
    +
    +

    Offentlig upphandling

    +

    게시일:

    +

    FiU 위원회 보고서 (bet). 이것이 의미하는 바: 재정 정책 분야에 관련됩니다. 재정위원회의 재정 문제에 대한 입장은 대개 결정적입니다 — 의회는 거의 항상 예산 문제에 관한 위원회의 권고를 따릅니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01FiU34

    +
    +
    +

    Statlig förvaltning och statistikfrågor

    +

    게시일:

    +

    FiU 위원회 보고서 (bet). 이것이 의미하는 바: 재정 정책 분야에 관련됩니다. 재정위원회의 재정 문제에 대한 입장은 대개 결정적입니다 — 의회는 거의 항상 예산 문제에 관한 위원회의 권고를 따릅니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01FiU25

    +
    +
    +

    Kommunala frågor

    +

    게시일:

    +

    재정위원회 föreslår att riksdagen säger nej till 69 förslag om kommunala frågor. Alla utom ett förslag har lämnats in under den allmänna motionstiden 2025 och handlar bland annat om det kommunalekonomiska utjämningssystemet, riktade statsbidrag, vinster inom välfärden och social dumpning. Utskottet hänvisar till befintlig lagstiftning, pågående utredningar och arbete inom de områden som motionärerna lyfter. 이것이 의미하는 바: 재정 정책 분야에 관련됩니다. 재정위원회의 재정 문제에 대한 입장은 대개 결정적입니다 — 의회는 거의 항상 예산 문제에 관한 위원회의 권고를 따릅니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01FiU26

    +
    +
    +

    Ekonomisk familjepolitik

    +

    게시일:

    +

    사회보험위원회 föreslår att riksdagen säger nej till 104 förslag i motioner från den allmänna motionstiden 2025. Motionerna handlar bland annat om familjepolitikens inriktning, föräldraförsäkring, barnbidrag och bostadsbidrag. Utskottet hänvisar främst till pågående arbete inom de frågor som motionerna tar upp. 이것이 의미하는 바: 이민 정책 분야에 관련됩니다. 사회보험위원회의 이민 보고서는 스웨덴의 EU법 의무, UN 난민협약 약속, 그리고 국내 정치적 압력 사이를 조율합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01SfU17

    +
    +
    +

    Skärpningar av regelverket för invasiva främmande arter och naturvård

    +

    게시일:

    +

    환경농업위원회 föreslår att riksdagen säger ja till regeringens förslag om att skärpa regelverket för invasiva främmande arter. Lagförslaget innebär bland annat att den som uppsåtligen eller av grov oaktsamhet för in en invasiv främmande art till Sverige från ett annat EU-land kan dömas till böter eller fängelse i högst två år. Förslaget innebär också att den som för in invasiva främmande arter från ett annat EU-land ska vara skyldig att anmäla arten till Tullverket. 이것이 의미하는 바: 환경·기후 정책, 보건 정책 분야에 관련됩니다. 환경위원회의 권고는 기후 야심과 경제적 경쟁력 사이의 균형을 맞춥니다 — 그 입장이 입법 기준선을 설정합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01MJU13

    +
    +
    +

    Finansiell stabilitet och finansmarknadsfrågor

    +

    게시일:

    +

    재정위원회 har behandlat en skrivelse från regeringen om verksamheten i Internationella valutafonden (IMF) 2025. Skrivelsen behandlar bland annat institutionens arbete med övervakning, utlåning och kapacitetsutveckling för att främja ekonomisk utveckling och finansiell stabilitet. Av skrivelsen framgår även regeringens prioriteringar för IMF:s verksamhet. Liksom regeringen anser utskottet att IMF har en viktig roll i att stödja Ukraina. 이것이 의미하는 바: 재정 정책 분야에 관련됩니다. 재정위원회의 재정 문제에 대한 입장은 대개 결정적입니다 — 의회는 거의 항상 예산 문제에 관한 위원회의 권고를 따릅니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01FiU22

    +
    +
    +

    Luftfartsfrågor

    +

    게시일:

    +

    TU 위원회 보고서 (bet). 이것이 의미하는 바: 교통 정책 분야에 관련됩니다. 교통위원회의 보고서는 스웨덴의 국가 인프라 계획 주기를 안내하며, 장기 투자 우선순위에 직접적으로 영향을 미칩니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01TU12

    +
    +
    +

    스웨덴 국가감사원 보고서 om socialförsäkringsskyddet vid internationell rörlighet

    +

    게시일:

    +

    SfU 위원회 보고서 (bet). 이것이 의미하는 바: 이민 정책 분야에 관련됩니다. 사회보험위원회의 이민 보고서는 스웨덴의 EU법 의무, UN 난민협약 약속, 그리고 국내 정치적 압력 사이를 조율합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01SfU10

    +
    +
    +

    Cykelfrågor

    +

    게시일:

    +

    TU 위원회 보고서 (bet). 이것이 의미하는 바: 교통 정책 분야에 관련됩니다. 교통위원회의 보고서는 스웨덴의 국가 인프라 계획 주기를 안내하며, 장기 투자 우선순위에 직접적으로 영향을 미칩니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01TU13

    +
    +
    +

    Hund under kontroll

    +

    게시일:

    +

    Länsstyrelserna ska få fler verktyg för att utöva tillsyn över hundar för att förebygga och hindra att hundar orsakar skador. Som exempel kan nämnas att länsstyrelserna får tillträde till bostäder och andra utrymmen för tillsynen och att de i större utsträckning får besluta om omedelbara omhändertaganden. 이것이 의미하는 바: 환경·기후 정책 분야에 관련됩니다. 환경위원회의 권고는 기후 야심과 경제적 경쟁력 사이의 균형을 맞춥니다 — 그 입장이 입법 기준선을 설정합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01MJU15

    +
    +
    +

    Nya regler för arbetskraftsinvandring

    +

    게시일:

    +

    사회보험위원회 föreslår att riksdagen säger ja till regeringens lagförslag där villkoren för arbetskraftsinvandring skärps, samtidigt som invandring av högkvalificerad arbetskraft främjas. Bland annat införs det ett krav på heltäckande sjukförsäkring och ett lönekrav som villkor för arbetskraftsinvandring. Vissa yrkesgrupper ska kunna undantas från lönekravet medan andra yrkesgrupper ska kunna uteslutas från möjligheten att beviljas arbetstillstånd. 이것이 의미하는 바: 이민 정책 분야에 관련됩니다. 사회보험위원회의 이민 보고서는 스웨덴의 EU법 의무, UN 난민협약 약속, 그리고 국내 정치적 압력 사이를 조율합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01SfU12

    +
    +
    +

    Ändring av detaljplaner

    +

    게시일:

    +

    CU 위원회 보고서 (bet). 이것이 의미하는 바: 주택 정책 분야에 관련됩니다. 민사위원회의 주택 보고서는 스웨덴의 가장 지속적인 정책 과제 중 하나를 다루며, 위원회 결정이 주요 규제 변경을 가능하게 하거나 차단합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01CU44

    +
    +
    +

    Mineralpolitik

    +

    게시일:

    +

    산업통상위원회 föreslår att riksdagen säger nej till 45 förslag i motioner från den allmänna motionstiden 2025. Förslagen handlar bland annat om allmänna frågor om gruv- och mineralnäring, kritiska metaller och mineral, tillståndsprocesser samt utvinning av uran. Utskottet hänvisar bland annat till pågående och utförda insatser samt till regeringens arbete med att ta fram en mineralstrategi. 이것이 의미하는 바: 통상·산업 정책 분야에 관련됩니다. 산업통상위원회는 투자, 혁신, 수출 조건을 설정하는 보고서를 통해 스웨덴의 기업 환경을 형성합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01NU16

    +
    +
    +

    스웨덴 국가감사원 보고서 om Arbetsförmedlingens stöd till personer med funktionsnedsättning, m.m.

    +

    게시일:

    +

    노동시장위원회 föreslår ett tillkännagivande, en uppmaning, till regeringen om att se över förordningen om särskilda insatser för personer med funktionsnedsättning som medför nedsatt arbetsförmåga. Exempel på särskilda insatser är lönebidrag, bidrag till hjälpmedel på arbetsplatsen samt tolk för personer med syn- eller hörselnedsättning. Skälet till översynen är att säkerställa att villkoren för insatserna bidrar till att syftet med det arbetsmarknadspolitiska programmet uppfylls. 이것이 의미하는 바: 노동시장 정책 분야에 관련됩니다. 노동위원회의 직장 법률에 관한 보고서는 단체교섭 자율성과 법적 최소 기준 설정 사이를 조율해야 합니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01AU6

    +
    +
    +

    Inriktning för omhändertagandet av använt kärnbränsle och radioaktivt avfall

    +

    게시일:

    +

    FöU 위원회 보고서 (bet). 이것이 의미하는 바: 국방·안보 정책 분야에 관련됩니다. 국방에 관한 위원회 보고서는 스웨덴의 군사 태세와 NATO 통합 궤적을 형성합니다 — 이곳의 결정은 수십 년에 걸친 결과를 가져옵니다. ⚠ 근소한 과반: 0석의 근소한 과반으로, 이 위원회 보고서는 부결 위험이 높아졌습니다.

    +

    HD01FöU22

    +
    + +

    동의 (15)

    +
    +

    의안에 대한 답변 2025/26:146 Bättre migrationsrättsliga regler för forskare och doktorander samt åtgärder för att motverka missbruk av uppehållstillstånd för studier

    +

    게시일:

    +

    Bättre migrationsrättsliga regler för forskare och doktorander samt åtgärder för att motverka missbruk av uppehållstillstånd för studier 이것이 의미하는 바: 이민 정책 분야에 관련됩니다. 이민 동의안은 스웨덴에서 가장 논쟁적인 정책 분야 중 하나를 반영하며, 망명 규정, 통합 요건, 추방 절차에 대해 정당들이 나뉘어 있습니다. 역사적 맥락: MP 발의안은 29.6% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023945

    +
    +
    +

    의안에 대한 답변 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    게시일:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap 이것이 의미하는 바: 국방·안보 정책 분야에 관련됩니다. 국방 동의안은 스웨덴의 NATO 가입 이후 전략적 중요성이 높아졌으며, 정부에 장기적 안보 약속을 압박하고 있습니다. 역사적 맥락: MP 발의안은 29.6% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023944

    +
    +
    +

    의안에 대한 답변 2025/26:138 En särskild straffbestämmelse för psykiskt våld

    +

    게시일:

    +

    En särskild straffbestämmelse för psykiskt våld 이것이 의미하는 바: 사법 정책 분야에 관련됩니다. 사법 동의안은 범죄, 양형, 치안 문제를 다룹니다 — 야당이 더 강력하거나 더 표적화된 조치를 자주 요구하는 공적 관심이 높은 분야입니다. 역사적 맥락: MP 발의안은 29.6% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023943

    +
    +
    +

    의안에 대한 답변 2025/26:136 Kvalificering till socialförsäkringen

    +

    게시일:

    +

    Kvalificering till socialförsäkringen 이것이 의미하는 바: 이민 정책 분야에 관련됩니다. 이민 동의안은 스웨덴에서 가장 논쟁적인 정책 분야 중 하나를 반영하며, 망명 규정, 통합 요건, 추방 절차에 대해 정당들이 나뉘어 있습니다. 역사적 맥락: MP 발의안은 29.6% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023942

    +
    +
    +

    의안에 대한 답변 2025/26:133 Stärkt säkerhet vid allmänna sammankomster och offentliga tillställningar

    +

    게시일:

    +

    동의 till riksdagen 2025/26:3941 av Lorena Delgado Varas och Daniel Riazat (båda med anledning av prop. 2025/26:133 Stärkt säkerhet vid allmänna sammankomster och offentliga tillställningar 이것이 의미하는 바: 사법 정책 분야에 관련됩니다. 사법 동의안은 범죄, 양형, 치안 문제를 다룹니다 — 야당이 더 강력하거나 더 표적화된 조치를 자주 요구하는 공적 관심이 높은 분야입니다.

    +

    HD023941

    +
    +
    +

    의안에 대한 답변 2025/26:127 Slopat krav på introduktionsutbildning för övningskörning

    +

    게시일:

    +

    Slopat krav på introduktionsutbildning för övningskörning 이것이 의미하는 바: 교육 정책, 교통 정책 분야에 관련됩니다. 교육 동의안은 학교 기준, 교사 급여, 독립학교의 역할에 관한 깊은 의견 차이를 반영합니다 — 스웨덴에서 가장 논쟁적인 국내 토론 중 하나입니다. 역사적 맥락: C 발의안은 44.6% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023940

    +
    +
    +

    의안에 대한 답변 2025/26:136 Kvalificering till socialförsäkringen

    +

    게시일:

    +

    Kvalificering till socialförsäkringen 이것이 의미하는 바: 이민 정책 분야에 관련됩니다. 이민 동의안은 스웨덴에서 가장 논쟁적인 정책 분야 중 하나를 반영하며, 망명 규정, 통합 요건, 추방 절차에 대해 정당들이 나뉘어 있습니다. 역사적 맥락: C 발의안은 44.6% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023939

    +
    +
    +

    의안에 대한 답변 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    게시일:

    +

    동의 till riksdagen 2025/26:3938 av Mikael Larsson och Kerstin Lundgren (båda C) med anledning av prop. 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap 이것이 의미하는 바: 국방·안보 정책 분야에 관련됩니다. 국방 동의안은 스웨덴의 NATO 가입 이후 전략적 중요성이 높아졌으며, 정부에 장기적 안보 약속을 압박하고 있습니다.

    +

    HD023938

    +
    +
    +

    의안에 대한 답변 2025/26:131 Sysselsättning och boende på landsbygden

    +

    게시일:

    +

    Sysselsättning och boende på landsbygden 이것이 의미하는 바: 주택 정책 분야에 관련됩니다. 주택 동의안은 저렴한 주택에 대한 수요와 도시계획법, 임대료 규제, 건설 비용 압력의 제약 사이의 구조적 긴장을 반영합니다. 역사적 맥락: C 발의안은 44.6% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023937

    +
    +
    +

    의안에 대한 답변 2025/26:136 Kvalificering till socialförsäkringen

    +

    게시일:

    +

    Kvalificering till socialförsäkringen 이것이 의미하는 바: 이민 정책 분야에 관련됩니다. 이민 동의안은 스웨덴에서 가장 논쟁적인 정책 분야 중 하나를 반영하며, 망명 규정, 통합 요건, 추방 절차에 대해 정당들이 나뉘어 있습니다. 역사적 맥락: S 발의안은 43.4% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023936

    +
    +
    +

    의안에 대한 답변 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    게시일:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap 이것이 의미하는 바: 국방·안보 정책 분야에 관련됩니다. 국방 동의안은 스웨덴의 NATO 가입 이후 전략적 중요성이 높아졌으며, 정부에 장기적 안보 약속을 압박하고 있습니다. 역사적 맥락: S 발의안은 43.4% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023935

    +
    +
    +

    의안에 대한 답변 2025/26:134 ILO:s konvention om avskaffande av våld och trakasserier i arbetslivet och ILO:s konvention om en säker och hälsosam arbetsmiljö

    +

    게시일:

    +

    ILO:s konvention om avskaffande av våld och trakasserier i arbetslivet och ILO:s konvention om en säker och hälsosam arbetsmiljö 이것이 의미하는 바: 환경·기후 정책, 노동시장 정책 분야에 관련됩니다. 기후 동의안은 더 빠른 탈탄소화를 위한 의회 압력의 증가를 반영하며, 종종 특정 산업이나 정책 시행 속도를 대상으로 합니다. 역사적 맥락: S 발의안은 43.4% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023934

    +
    +
    +

    의안에 대한 답변 2025/26:138 En särskild straffbestämmelse för psykiskt våld

    +

    게시일:

    +

    En särskild straffbestämmelse för psykiskt våld 이것이 의미하는 바: 사법 정책 분야에 관련됩니다. 사법 동의안은 범죄, 양형, 치안 문제를 다룹니다 — 야당이 더 강력하거나 더 표적화된 조치를 자주 요구하는 공적 관심이 높은 분야입니다. 역사적 맥락: S 발의안은 43.4% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023933

    +
    +
    +

    의안에 대한 답변 2025/26:136 Kvalificering till socialförsäkringen

    +

    게시일:

    +

    Kvalificering till socialförsäkringen 이것이 의미하는 바: 이민 정책 분야에 관련됩니다. 이민 동의안은 스웨덴에서 가장 논쟁적인 정책 분야 중 하나를 반영하며, 망명 규정, 통합 요건, 추방 절차에 대해 정당들이 나뉘어 있습니다. 역사적 맥락: V 발의안은 34.8% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023932

    +
    +
    +

    의안에 대한 답변 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    게시일:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap 이것이 의미하는 바: 국방·안보 정책 분야에 관련됩니다. 국방 동의안은 스웨덴의 NATO 가입 이후 전략적 중요성이 높아졌으며, 정부에 장기적 안보 약속을 압박하고 있습니다. 역사적 맥락: V 발의안은 34.8% 통과율을 보입니다(야당 발의안의 96%가 거부됩니다). 이 동의안은 임박한 입법 변경이 아닌 정책 입장을 나타냅니다.

    +

    HD023931

    +
    + +

    위원회 활동

    +
    +
      +
    • Committee on Finance: 4
    • +
    • 헌법위원회: 3
    • +
    • Committee on Social Insurance: 3
    • +
    • Committee on Environment and Agriculture: 2
    • +
    • Committee on Transport: 2
    • +
    • Committee on Civil Affairs: 1
    • +
    • Committee on Industry and Trade: 1
    • +
    • Committee on Labour Market Affairs: 1
    • +
    • Committee on Defence: 1
    • +
    +
    + +

    정책적 시사점

    +

    Klimat- och näringslivsdepartementet이(가) 4건의 법안을 받아 정부 우선순위를 나타냅니다.

    +

    이러한 경쟁하는 역학들이 내일의 의회 업무를 위한 무대를 설정합니다.

    + +
    +

    심층 분석

    +

    무슨 일이 있었나

    +

    이민 정책 (10), 국방·안보 정책 (5), 재정 정책 (4), 주택 정책 (4), 환경·기후 정책 (3), 교통 정책 (3)

    +

    위원회 보고서: 18, 정부 제출 법안: 17, 동의안: 15

    +

    타임라인 & 맥락

    +

    18개 활동 위원회에 걸친 50개의 의회 안건이 현재의 입법 환경을 정의합니다. 활동의 속도는 이러한 절차를 추진하는 정치적 긴급성을 나타냅니다.

    +

    왜 중요한가

    +

    11개 정책 분야가 관여하는 이 상황은 스웨덴 사회의 여러 측면을 형성할 광범위한 입법 추진을 나타냅니다. 활동의 폭은 이 시기를 정부의 전략적 방향을 이해하기 위한 중요한 기간으로 만듭니다.

    +

    승자와 패자

    +

    여당 연합은 35건의 입법 항목으로 주도권을 유지하며, 야당의 15건 동의에 대해 우위를 보이고 있습니다.

    +

    정치적 영향

    +

    17개의 정부 제안은 채택될 경우 스웨덴 법률과 정책을 직접 변경하여 시민, 기업, 기관에 영향을 미칩니다. 18개의 위원회 보고서는 입법 검토의 정점으로, 본회의 투표를 안내하는 권고사항을 제공합니다. 15개의 야당 발의안은 정부 입장에 도전하며, 역사적으로 대부분 거부되지만 미래 선거 전장을 예고합니다. 0석의 근소한 과반 마진은 어떤 이탈도 정부 조치를 패배시킬 수 있음을 의미하며, 정치적 이해관계를 증폭시킵니다.

    +

    행동과 결과

    +

    채택될 경우 이 17개 제안은 정부 기관 전반에 걸쳐 이행을 시작하여 규제 변경, 예산 배분, 행정 적응을 요구합니다. 통과 실패는 연립의 약점을 드러내고 야당에 용기를 줄 것입니다. 15개의 야당 동의안은 부결될 가능성이 높지만, 야당이 다음 선거 주기에서 지지할 정책 대안을 확립합니다. 부결이 캠페인 탄약으로서의 전략적 가치를 감소시키지는 않습니다.

    +

    비판적 평가

    +

    이 안건들에 대한 본회의 토론 데이터가 없어 의회 심의의 깊이를 평가하는 능력이 제한됩니다. 이 정보 격차는 모니터링되어야 합니다 — 민주주의의 질은 절차적 통과뿐만 아니라 실질적인 토론에 달려 있습니다.

    +
    + +

    핵심 요점

    +
    +
      +
    • 17 정부 법안, 18 위원회 보고서, 15 동의 이 기간에 처리됨
    • +
    • 정책 맥락: 재정 정책; 이민 정책; 환경·기후 정책; 보건 정책
    • +
    • 분석 깊이: 50 / 50
    • +
    • 역사적 맥락: 현재 0석 차이는 단 한 명의 이탈로도 결과가 뒤집힐 수 있음을 의미합니다.
    • +
    +
    + +

    연립 동향

    +
    +

    연립 정부는 높은 위험 지표를 보이고 있습니다. 안정성 점수는 83점이지만, CIA 위험 평가는 의석 차이 없는 과반수로 인해 HIGH 위험을 나타냅니다.

    +
      +
    • 위험 점수: 37/100
    • +
    • 위험 수준: 높음
    • +
    +
      +
    • 연립 정부의 과반수 의석 차이는 0석뿐입니다 — 어떤 탈당도 정부 패배의 위험이 있습니다.
    • +
    • KD-M 연대 주제에서 비정상적으로 높은 당 초월 투표율(86.99%)이 감지되었습니다.
    • +
    • L-M 연대 주제에서 비정상적으로 높은 당 초월 투표율(86.34%)이 감지되었습니다.
    • +
    +
    + +

    주간 활동

    +
    +
      +
    • 문서: 50
    • +
    • 연설: 100
    • +
    • CIA 안정성 추세: 감소 중 ↓
    • +
    +

    추세 인사이트: 연립 정부 안정성은 매우 불안정합니다 — 궤적에서 중요한 변화가 감지되었습니다.

    +
    + + + + +
    +

    🇸🇪 의회 정보 개요

    + +
    +
    +

    연립 안정성

    +
    + 83/100 + HIGH +
    +
    +
    +
    +
    +
    위험 수준
    high
    +
    이탈 확률
    23%
    +
    과반수 마진
    0 의석
    +
    +
    +
    +

    📊 정당 성과

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ID정당의석성공률%결속력추세
    SArbetarepartiet-Socialdemokraterna10643.4%94%
    SDSverigedemokraterna7163.8%99%
    MModeraterna6886.5%100%
    CCenterpartiet2444.6%95%
    VVänsterpartiet2234.8%94%
    KDKristdemokraterna1981.9%99%
    MPMiljöpartiet de gröna1829.6%96%
    LLiberalerna1678.1%100%
    +
    +
    +
    +

    이슈별 투표 정렬

    +
    +
    + KD-M alignment +
    +
    + 연립 +
    +
    +
    + 84% +
    +
    + 야당 +
    +
    +
    + 16% +
    +
    +
    +
    + L-M alignment +
    +
    + 연립 +
    +
    +
    + 83% +
    +
    + 야당 +
    +
    +
    + 17% +
    +
    +
    +
    + KD-L alignment +
    +
    + 연립 +
    +
    +
    + 83% +
    +
    + 야당 +
    +
    +
    + 17% +
    +
    +
    +
    + C-L alignment +
    +
    + 연립 +
    +
    +
    + 78% +
    +
    + 야당 +
    +
    +
    + 22% +
    +
    +
    +
    + C-KD alignment +
    +
    + 연립 +
    +
    +
    + 77% +
    +
    + 야당 +
    +
    +
    + 23% +
    +
    +
    +
    +
    +
    +

    출처: CIA 플랫폼 — 스웨덴 의회 정보 데이터

    +
    +
    + +
    +
    +

    출처 및 데이터

    +

    데이터 소스: riksdag-regering-mcp, Committee Reports, Government Propositions, Parliamentary Motions, Riksdagen Documents, Riksdagen Document Content, Riksdagen Voting Records, Riksdagen Speeches

    +

    생성자: riksdag-regering-mcp 자동 뉴스 시스템

    +

    분석 도구: 편집 감독이 있는 AI 지원 저널리즘

    +
    + +
    + + ← 뉴스로 돌아가기 + +
    +
    +
    + + + + + + + \ No newline at end of file diff --git a/news/2026-03-14-weekly-review-nl.html b/news/2026-03-14-weekly-review-nl.html new file mode 100644 index 00000000000..1e60cfb232e --- /dev/null +++ b/news/2026-03-14-weekly-review-nl.html @@ -0,0 +1,297 @@ + + + + + + + De week in de Zweedse politiek: strengere staatsburgerschapseisen, nucleaire uitbreiding en immigratiehervorming + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← Terug naar nieuws + +
    + +
    +
    + + + +
    Laatste nieuws en analyses uit de Zweedse Riksdag. AI-gegenereerde politieke inlichtingenjournalistiek op basis van OSINT/INTOP-gegevens over parlement, regering en overheidsinstellingen met systematische transparantie.
    +

    De week in de Zweedse politiek: strengere staatsburgerschapseisen, nucleaire uitbreiding en immigratiehervorming

    + +
    + +
    +

    Belangrijkste punten

    +
      +
    • Strengere staatsburgerschapseisen: Propositie 2025/26:175 introduceert aanzienlijk strengere eisen voor het Zweedse staatsburgerschap — de meest ingrijpende hervorming van de nationaliteitswet in decennia
    • +
    • Verbod op neef-nichthuwelijken: Propositie 2025/26:154 stelt voor om huwelijken tussen neven en nichten en andere naaste verwanten te verbieden, als uitbreiding van de bestaande Zweedse huwelijksbelemmeringen
    • +
    • Nucleaire uitbreiding: Drie nucleaire proposities (160, 167, 168) signaleren de volledige inzet van de regering op nieuwe kerncentrales aan de kust en gemoderniseerde veiligheidsvoorschriften
    • +
    • Hervorming arbeidsimmigratie: Commissierapport SfU12 beveelt goedkeuring aan van ingrijpende nieuwe regels voor arbeidsimmigratie, waaronder salarisdrempels en nieuwe strafbaarstellingen voor uitbuiting van werknemers
    • +
    • 15 commissierapporten: Commissies leverden besluiten op het gebied van staatsrecht (KU), financiën (FiU), sociale verzekeringen (SfU), milieu (MJU), vervoer (TU) en burgerlijke zaken (CU)
    • +
    +
    + +
    +

    Hoofdverhaal: staatsburgerschap en identiteit centraal in het politieke debat

    +

    De regering-Kristersson plaatste staatsburgerschap en familierecht deze week nadrukkelijk in het centrum van haar wetgevingsagenda voor de verkiezingen, met twee proposities die samen de meest ingrijpende wijzigingen in het Zweedse personen- en familierecht in een generatie vertegenwoordigen.

    +

    Propositie 2025/26:175, op 12 maart gepresenteerd door migratieminister Johan Forssell, introduceert drastisch strengere eisen voor het verkrijgen van het Zweedse staatsburgerschap. De hervorming, die de harde lijn van de regering op het gebied van integratie onderstreept, is bedoeld om ervoor te zorgen dat staatsburgerschap een werkelijke maatschappelijke binding weerspiegelt — een beleidseis die lang door SD is bepleit en nu door de Tidö-coalitie wordt doorgevoerd.

    +

    Dezelfde dag presenteerde justitieminister Gunnar Strömmer Propositie 2025/26:154, die huwelijken tussen neven en nichten en andere naaste verwanten zou verbieden, bovenop het bestaande verbod op huwelijken tussen broers en zussen. De regering presenteerde de maatregel als bescherming van individuele rechten en bestrijding van eergerelateerde cultuur.

    +

    Deze twee proposities onderstrepen de strategie van de regering om in hoog tempo sociaal-conservatieve hervormingen door te voeren nu de verkiezingen van september 2026 naderen, waarmee duidelijke ideologische frontlinies worden getrokken tegenover de Sociaaldemocraten en Miljöpartiet.

    +
    + +
    +

    Wetgevingsresultaten: 15 commissierapporten over zes beleidsterreinen

    +

    Commissies leverden deze week 15 betänkanden (rapporten) af, over een uitzonderlijk breed spectrum van beleidsterreinen. Verschillende hadden aanzienlijk wetgevend gewicht:

    +

    Hervorming arbeidsimmigratie (SfU12)

    +

    Het rapport van de Commissie Sociale Verzekeringen over nieuwe regels voor arbeidsimmigratie beveelt aan dat de Riksdag de meest omvattende hervorming van werkvergunningen in jaren goedkeurt. Kernmaatregelen omvatten verplichte salarisdrempels, een vereiste voor uitgebreide ziektekostenverzekering, nieuwe strafbaarstellingen voor uitbuiting van buitenlandse werknemers en handel in werkvergunningen, en verlengde geldigheid van de EU Blue Card. De hervormingen treden in werking op 1 juni 2026.

    +

    Aanpak invasieve soorten (MJU13)

    +

    De Commissie Milieu en Landbouw beveelt aan de regelgeving tegen invasieve uitheemse soorten te verscherpen. Onder de nieuwe regels wordt het opzettelijk of nalatig binnenbrengen van invasieve soorten in Zweden vanuit een EU-land strafbaar met maximaal twee jaar gevangenisstraf. Tullverket krijgt inspectiebevoegdheden aan de binnengrenzen. De wet treedt in werking op 1 mei 2026.

    +

    Hondencontrole (MJU15)

    +

    Länsstyrelserna krijgen uitgebreide bevoegdheden voor hondentoezicht, waaronder het recht om woningen te betreden voor inspecties en ruimere mogelijkheden voor onmiddellijke inbeslagname van gevaarlijke honden. De politie kan in bepaalde gevallen ook de euthanasie van in beslag genomen dieren gelasten. Ingangsdatum 1 mei 2026.

    +

    Staatsrechtelijke en bestuurlijke zaken (KU35–37)

    +

    De Commissie Staatsrecht leverde drie rapporten af: over digitale gemeenteraadsvergaderingen (KU35), een evaluatie van privacy en nieuwe technologie 2020–2024 (KU36) en de concentratie van activiteiten van länsstyrelserna (KU37). Het rapport over digitale vergaderingen stelt flexibelere deelname op afstand aan lokaal bestuur voor.

    +

    Financiële markten en gemeentelijk bestuur (FiU22, FiU25, FiU26, FiU34)

    +

    De Commissie Financiën behandelde vier rapporten. FiU22 gaat over de activiteiten van het IMF in 2025 en de stabiliteit van de financiële markten. FiU26 verwierp 69 moties over gemeentelijke kwesties. FiU34 betrof overheidsopdrachten en FiU25 behandelde staatsadministratie.

    +

    Gezinsbeleid en sociale verzekeringen (SfU10, SfU17)

    +

    SfU17 verwierp 104 moties over economisch gezinsbeleid — waaronder ouderschapsverzekering, kinderbijslag en huurtoeslag. SfU10 behandelde de bescherming van sociale verzekeringen voor internationaal mobiele werknemers.

    +
    + +
    +

    Regeringsoverzicht: nucleaire ambities en versterkte defensie

    +

    Naast staatsburgerschap en familierecht voerde de regering deze week een omvangrijke energieagenda door en versterkte zij de Zweedse defensiepositie:

    +
      +
    • Nucleaire drieslag (Prop. 160, 167, 168, 171): Vier nucleaire proposities in één week. Prop. 160 opent nieuwe kustlocaties voor kerncentrales, terwijl Prop. 167, 168 en 171 het toezicht op nucleaire materialen, de veiligheids- en stralingseisen en het beoordelingsproces moderniseren.
    • +
    • EU-activiteitenrapport (Skr. 115): Het jaarlijkse EU-overzicht van de regering werd op 12 maart ingediend, met een bespreking van Zwedens EU-betrokkenheid in 2025.
    • +
    • Resultaten Oekraïnehulp (Skr. 97): Een gedetailleerd rapport over de resultaten van Zweedse internationale hulp aan Oekraïne werd gepresenteerd.
    • +
    • Antecedentenonderzoek op scholen (Prop. 174): Uitgebreide registercontroles voor schoolmedewerkers, als onderdeel van de veiligheidsagenda van de regering voor het onderwijs.
    • +
    • Hervorming bouwprocessen (Prop. 172): Stroomlijning van bouwprocessen voor meer efficiëntie en veiligheid.
    • +
    • Nauwkeurigheid huurtoeslag (Prop. 170): Gerichtere en correctere berekeningen van woontoeslagen.
    • +
    • Verdeling pensioenreserves (Prop. 169): Regels voor de verdeling van overschotten uit het inkomenspensioen.
    • +
    • Defensie-industriële samenwerking met Oekraïne: Zweden kondigde versterkte defensie-industriële samenwerking met Oekraïne aan, en minister van Civiele Verdediging Carl-Oskar Bohlin nam deel aan een oefening voor totale verdediging in Stockholm.
    • +
    +

    Er werden ook drie SOU's (officiële onderzoeken) gepubliceerd: over landbouwveen en klimaat (SOU 2026:18), defensie-exportinitiatieven (SOU 2026:16) en de bestrijding van onrechtmatige medicijnvoorschriften (SOU 2026:19).

    +
    + +
    +

    Kamerdebatten: begrotingstransparantie, Iran en de circulaire economie

    +

    De kamerdebatten van deze week weerspiegelden zowel buitenlandse spanningen als binnenlandse financiële verantwoording:

    +

    Iran en democratische steun

    +

    Minister van Buitenlandse Zaken Maria Malmer Stenergard (M) beantwoordde een interpellatie van Daniel Riazat (onafhankelijk, voorheen V) over de steun van de regering aan de opstand van het Iraanse volk tegen het regime. SD's Nima Gholam Ali Pour nam ook deel, wat een ongebruikelijke partijoverschrijdende dynamiek creëerde.

    +

    Begrotingseffecten en fiscale transparantie

    +

    Minister van Financiën Elisabeth Svantesson (M) werd aanhoudend ondervraagd door S-Kamerlid Peder Björk over de rapportage van begrotingseffecten — onderdeel van de oppositiestrategie om de regering verantwoording te laten afleggen over economische transparantie in aanloop naar de verkiezingen van 2026.

    +

    Btw op parkeren en gemeentelijke economie

    +

    Svantesson debatteerde ook met S-Kamerlid Marie Olsson over btw voor parkeerplaatsen — een kwestie met aanzienlijke gevolgen voor gemeentelijke financiën en de commerciële vastgoedmarkt.

    +

    Circulaire en gifvrije economie

    +

    Het voortgezette debat over MJU12 kende bijdragen van MP-Kamerlid Katarina Luhr, C-Kamerlid Stina Larsson, V-Kamerlid Andrea Andersson Tay en M-Kamerlid Lars Johnsson — wat de groeiende politieke relevantie van duurzaamheid en chemische veiligheid weerspiegelt.

    +
    + +
    +

    Oppositiedynamiek: kwalificatie voor sociale verzekeringen wordt het strijdtoneel

    +

    De meest intense oppositieactiviteit richtte zich op Propositie 2025/26:136 over de kwalificatie voor sociale verzekeringen. Vier oppositiepartijen dienden afzonderlijke moties in:

    +
      +
    • Sociaaldemocraten (S): Motie 3936 van Ida Karkiainen wil de propositie gedeeltelijk verwerpen en accepteert de eis van rechtmatig verblijf, maar verzet zich tegen bredere kwalificatiebeperkingen
    • +
    • Vänsterpartiet (V): Motie 3932 van Tony Haddou eist volledige verwerping
    • +
    • Miljöpartiet (MP): Motie 3942 van Malte Tängmark Roos roept eveneens op tot volledige verwerping
    • +
    • Centerpartiet (C): Motie 3939 van Anders W Jonsson beoogt wijzigingen van de kwalificatie-uitzonderingen
    • +
    +

    Dit vierpartijenfront van de oppositie inzake welzijnskwalificatie vertegenwoordigt een significante politieke afstemming. Het interpellatieoffensief van de Sociaaldemocraten werd onverminderd voortgezet, met een focus op ouderenzorg (drie afzonderlijke interpellaties van Lundh Sammeli, Dahlqvist en Sundin gericht aan minister Tenje), economische prioriteiten en sociale dumping tussen gemeenten. Deze ouderenzorg-drieslag signaleert dat S van zorgkwaliteit een verkiezingsthema wil maken in 2026.

    +

    MP-Kamerlid Ulrika Westerlund betwistte de juridische grondslagen van de strafrechtelijke agenda van de regering in interpellatie 382, terwijl MP-Kamerlid Annika Hirvonen het beleid inzake uitzetting van tieners aan de orde stelde in interpellatie 381.

    +
    + +
    +

    Wat het meest ertoe deed: de wetgevingssprint voor de verkiezingen

    +

    Het enorme volume en de reikwijdte van de wetgevingsproductie van deze week onthullen een regering in volle verkiezingssprint. Met de verkiezingen van september 2026 op ongeveer zes maanden afstand duwt de coalitie-Kristersson haar resterende wetgevingsprioriteiten in ongekend tempo door het parlement — hervorming van staatsburgerschap, immigratiehervorming, nucleaire uitbreiding en sociaal-conservatief familierecht in één week.

    +

    De gecoördineerde reactie van de oppositie op de kwalificatie voor sociale verzekeringen suggereert dat de campagnefrontlinies voor 2026 zich verharden: de rechtercoalitie bouwt voort op migratie, staatsburgerschap en veiligheid; de oppositie verdedigt de universaliteit van de welvaartsstaat en burgerlijke vrijheden. Het ouderenzorgoffensief voegt een binnenlandse dienstverleningsdimensie toe die electoraal krachtig kan blijken.

    +

    Het meest verstrekkend markeert de nucleaire drieslag een onomkeerbare beleidsverbintenis. De opening van nieuwe kustlocaties voor kerncentrales, in combinatie met gemoderniseerde veiligheids- en beoordelingsprocessen, positioneert Zweden als een van de meest pro-nucleaire democratieën van Europa — een standpunt met gevolgen voor energiemarkten, klimaatbeleid en geopolitieke oriëntatie voor de komende decennia.

    +
    + +
    +

    Vooruitblik

    +

    Volgende week (16–20 maart) zal de Riksdag meerdere van de commissierapporten van deze week behandelen in plenaire stemmingen. Let op het debat over de hervorming van arbeidsimmigratie (SfU12), dat waarschijnlijk tot partijoverschrijdende botsingen zal leiden. De propositie over kwalificatie voor sociale verzekeringen (Prop. 136) vervolgt haar commissietraject, waarbij tegengestelde moties van vier partijen een omstreden proces garanderen.

    +

    De staatsburgerschapspropositie van de regering (Prop. 175) wordt doorverwezen naar de Commissie Sociale Verzekeringen, waar de gezamenlijke oppositie naar verwachting krachtig verzet zal bieden. Ondertussen gaan de nucleaire proposities naar de betreffende commissies, waar technische hoorzittingen waarschijnlijk worden gepland.

    +

    Aan regeringszijde signaleert het bezoek van minister van Buitenlandse Zaken Malmer Stenergard aan Texas en Virginia de voortgezette betrokkenheid bij de Verenigde Staten, terwijl verdere EU-handelspolitieke initiatieven worden verwacht na de ontmoetingen van commissaris Šefçoviç met minister Dousa.

    +
    + +
    +

    Gegevensbronnen

    +

    Deze analyse is gebaseerd op gegevens van de open data-API van het Zweedse parlement (data.riksdagen.se) en het Regeringskansliet via g0v.se, over de periode 7–14 maart 2026. Alle parlementaire documenten, stemmingen en toespraken zijn openbaar beschikbaar via het documentarchief van de Riksdag. Riksmöte: 2025/26.

    +
    + +
    + +
    +

    Gepubliceerd door Riksdagsmonitor — AI-gegenereerde politieke inlichtingen op basis van OSINT/INTOP-gegevens van de open data-API's van het Zweedse parlement en de regering.

    +

    © 2026 Hack23 AB. Licensed under CC BY-SA 4.0.

    +
    + + + + \ No newline at end of file diff --git a/news/2026-03-14-weekly-review-no.html b/news/2026-03-14-weekly-review-no.html new file mode 100644 index 00000000000..9603693b868 --- /dev/null +++ b/news/2026-03-14-weekly-review-no.html @@ -0,0 +1,297 @@ + + + + + + + Uken i svensk politikk: Skjerpede statsborgerskapskrav, atomkraftekspansjon og innvandringsreform + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← Tilbake til nyheter + +
    + +
    +
    + + + +
    Siste nyheter og analyser fra Sveriges Riksdag. AI-generert politisk etterretningsjournalistikk basert på OSINT/INTOP-data om parlament, regjering og myndigheter med systematisk åpenhet.
    +

    Uken i svensk politikk: Skjerpede statsborgerskapskrav, atomkraftekspansjon og innvandringsreform

    + +
    + +
    +

    Hovedpunkter

    +
      +
    • Strengere statsborgerskap: Proposisjon 2025/26:175 innfører betydelig strengere krav til svensk statsborgerskap — den mest omfattende reformen av statsborgerskapsloven på flere tiår
    • +
    • Forbud mot søskenbarnekteskap: Proposisjon 2025/26:154 foreslår å forby ekteskap mellom søskenbarn og andre nære slektninger, og utvider Sveriges eksisterende ekteskapshindringer
    • +
    • Atomkraftekspansjon: Tre atomkraftrelaterte proposisjoner (160, 167, 168) signaliserer regjeringens satsing på nye kystbaserte atomkraftverk og moderniserte sikkerhetsregler
    • +
    • Reform av arbeidsinnvandring: Komitérapport SfU12 anbefaler godkjenning av nye regler for arbeidsinnvandring med lønnsterskler og nye straffebestemmelser mot utnyttelse av arbeidere
    • +
    • 15 komitérapporter: Komiteene leverte avgjørelser innenfor grunnlovsspørsmål (KU), finans (FiU), sosialforsikring (SfU), miljø (MJU), transport (TU) og sivilrett (CU)
    • +
    +
    + +
    +

    Hovedsak: Statsborgerskap og identitet i sentrum av den politiske debatten

    +

    Kristersson-regjeringen plasserte statsborgerskap og familierett i sentrum av sin lovgivningsagenda denne uken, og la frem to proposisjoner som til sammen representerer de mest betydningsfulle endringene i svensk personrett på en generasjon.

    +

    Proposisjon 2025/26:175, lagt frem 12. mars av migrasjonsminister Johan Forssell, innfører markant strengere krav for å oppnå svensk statsborgerskap. Reformen signaliserer regjeringens harde linje på integreringsområdet og er utformet for å sikre at statsborgerskap gjenspeiler reell samfunnstilknytning — et politisk krav lenge fremmet av SD og nå gjennomført av Tidö-koalisjonen.

    +

    Samme dag la justisminister Gunnar Strömmer frem Proposisjon 2025/26:154, som vil forby ekteskap mellom søskenbarn og andre nære slektninger utover det eksisterende søskenforbudet. Regjeringen presenterte tiltaket som beskyttelse av individuelle rettigheter og bekæmpelse av æresrelatert kultur, en innramming som bestrides av opposisjonspartier som argumenterer for mer nyanserte tilnærminger.

    +

    Disse to proposisjonene understreker regjeringens strategi med å fremme sosialt konservative reformer i høyt tempo når valget i september 2026 nærmer seg, og skaper tydelige ideologiske kamplinjer mot Socialdemokraterna og Miljöpartiet.

    +
    + +
    +

    Lovgivningsresultater: 15 komitérapporter på tvers av seks politikkområder

    +

    Komiteene leverte 15 betänkanden (rapporter) denne uken, som dekket et ekstraordinært bredt spekter av politikkområder. Flere hadde betydelig lovgivningsmessig tyngde:

    +

    Reform av arbeidsinnvandring (SfU12)

    +

    Sosialforsikringskomiteens rapport om nye regler for arbeidsinnvandring anbefaler at Riksdagen godkjenner regjeringens mest omfattende reform av arbeidstillatelser på år. Nøkkelelementer inkluderer obligatoriske lønnsterskler, krav om helseforsikring, nye straffebestemmelser for utnyttelse av utenlandske arbeidere og utvidet EU Blue Card-gyldighet. Reformene trer i kraft 1. juni 2026.

    +

    Innstramming mot invasive arter (MJU13)

    +

    Miljø- og jordbrukskomiteen anbefaler å styrke reguleringen av invasive fremmede arter. Under de nye reglene kan forsettlig eller uaktsom innførsel av invasive arter til Sverige fra et EU-land straffes med opptil to års fengsel. Tullverket får inspeksjonsmyndighet ved indre grenser. Loven trer i kraft 1. mai 2026.

    +

    Hundekontroll (MJU15)

    +

    Länsstyrelsene får utvidede verktøy for hundetilsyn, inkludert myndighet til å få tilgang til boliger for inspeksjoner og bredere fullmakter til umiddelbar beslagleggelse av farlige hunder. Politiet kan også beordre avlivning av beslaglagte dyr i visse tilfeller. Trer i kraft 1. mai 2026.

    +

    Grunnlovs- og forvaltningsspørsmål (KU35–37)

    +

    Grunnlovskomiteen leverte tre rapporter: om digitale kommunale møter (KU35), gjennomgang av personvern og ny teknologi 2020–2024 (KU36) og konsentrasjon av länsstyrelsenes virksomhet (KU37). Rapporten om digitale møter foreslår mer fleksibel fjerndeltagelse i kommunal styring — en arv fra pandemiens praksiser.

    +

    Finansmarkeder og kommunal styring (FiU22, FiU25, FiU26, FiU34)

    +

    Finanskomiteen behandlet fire rapporter. FiU22 omhandler IMFs aktiviteter i 2025 og finansiell markedsstabilitet, og bemerker IMFs viktige rolle i støtten til Ukraina. FiU26 avviste 69 mosjoner om kommunale spørsmål. FiU34 dekket offentlige anskaffelser, og FiU25 behandlet statsforvaltning.

    +

    Familiepolitikk og sosialforsikring (SfU10, SfU17)

    +

    SfU17 avviste 104 mosjoner om økonomisk familiepolitikk — inkludert foreldreforsikring, barnetrygd og bostøtte — med henvisning til pågående regjeringsarbeid. SfU10 behandlet Riksrevisionens gjennomgang av sosialforsikringsbeskyttelse for internasjonalt mobile arbeidstakere.

    +
    + +
    +

    Regjeringsovervåkning: Atomkraftambisjoner og styrket forsvar

    +

    Utover statsborgerskap og familierett fremmet regjeringen en større energiagenda og styrket Sveriges forsvarsposisjon denne uken:

    +
      +
    • Atomkraft-trippelspill (Prop. 160, 167, 168, 171): Fire atomkraftrelaterte proposisjoner på en enkelt uke. Prop. 160 åpner nye kystlokasjoner for atomkraftverk, mens Prop. 167, 168 og 171 moderniserer kontroll av nukleære materialer, sikkerhets- og strålingskrav samt godkjenningsprosessen.
    • +
    • EU-aktivitetsrapport (Skr. 115): Regjeringens årlige EU-gjennomgang ble lagt frem 12. mars med oversikt over Sveriges EU-engasjement gjennom 2025.
    • +
    • Resultater av Ukraina-bistand (Skr. 97): En detaljert rapport om resultatene av svensk internasjonal bistand til Ukraina ble presentert.
    • +
    • Bakgrunnssjekk i skoler (Prop. 174): Utvidede registersjekker for skoleansatte, som gjenspeiler regjeringens sikkerhetsagenda for utdanning.
    • +
    • Reform av byggeprosesser (Prop. 172): Effektivisering av byggeprosesser for økt effektivitet og sikkerhet.
    • +
    • Nøyaktighet i bostøtte (Prop. 170): Mer målrettede og korrekte bostøtteberegninger.
    • +
    • Fordeling av pensjonsoverskudd (Prop. 169): Regler for fordeling av overskudd fra inntektspensjonssystemet.
    • +
    • Forsvarsindustrielt samarbeid med Ukraina: Sverige kunngjorde styrket forsvarsindustrielt samarbeid med Ukraina, og sivilforsvarsminister Carl-Oskar Bohlin deltok i en totalforsvarsøvelse i Stockholm.
    • +
    +

    Tre SOUer (offisielle utredninger) ble også publisert: om jordbrukstorv og klima (SOU 2026:18), forsvarseksportinitiativer (SOU 2026:16) og motvirkning av ureglementert medisinforskrivning (SOU 2026:19).

    +
    + +
    +

    Kammerdebatter: Budsjettransparens, Iran og den sirkælure økonomien

    +

    Ukens kammerdebatter gjenspeilte både utenrikspolitiske spenninger og innenrikspolitisk økonomisk ansvarlighet:

    +

    Iran og demokratisk støtte

    +

    Utenriksminister Maria Malmer Stenergard (M) besvarte en interpellasjon fra Daniel Riazat (uavhengig, tidligere V) om regjeringens støtte til det iranske folkets oppreisning mot regimet. SDs Nima Gholam Ali Pour deltok også, noe som skapte en uvanlig tverrpolitisk dynamikk.

    +

    Budsjetteffekter og fiskal transparens

    +

    Finansminister Elisabeth Svantesson (M) ble utsatt for vedvarende spørsmål fra Ss Peder Björk om rapportering av budsjetteffekter — en del av opposisjonens strategi for å holde regjeringen ansvarlig for økonomisk transparens før valget i 2026.

    +

    Parkerings-mva og kommunal økonomi

    +

    Svantesson debatterte også Ss Marie Olsson om mva for parkeringsplasser — et tema med betydelige konsekvenser for kommunale finanser og kommersiell eiendomsøkonomi.

    +

    Sirkælær og giftfri økonomi

    +

    Den fortsatte debatten om MJU12 så engasjement fra MPs Katarina Luhr, Cs Stina Larsson, Vs Andrea Andersson Tay og Ms Lars Johnsson — som gjenspeiler den voksende politiske relevansen av bærekraft og kjemikaliesikkerhet.

    +
    + +
    +

    Opposisjonsdynamikk: Sosialforsikringskvalifisering blir slagmarken

    +

    Den mest intense opposisjonsaktiviteten dreide seg om Proposisjon 2025/26:136 om sosialforsikringskvalifisering. Fire opposisjonspartier leverte separate mosjoner:

    +
      +
    • Socialdemokraterna (S): Mosjon 3936 av Ida Karkiainen vil delvis avvise proposisjonen og akseptere kravet om lovlig opphold, men avvise bredere kvalifikasjonsrestriksjoner
    • +
    • Vänsterpartiet (V): Mosjon 3932 av Tony Haddou krever fullstendig avvisning
    • +
    • Miljöpartiet (MP): Mosjon 3942 av Malte Tängmark Roos krever også fullstendig avvisning
    • +
    • Centerpartiet (C): Mosjon 3939 av Anders W Jonsson søker endringer i kvalifikasjonsunntakene
    • +
    +

    Denne fireparti-opposisjonsfronten på velferdskvalifisering representerer en betydelig politisk samordning. Socialdemokraternas interpellasjonsoffensiv fortsatte med fokus på eldreomsorg (tre separate interpellasjoner av Lundh Sammeli, Dahlqvist og Sundin rettet mot minister Tenje), økonomiske prioriteringer og sosial dumping mellom kommuner. Denne eldreomsorgs-triaden signaliserer at S har til hensikt å gjøre kvalitet i omsorgen til en valgkampsak i 2026.

    +

    MPs Ulrika Westerlund utfordret de juridiske grunnlagene for regjeringens strafferettslige agenda i interpellasjon 382, mens MPs Annika Hirvonen rettet fokus mot politikk for deportasjon av tenlåringer i interpellasjon 381.

    +
    + +
    +

    Det som betydde mest: Den førvalgsmessige lovgivningssprinten

    +

    Det enorme omfanget av ukens lovgivningsproduksjon avslører en regjering i full førvalgssprint. Med valget i september 2026 omtrent seks måneder unna, presser Kristersson-koalisjonen sine gjenstående lovgivningsprioriteringer gjennom i et hitest uset tempo — statsborgerskapsreform, innvandringsreform, atomkraftekspansjon og sosialt konservativ familierett på en enkelt uke.

    +

    Opposisjonens koordinerte respons på sosialforsikringskvalifisering antyder at valgkampfrontlinjene for 2026 herder: høyrekoalisjonen bygger på migrasjon, statsborgerskap og sikkerhet; opposisjonen forsvarer velferdsuniversalitet og borgerlige friheter. Eldreomsorgsoffensiven legger til en hjemlig tjenesteytelses-dimensjon som kan vise seg valgmessig potent.

    +

    Mest konsekvensfylt markerer atomkraft-trippelspillet en irreversibel politisk forpliktelse. Åpningen av nye kystbaserte atomkraftverk, kombinert med moderniserte sikkerhets- og godkjenningsprosesser, posisjonerer Sverige som et av Europas mest pro-atomkraft-demokratier — en holdning med konsekvenser for energimarkeder, klimapolitikk og geopolitisk tilpasning i tiår fremover.

    +
    + +
    +

    Veien videre

    +

    Neste uke (16.–20. mars) vil Riksdagen behandle flere av ukens komitérapporter i plenumsavstemninger. Følg med på debatten om reform av arbeidsinnvandring (SfU12), som sannsynligvis vil generere tverrpolitiske sammenstøt. Sosialforsikringskvalifiseringsproposisjonen (Prop. 136) fortsetter sin komitéreise med motstridende mosjoner fra fire partier som sikrer en omstridt prosess.

    +

    Regjeringens statsborgerskapsproposisjon (Prop. 175) vil bli henvist til Sosialforsikringskomiteen, der den samlede opposisjonen forventes å yte kraftig motstand. Atomkraftproposisjonene går videre til de relevante komiteene der tekniske høringer sannsynligvis vil bli planlagt.

    +

    På regjeringssiden signaliserer utenriksminister Malmer Stenergards besøk til Texas og Virginia fortsatt engasjement med USA, mens ytterligere EU-handelspolitiske initiativer forventes etter kommissær Šefçoviç' møter med minister Dousa.

    +
    + +
    +

    Datakilder

    +

    Denne analysen er basert på data fra det svenske parlamentets åpne data-API (data.riksdagen.se) og Regeringskansliet via g0v.se, som dekker perioden 7.–14. mars 2026. Alle parlamentariske dokumenter, avstemninger og taler er offentlig tilgjengelige via Riksdagens dokumentarkiv. Riksmöte: 2025/26.

    +
    + +
    + +
    +

    Publisert av Riksdagsmonitor — AI-generert politisk etterretning basert på OSINT/INTOP-data fra det svenske parlamentets og regjeringens åpne data-APIer.

    +

    © 2026 Hack23 AB. Licensed under CC BY-SA 4.0.

    +
    + + + + diff --git a/news/2026-03-14-weekly-review-sv.html b/news/2026-03-14-weekly-review-sv.html index 3cbd3aee047..a9e43ee893b 100644 --- a/news/2026-03-14-weekly-review-sv.html +++ b/news/2026-03-14-weekly-review-sv.html @@ -3,7 +3,7 @@ - + Veckan i svensk politik: Skärpt medborgarskap, kärnkraftsexpansion och ny arbetskraftsinvandring @@ -43,6 +43,18 @@ + + + + + + + + + + + + @@ -141,6 +153,18 @@
    diff --git a/news/2026-03-14-weekly-review-zh.html b/news/2026-03-14-weekly-review-zh.html new file mode 100644 index 00000000000..8b438ce1757 --- /dev/null +++ b/news/2026-03-14-weekly-review-zh.html @@ -0,0 +1,933 @@ + + + + + + + 每周回顾:议会回顾 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + ← 返回新闻 + +
    + +
    +
    + + + +
    来自瑞典议会的最新新闻和分析。基于OSINT/INTOP数据的AI生成政治情报新闻,以系统性透明度报道议会、政府和机构。
    +

    每周回顾:议会回顾

    + +
    + + + +
    +

    + 过去一周50项重要发展的分析 +

    + +

    议会正在审议本期17项政府提案,包括"Verksamheten i Europeiska unionen under 2025"。

    + +

    主题分析

    + +

    政府提案 (17)

    +
    +

    Verksamheten i Europeiska unionen under 2025

    +

    发布日期:

    +

    政府通知 2025/26:115 Verksamheten i Europeiska unionen under 2025 Skr. 2025/26:115 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Jessica Rosencrantz (Statsrådsberedningen) Skrivelsens huvudsakliga innehåll I skrivelsen redogör regeringen för verksamheten 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03115

    +
    +
    +

    Förbud mot kusinäktenskap och äktenskap mellan andra nära släktingar

    +

    发布日期:

    +

    政府法案 2025/26:154 Förbud mot kusinäktenskap och äktenskap Prop. mellan andra nära släktingar 2025/26:154 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 12 mars 2026 Ulf Kristersson Gunnar Strömmer (Justitiedepartementet) Propositionens huvudsakliga innehåll I propositionen 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03154

    +
    +
    +

    Skärpta krav för svenskt medborgarskap

    +

    发布日期:

    +

    Skärpta krav för svenskt medborgarskap Regeringens proposition 2025/26:175 Skärpta krav för svenskt medborgarskap Prop. 2025/26:175 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 12 mars 2026 Ulf Kristersson Johan Forssell (Justitiedepartementet) Propositionens huvudsakliga innehåll Det 这意味着什么: 涉及移民政策领域。 移民提案必须在欧盟监管义务和国家政治需求之间取得平衡,使跨党派支持成为持久立法的关键。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03175

    +
    +
    +

    Kärnämneskontroll och bedömningar av överensstämmelse med säkerhets- och strålskyddskrav i verksamhet med strålning

    +

    发布日期:

    +

    政府法案 2025/26:167 Kärnämneskontroll och bedömningar av Prop. överensstämmelse med säkerhets- och 2025/26:167 strålskyddskrav i verksamhet med strålning Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03167

    +
    +
    +

    Utökade registerkontroller i skolväsendet

    +

    发布日期:

    +

    政府法案 2025/26:174 Utökade registerkontroller i skolväsendet Prop. 2025/26:174 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Propositionens huvudsakliga innehåll Förskolan och skolan ska vara trygga 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03174

    +
    +
    +

    Fördjupad resultatredovisning av internationellt bistånd – Ukraina

    +

    发布日期:

    +

    政府通知 2025/26:97 Fördjupad resultatredovisning av internationellt Skr. bistånd Ukraina 2025/26:97 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Benjamin Dousa (Utrikesdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen ges en fördjupad 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD0397

    +
    +
    +

    Effektiv och säker byggprocess

    +

    发布日期:

    +

    Effektiv och säker byggprocess Regeringens proposition 2025/26:172 Effektiv och säker byggprocess Prop. 2025/26:172 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Andreas Carlson (Landsbygds- och infrastrukturdepartementet) Propositionens huvudsakliga innehåll 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03172

    +
    +
    +

    法律 om avgift för områdessamverkan

    +

    发布日期:

    +

    政府法案 2025/26:157 Lag om avgift för områdessamverkan Prop. 2025/26:157 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Andreas Carlson (Landsbygds- och infrastrukturdepartementet) Propositionens huvudsakliga innehåll Regeringen föreslår en ny 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03157

    +
    +
    +

    瑞典国家审计局报告 om Statens fastighetsverks fastighetsförvaltning

    +

    发布日期:

    +

    政府通知 2025/26:152 Riksrevisionens rapport om Statens Skr. fastighetsverks fastighetsförvaltning 2025/26:152 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Niklas Wykman (Finansdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen behandlar 这意味着什么: 涉及住房政策领域。 住房提案必须调和市政当局、业主、租户和开发商之间的竞争利益——这种联盟很少能快速达成。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03152

    +
    +
    +

    Ny kärnkraft i Sverige – fler möjliga platser vid kusten

    +

    发布日期:

    +

    政府法案 2025/26:160 Ny kärnkraft i Sverige fler möjliga platser vid Prop. kusten 2025/26:160 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) Propositionens huvudsakliga innehåll Propositionen innehåller 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03160

    +
    +
    +

    En mer ändamålsenlig prövning av kärntekniska anläggningar

    +

    发布日期:

    +

    En mer ändamålsenlig prövning av kärntekniska anläggningar Regeringens proposition 2025/26:171 En mer ändamålsenlig prövning av kärntekniska Prop. anläggningar 2025/26:171 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03171

    +
    +
    +

    Ändamålsenliga säkerhets- och strålskyddskrav för utvinning och bearbetning av kärnämnen

    +

    发布日期:

    +

    政府法案 2025/26:168 Ändamålsenliga säkerhets- och strålskyddskrav Prop. för utvinning och bearbetning av kärnämnen 2025/26:168 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Johan Britz (Klimat- och näringslivsdepartementet) Propositionens huvudsakliga 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03168

    +
    +
    +

    Ett mer träffsäkert och korrekt bostadsbidrag

    +

    发布日期:

    +

    Ett mer träffsäkert och korrekt bostadsbidrag Regeringens proposition 2025/26:170 Ett mer träffsäkert och korrekt bostadsbidrag Prop. 2025/26:170 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Anna Tenje (Socialdepartementet) Propositionens huvudsakliga innehåll 这意味着什么: 涉及住房政策领域。 住房提案必须调和市政当局、业主、租户和开发商之间的竞争利益——这种联盟很少能快速达成。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03170

    +
    +
    +

    瑞典国家审计局报告 om förvar i migrationsprocessen

    +

    发布日期:

    +

    政府通知 2025/26:137 Riksrevisionens rapport om förvar i Skr. migrationsprocessen 2025/26:137 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Gunnar Strömmer (Justitiedepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen redovisar regeringen 这意味着什么: 涉及移民政策领域。 移民提案必须在欧盟监管义务和国家政治需求之间取得平衡,使跨党派支持成为持久立法的关键。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03137

    +
    +
    +

    Utdelning av överskott i inkomstpensionssystemet

    +

    发布日期:

    +

    政府法案 2025/26:169 Utdelning av överskott i Prop. inkomstpensionssystemet 2025/26:169 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Anna Tenje (Socialdepartementet) Propositionens huvudsakliga innehåll I propositionen föreslås ett regelverk 这意味着什么: 在作出决定之前需要委员会审查和全体辩论。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03169

    +
    +
    +

    Framtidens yrkeshögskola

    +

    发布日期:

    +

    政府法案 2025/26:173 Framtidens yrkeshögskola Prop. 2025/26:173 Regeringen överlämnar denna proposition till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Propositionens huvudsakliga innehåll Propositionen innehåller förslag till ändringar i lagen 这意味着什么: 涉及教育政策领域。 教育提案必须在国家课程标准、市政实施自主权以及瑞典学校体系中私人提供者的争议角色之间取得平衡。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03173

    +
    +
    +

    瑞典国家审计局报告 om utbildning på vetenskaplig grund

    +

    发布日期:

    +

    政府通知 2025/26:166 Riksrevisionens rapport om utbildning på Skr. vetenskaplig grund 2025/26:166 Regeringen överlämnar denna skrivelse till riksdagen. Stockholm den 5 mars 2026 Ulf Kristersson Simona Mohamsson (Utbildningsdepartementet) Skrivelsens huvudsakliga innehåll I skrivelsen redovisar regeringen 这意味着什么: 涉及教育政策领域。 教育提案必须在国家课程标准、市政实施自主权以及瑞典学校体系中私人提供者的争议角色之间取得平衡。 ⚠ 微弱多数: 仅有0席多数,这项政府法案面临较高的否决风险。

    +

    HD03166

    +
    + +

    委员会报告 (18)

    +
    +

    Koncentration av viss länsstyrelseverksamhet

    +

    发布日期:

    +

    KU 委员会报告 (bet). 这意味着什么: 此事项已移交宪法委员会进行议会审查。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01KU37

    +
    +
    +

    Integritet och ny teknik 2020–2024

    +

    发布日期:

    +

    KU 委员会报告 (bet). 这意味着什么: 此事项已移交宪法委员会进行议会审查。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01KU36

    +
    +
    +

    Bättre förutsättningar för digitala kommunala sammanträden och förbättrad kontroll och uppföljning av privata utförare i kommuner och regioner

    +

    发布日期:

    +

    KU 委员会报告 (bet). 这意味着什么: 此事项已移交宪法委员会进行议会审查。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01KU35

    +
    +
    +

    Offentlig upphandling

    +

    发布日期:

    +

    FiU 委员会报告 (bet). 这意味着什么: 涉及财政政策领域。 财政委员会在财政事务上的立场通常是决定性的——议会几乎总是在预算问题上遵循其建议。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01FiU34

    +
    +
    +

    Statlig förvaltning och statistikfrågor

    +

    发布日期:

    +

    FiU 委员会报告 (bet). 这意味着什么: 涉及财政政策领域。 财政委员会在财政事务上的立场通常是决定性的——议会几乎总是在预算问题上遵循其建议。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01FiU25

    +
    +
    +

    Kommunala frågor

    +

    发布日期:

    +

    财政委员会 föreslår att riksdagen säger nej till 69 förslag om kommunala frågor. Alla utom ett förslag har lämnats in under den allmänna motionstiden 2025 och handlar bland annat om det kommunalekonomiska utjämningssystemet, riktade statsbidrag, vinster inom välfärden och social dumpning. Utskottet hänvisar till befintlig lagstiftning, pågående utredningar och arbete inom de områden som motionärerna lyfter. 这意味着什么: 涉及财政政策领域。 财政委员会在财政事务上的立场通常是决定性的——议会几乎总是在预算问题上遵循其建议。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01FiU26

    +
    +
    +

    Ekonomisk familjepolitik

    +

    发布日期:

    +

    社会保险委员会 föreslår att riksdagen säger nej till 104 förslag i motioner från den allmänna motionstiden 2025. Motionerna handlar bland annat om familjepolitikens inriktning, föräldraförsäkring, barnbidrag och bostadsbidrag. Utskottet hänvisar främst till pågående arbete inom de frågor som motionerna tar upp. 这意味着什么: 涉及移民政策领域。 社会保险委员会的移民报告在瑞典的欧盟法律义务、联合国难民公约承诺和国内政治压力之间进行协调。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01SfU17

    +
    +
    +

    Skärpningar av regelverket för invasiva främmande arter och naturvård

    +

    发布日期:

    +

    环境农业委员会 föreslår att riksdagen säger ja till regeringens förslag om att skärpa regelverket för invasiva främmande arter. Lagförslaget innebär bland annat att den som uppsåtligen eller av grov oaktsamhet för in en invasiv främmande art till Sverige från ett annat EU-land kan dömas till böter eller fängelse i högst två år. Förslaget innebär också att den som för in invasiva främmande arter från ett annat EU-land ska vara skyldig att anmäla arten till Tullverket. 这意味着什么: 涉及环境和气候政策, 医疗政策领域。 环境委员会的建议在气候雄心和经济竞争力之间取得平衡——其立场设定了立法基准线。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01MJU13

    +
    +
    +

    Finansiell stabilitet och finansmarknadsfrågor

    +

    发布日期:

    +

    财政委员会 har behandlat en skrivelse från regeringen om verksamheten i Internationella valutafonden (IMF) 2025. Skrivelsen behandlar bland annat institutionens arbete med övervakning, utlåning och kapacitetsutveckling för att främja ekonomisk utveckling och finansiell stabilitet. Av skrivelsen framgår även regeringens prioriteringar för IMF:s verksamhet. Liksom regeringen anser utskottet att IMF har en viktig roll i att stödja Ukraina. 这意味着什么: 涉及财政政策领域。 财政委员会在财政事务上的立场通常是决定性的——议会几乎总是在预算问题上遵循其建议。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01FiU22

    +
    +
    +

    Luftfartsfrågor

    +

    发布日期:

    +

    TU 委员会报告 (bet). 这意味着什么: 涉及交通政策领域。 交通委员会的报告指导瑞典的国家基础设施规划周期,直接影响长期投资优先事项。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01TU12

    +
    +
    +

    瑞典国家审计局报告 om socialförsäkringsskyddet vid internationell rörlighet

    +

    发布日期:

    +

    SfU 委员会报告 (bet). 这意味着什么: 涉及移民政策领域。 社会保险委员会的移民报告在瑞典的欧盟法律义务、联合国难民公约承诺和国内政治压力之间进行协调。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01SfU10

    +
    +
    +

    Cykelfrågor

    +

    发布日期:

    +

    TU 委员会报告 (bet). 这意味着什么: 涉及交通政策领域。 交通委员会的报告指导瑞典的国家基础设施规划周期,直接影响长期投资优先事项。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01TU13

    +
    +
    +

    Hund under kontroll

    +

    发布日期:

    +

    Länsstyrelserna ska få fler verktyg för att utöva tillsyn över hundar för att förebygga och hindra att hundar orsakar skador. Som exempel kan nämnas att länsstyrelserna får tillträde till bostäder och andra utrymmen för tillsynen och att de i större utsträckning får besluta om omedelbara omhändertaganden. 这意味着什么: 涉及环境和气候政策领域。 环境委员会的建议在气候雄心和经济竞争力之间取得平衡——其立场设定了立法基准线。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01MJU15

    +
    +
    +

    Nya regler för arbetskraftsinvandring

    +

    发布日期:

    +

    社会保险委员会 föreslår att riksdagen säger ja till regeringens lagförslag där villkoren för arbetskraftsinvandring skärps, samtidigt som invandring av högkvalificerad arbetskraft främjas. Bland annat införs det ett krav på heltäckande sjukförsäkring och ett lönekrav som villkor för arbetskraftsinvandring. Vissa yrkesgrupper ska kunna undantas från lönekravet medan andra yrkesgrupper ska kunna uteslutas från möjligheten att beviljas arbetstillstånd. 这意味着什么: 涉及移民政策领域。 社会保险委员会的移民报告在瑞典的欧盟法律义务、联合国难民公约承诺和国内政治压力之间进行协调。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01SfU12

    +
    +
    +

    Ändring av detaljplaner

    +

    发布日期:

    +

    CU 委员会报告 (bet). 这意味着什么: 涉及住房政策领域。 民事委员会的住房报告涉及瑞典最持久的政策挑战之一,委员会的决定可以推动或阻止重大监管改革。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01CU44

    +
    +
    +

    Mineralpolitik

    +

    发布日期:

    +

    工业贸易委员会 föreslår att riksdagen säger nej till 45 förslag i motioner från den allmänna motionstiden 2025. Förslagen handlar bland annat om allmänna frågor om gruv- och mineralnäring, kritiska metaller och mineral, tillståndsprocesser samt utvinning av uran. Utskottet hänvisar bland annat till pågående och utförda insatser samt till regeringens arbete med att ta fram en mineralstrategi. 这意味着什么: 涉及贸易和产业政策领域。 工业和贸易委员会通过制定投资、创新和出口条件的报告来塑造瑞典的商业环境。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01NU16

    +
    +
    +

    瑞典国家审计局报告 om Arbetsförmedlingens stöd till personer med funktionsnedsättning, m.m.

    +

    发布日期:

    +

    劳动市场委员会 föreslår ett tillkännagivande, en uppmaning, till regeringen om att se över förordningen om särskilda insatser för personer med funktionsnedsättning som medför nedsatt arbetsförmåga. Exempel på särskilda insatser är lönebidrag, bidrag till hjälpmedel på arbetsplatsen samt tolk för personer med syn- eller hörselnedsättning. Skälet till översynen är att säkerställa att villkoren för insatserna bidrar till att syftet med det arbetsmarknadspolitiska programmet uppfylls. 这意味着什么: 涉及劳动市场政策领域。 劳动委员会关于工作场所立法的报告必须在集体谈判自主权和设定法定最低标准之间进行协调。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01AU6

    +
    +
    +

    Inriktning för omhändertagandet av använt kärnbränsle och radioaktivt avfall

    +

    发布日期:

    +

    FöU 委员会报告 (bet). 这意味着什么: 涉及国防和安全政策领域。 关于国防的委员会报告塑造了瑞典的军事姿态和北约整合轨迹——这里的决定具有数十年的影响。 ⚠ 微弱多数: 仅有0席多数,这份委员会报告面临较高的否决风险。

    +

    HD01FöU22

    +
    + +

    动议 (15)

    +
    +

    回应法案 2025/26:146 Bättre migrationsrättsliga regler för forskare och doktorander samt åtgärder för att motverka missbruk av uppehållstillstånd för studier

    +

    发布日期:

    +

    Bättre migrationsrättsliga regler för forskare och doktorander samt åtgärder för att motverka missbruk av uppehållstillstånd för studier 这意味着什么: 涉及移民政策领域。 移民动议反映了瑞典最具争议的政策领域之一,各政党在庇护规则、融合要求和驱逐程序方面存在分歧。 历史背景:MP党动议的通过率为29.6%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023945

    +
    +
    +

    回应法案 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    发布日期:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap 这意味着什么: 涉及国防和安全政策领域。 国防动议在瑞典加入北约后具有更高的战略意义,向政府施压要求做出长期安全承诺。 历史背景:MP党动议的通过率为29.6%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023944

    +
    +
    +

    回应法案 2025/26:138 En särskild straffbestämmelse för psykiskt våld

    +

    发布日期:

    +

    En särskild straffbestämmelse för psykiskt våld 这意味着什么: 涉及司法政策领域。 司法动议涉及犯罪、量刑和治安——这些是公众关注度高的领域,反对党经常施压要求采取更严厉或更有针对性的措施。 历史背景:MP党动议的通过率为29.6%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023943

    +
    +
    +

    回应法案 2025/26:136 Kvalificering till socialförsäkringen

    +

    发布日期:

    +

    Kvalificering till socialförsäkringen 这意味着什么: 涉及移民政策领域。 移民动议反映了瑞典最具争议的政策领域之一,各政党在庇护规则、融合要求和驱逐程序方面存在分歧。 历史背景:MP党动议的通过率为29.6%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023942

    +
    +
    +

    回应法案 2025/26:133 Stärkt säkerhet vid allmänna sammankomster och offentliga tillställningar

    +

    发布日期:

    +

    动议 till riksdagen 2025/26:3941 av Lorena Delgado Varas och Daniel Riazat (båda med anledning av prop. 2025/26:133 Stärkt säkerhet vid allmänna sammankomster och offentliga tillställningar 这意味着什么: 涉及司法政策领域。 司法动议涉及犯罪、量刑和治安——这些是公众关注度高的领域,反对党经常施压要求采取更严厉或更有针对性的措施。

    +

    HD023941

    +
    +
    +

    回应法案 2025/26:127 Slopat krav på introduktionsutbildning för övningskörning

    +

    发布日期:

    +

    Slopat krav på introduktionsutbildning för övningskörning 这意味着什么: 涉及教育政策, 交通政策领域。 教育动议反映了在学校标准、教师薪酬和独立学校角色方面的深刻分歧——这是瑞典最具争议的国内辩论之一。 历史背景:C党动议的通过率为44.6%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023940

    +
    +
    +

    回应法案 2025/26:136 Kvalificering till socialförsäkringen

    +

    发布日期:

    +

    Kvalificering till socialförsäkringen 这意味着什么: 涉及移民政策领域。 移民动议反映了瑞典最具争议的政策领域之一,各政党在庇护规则、融合要求和驱逐程序方面存在分歧。 历史背景:C党动议的通过率为44.6%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023939

    +
    +
    +

    回应法案 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    发布日期:

    +

    动议 till riksdagen 2025/26:3938 av Mikael Larsson och Kerstin Lundgren (båda C) med anledning av prop. 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap 这意味着什么: 涉及国防和安全政策领域。 国防动议在瑞典加入北约后具有更高的战略意义,向政府施压要求做出长期安全承诺。

    +

    HD023938

    +
    +
    +

    回应法案 2025/26:131 Sysselsättning och boende på landsbygden

    +

    发布日期:

    +

    Sysselsättning och boende på landsbygden 这意味着什么: 涉及住房政策领域。 住房动议反映了对经济适用房的需求与规划法、租金监管和建筑成本压力之间的结构性紧张。 历史背景:C党动议的通过率为44.6%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023937

    +
    +
    +

    回应法案 2025/26:136 Kvalificering till socialförsäkringen

    +

    发布日期:

    +

    Kvalificering till socialförsäkringen 这意味着什么: 涉及移民政策领域。 移民动议反映了瑞典最具争议的政策领域之一,各政党在庇护规则、融合要求和驱逐程序方面存在分歧。 历史背景:S党动议的通过率为43.4%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023936

    +
    +
    +

    回应法案 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    发布日期:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap 这意味着什么: 涉及国防和安全政策领域。 国防动议在瑞典加入北约后具有更高的战略意义,向政府施压要求做出长期安全承诺。 历史背景:S党动议的通过率为43.4%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023935

    +
    +
    +

    回应法案 2025/26:134 ILO:s konvention om avskaffande av våld och trakasserier i arbetslivet och ILO:s konvention om en säker och hälsosam arbetsmiljö

    +

    发布日期:

    +

    ILO:s konvention om avskaffande av våld och trakasserier i arbetslivet och ILO:s konvention om en säker och hälsosam arbetsmiljö 这意味着什么: 涉及环境和气候政策, 劳动市场政策领域。 气候动议反映了议会对更快脱碳的日益增长的压力,通常针对特定行业或政策实施的速度。 历史背景:S党动议的通过率为43.4%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023934

    +
    +
    +

    回应法案 2025/26:138 En särskild straffbestämmelse för psykiskt våld

    +

    发布日期:

    +

    En särskild straffbestämmelse för psykiskt våld 这意味着什么: 涉及司法政策领域。 司法动议涉及犯罪、量刑和治安——这些是公众关注度高的领域,反对党经常施压要求采取更严厉或更有针对性的措施。 历史背景:S党动议的通过率为43.4%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023933

    +
    +
    +

    回应法案 2025/26:136 Kvalificering till socialförsäkringen

    +

    发布日期:

    +

    Kvalificering till socialförsäkringen 这意味着什么: 涉及移民政策领域。 移民动议反映了瑞典最具争议的政策领域之一,各政党在庇护规则、融合要求和驱逐程序方面存在分歧。 历史背景:V党动议的通过率为34.8%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023932

    +
    +
    +

    回应法案 2025/26:142 Ett starkare skydd för civilbefolkningen vid höjd beredskap

    +

    发布日期:

    +

    Ett starkare skydd för civilbefolkningen vid höjd beredskap 这意味着什么: 涉及国防和安全政策领域。 国防动议在瑞典加入北约后具有更高的战略意义,向政府施压要求做出长期安全承诺。 历史背景:V党动议的通过率为34.8%(96%的反对党动议被否决)。 这项动议发出的是政策立场信号,而非即将进行的立法变更。

    +

    HD023931

    +
    + +

    委员会活动

    +
    +
      +
    • Committee on Finance: 4
    • +
    • 宪法委员会: 3
    • +
    • Committee on Social Insurance: 3
    • +
    • Committee on Environment and Agriculture: 2
    • +
    • Committee on Transport: 2
    • +
    • Committee on Civil Affairs: 1
    • +
    • Committee on Industry and Trade: 1
    • +
    • Committee on Labour Market Affairs: 1
    • +
    • Committee on Defence: 1
    • +
    +
    + +

    政策影响

    +

    Klimat- och näringslivsdepartementet收到4项提案——表明这是政府的优先领域。

    +

    这些相互竞争的动态为明天的议会事务奠定了基础。

    + +
    +

    深度分析

    +

    发生了什么

    +

    移民政策 (10), 国防和安全政策 (5), 财政政策 (4), 住房政策 (4), 环境和气候政策 (3), 交通政策 (3)

    +

    委员会报告: 18, 政府法案: 17, 动议: 15

    +

    时间线与背景

    +

    18个活跃委员会共50项议会议题定义了当前的立法格局。活动的节奏表明推动这些程序的政治紧迫性。

    +

    为什么重要

    +

    涉及11个政策领域,这代表了一次广泛的立法推进,将塑造瑞典社会的多个方面。活动的广泛性使这一时期成为理解政府战略方向的关键时期。

    +

    赢家与输家

    +

    执政联盟以35项立法议题保持主动权,对比反对党的15项动议。

    +

    政治影响

    +

    17项政府提案若获通过,将直接改变瑞典法律和政策,影响公民、企业和机构。18份委员会报告代表立法审查的高潮,其建议指导议会投票。15项反对党动议挑战政府立场,尽管大多数动议历史上都被否决;它们指向未来的选举战场。0席的微弱多数意味着任何倒戈都可能击败政府措施,放大政治风险。

    +

    行动与后果

    +

    若获通过,这17项提案将在政府机构中触发实施,需要监管变更、预算分配和行政调整。未能通过将表明联盟的软弱并助长反对派的气焰。 15项反对派动议虽然可能被否决,但确立了反对党将在下一个选举周期中倡导的政策替代方案。否决并不会降低它们作为竞选弹药的战略价值。

    +

    批判性评估

    +

    这些议题没有可用的议会辩论数据,限制了我们评估议会审议深度的能力。应该监测这一信息差距——民主的质量取决于实质性辩论,而不仅仅是程序性通过。

    +
    + +

    关键要点

    +
    +
      +
    • 17 政府提案, 18 委员会报告, 15 动议 本期处理
    • +
    • 政策背景: 财政政策; 移民政策; 环境和气候政策; 医疗政策
    • +
    • 分析深度: 50 / 50
    • +
    • 历史背景:目前0席的多数意味着任何一位议员的倒戈都可能逆转结果。
    • +
    +
    + +

    联合政府动态

    +
    +

    联盟显示出较高的风险指标。稳定性得分为83,但由于零席多数优势,CIA风险评估显示为高风险。

    +
      +
    • 风险评分: 37/100
    • +
    • 风险等级:
    • +
    +
      +
    • 联盟多数优势仅有0席——任何倒戈都将危及政府的失败。
    • +
    • 在KD-M联盟议题上检测到异常高的跨党投票率(86.99%)。
    • +
    • 在L-M联盟议题上检测到异常高的跨党投票率(86.34%)。
    • +
    +
    + +

    本周活动

    +
    +
      +
    • 文件: 50
    • +
    • 演讲: 100
    • +
    • CIA稳定性趋势: 下降中 ↓
    • +
    +

    趋势洞察: 联盟稳定性高度波动——在轨迹上检测到重大变化。

    +
    + + + + +
    +

    🇸🇪 议会情报概述

    + +
    +
    +

    联合政府稳定性

    +
    + 83/100 + HIGH +
    +
    +
    +
    +
    +
    风险级别
    high
    +
    背叛概率
    23%
    +
    多数票优势
    0 席
    +
    +
    +
    +

    📊 政党绩效

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ID政党席位成功率%凝聚力趋势
    SArbetarepartiet-Socialdemokraterna10643.4%94%
    SDSverigedemokraterna7163.8%99%
    MModeraterna6886.5%100%
    CCenterpartiet2444.6%95%
    VVänsterpartiet2234.8%94%
    KDKristdemokraterna1981.9%99%
    MPMiljöpartiet de gröna1829.6%96%
    LLiberalerna1678.1%100%
    +
    +
    +
    +

    按议题的投票一致性

    +
    +
    + KD-M alignment +
    +
    + 联合政府 +
    +
    +
    + 84% +
    +
    + 反对党 +
    +
    +
    + 16% +
    +
    +
    +
    + L-M alignment +
    +
    + 联合政府 +
    +
    +
    + 83% +
    +
    + 反对党 +
    +
    +
    + 17% +
    +
    +
    +
    + KD-L alignment +
    +
    + 联合政府 +
    +
    +
    + 83% +
    +
    + 反对党 +
    +
    +
    + 17% +
    +
    +
    +
    + C-L alignment +
    +
    + 联合政府 +
    +
    +
    + 78% +
    +
    + 反对党 +
    +
    +
    + 22% +
    +
    +
    +
    + C-KD alignment +
    +
    + 联合政府 +
    +
    +
    + 77% +
    +
    + 反对党 +
    +
    +
    + 23% +
    +
    +
    +
    +
    +
    +

    来源:CIA平台——瑞典议会情报数据

    +
    +
    + +
    +
    +

    来源和数据

    +

    数据来源: riksdag-regering-mcp, Committee Reports, Government Propositions, Parliamentary Motions, Riksdagen Documents, Riksdagen Document Content, Riksdagen Voting Records, Riksdagen Speeches

    +

    生成者: 使用riksdag-regering-mcp的自动新闻系统

    +

    分析工具: 人工编辑监督下的AI辅助新闻

    +
    + +
    + + ← 返回新闻 + +
    +
    +
    + + + + + + + \ No newline at end of file diff --git a/news/metadata/batch-status.json b/news/metadata/batch-status.json index b9192652d11..dedf2634e10 100644 --- a/news/metadata/batch-status.json +++ b/news/metadata/batch-status.json @@ -33,5 +33,5 @@ "ko", "zh" ], - "timestamp": "2026-03-13T13:20:07.129Z" + "timestamp": "2026-03-14T16:16:58.547Z" } \ No newline at end of file diff --git a/news/metadata/last-generation.json b/news/metadata/last-generation.json index 9ae31708458..4960464620c 100644 --- a/news/metadata/last-generation.json +++ b/news/metadata/last-generation.json @@ -1,11 +1,9 @@ { - "timestamp": "2026-03-13T13:20:03.653Z", + "timestamp": "2026-03-14T16:16:38.288Z", "types": [ - "interpellations" + "weekly-review" ], "languagesGenerated": [ - "en", - "sv", "da", "no", "fi", @@ -37,23 +35,21 @@ ], "batchSize": "all", "skipExisting": true, - "generated": 14, + "generated": 12, "errors": 0, "articles": [ - "2026-03-13-interpellation-debates-en.html", - "2026-03-13-interpellation-debates-sv.html", - "2026-03-13-interpellation-debates-da.html", - "2026-03-13-interpellation-debates-no.html", - "2026-03-13-interpellation-debates-fi.html", - "2026-03-13-interpellation-debates-de.html", - "2026-03-13-interpellation-debates-fr.html", - "2026-03-13-interpellation-debates-es.html", - "2026-03-13-interpellation-debates-nl.html", - "2026-03-13-interpellation-debates-ar.html", - "2026-03-13-interpellation-debates-he.html", - "2026-03-13-interpellation-debates-ja.html", - "2026-03-13-interpellation-debates-ko.html", - "2026-03-13-interpellation-debates-zh.html" + "2026-03-14-weekly-review-da.html", + "2026-03-14-weekly-review-no.html", + "2026-03-14-weekly-review-fi.html", + "2026-03-14-weekly-review-de.html", + "2026-03-14-weekly-review-fr.html", + "2026-03-14-weekly-review-es.html", + "2026-03-14-weekly-review-nl.html", + "2026-03-14-weekly-review-ar.html", + "2026-03-14-weekly-review-he.html", + "2026-03-14-weekly-review-ja.html", + "2026-03-14-weekly-review-ko.html", + "2026-03-14-weekly-review-zh.html" ], "status": "enhanced", "note": "Enhanced script with MCP integration, multi-language support, and batch mode" diff --git a/styles.css b/styles.css index 8d920df5728..0dda1555df7 100644 --- a/styles.css +++ b/styles.css @@ -10234,6 +10234,124 @@ body.news-page { .news-article .cia-coalition-fill { background: #00d9ff; } .news-article .cia-opposition-fill { background: #ff006e; } +/* CIA Panel - risk level color variants (replaces inline style attributes) */ +.news-article .cia-score--positive, +.news-article .cia-panel-heading.cia-score--positive, +.news-article .cia-score-number.cia-score--positive { color: #83cf39; } + +.news-article .cia-risk-badge--high { + background: rgba(255, 0, 110, 0.125); + border: 1px solid #ff006e; + color: #ff006e; +} + +.news-article .cia-stability-bar--positive { background: #83cf39; } + +.news-article .cia-stat-value--risk { color: #ff006e; } + +/* CIA alignment bar width classes (replaces inline width styles) */ +.news-article .cia-bar-w-0 { width: 0%; } +.news-article .cia-bar-w-1 { width: 1%; } +.news-article .cia-bar-w-2 { width: 2%; } +.news-article .cia-bar-w-3 { width: 3%; } +.news-article .cia-bar-w-4 { width: 4%; } +.news-article .cia-bar-w-5 { width: 5%; } +.news-article .cia-bar-w-6 { width: 6%; } +.news-article .cia-bar-w-7 { width: 7%; } +.news-article .cia-bar-w-8 { width: 8%; } +.news-article .cia-bar-w-9 { width: 9%; } +.news-article .cia-bar-w-10 { width: 10%; } +.news-article .cia-bar-w-11 { width: 11%; } +.news-article .cia-bar-w-12 { width: 12%; } +.news-article .cia-bar-w-13 { width: 13%; } +.news-article .cia-bar-w-14 { width: 14%; } +.news-article .cia-bar-w-15 { width: 15%; } +.news-article .cia-bar-w-16 { width: 16%; } +.news-article .cia-bar-w-17 { width: 17%; } +.news-article .cia-bar-w-18 { width: 18%; } +.news-article .cia-bar-w-19 { width: 19%; } +.news-article .cia-bar-w-20 { width: 20%; } +.news-article .cia-bar-w-21 { width: 21%; } +.news-article .cia-bar-w-22 { width: 22%; } +.news-article .cia-bar-w-23 { width: 23%; } +.news-article .cia-bar-w-24 { width: 24%; } +.news-article .cia-bar-w-25 { width: 25%; } +.news-article .cia-bar-w-26 { width: 26%; } +.news-article .cia-bar-w-27 { width: 27%; } +.news-article .cia-bar-w-28 { width: 28%; } +.news-article .cia-bar-w-29 { width: 29%; } +.news-article .cia-bar-w-30 { width: 30%; } +.news-article .cia-bar-w-31 { width: 31%; } +.news-article .cia-bar-w-32 { width: 32%; } +.news-article .cia-bar-w-33 { width: 33%; } +.news-article .cia-bar-w-34 { width: 34%; } +.news-article .cia-bar-w-35 { width: 35%; } +.news-article .cia-bar-w-36 { width: 36%; } +.news-article .cia-bar-w-37 { width: 37%; } +.news-article .cia-bar-w-38 { width: 38%; } +.news-article .cia-bar-w-39 { width: 39%; } +.news-article .cia-bar-w-40 { width: 40%; } +.news-article .cia-bar-w-41 { width: 41%; } +.news-article .cia-bar-w-42 { width: 42%; } +.news-article .cia-bar-w-43 { width: 43%; } +.news-article .cia-bar-w-44 { width: 44%; } +.news-article .cia-bar-w-45 { width: 45%; } +.news-article .cia-bar-w-46 { width: 46%; } +.news-article .cia-bar-w-47 { width: 47%; } +.news-article .cia-bar-w-48 { width: 48%; } +.news-article .cia-bar-w-49 { width: 49%; } +.news-article .cia-bar-w-50 { width: 50%; } +.news-article .cia-bar-w-51 { width: 51%; } +.news-article .cia-bar-w-52 { width: 52%; } +.news-article .cia-bar-w-53 { width: 53%; } +.news-article .cia-bar-w-54 { width: 54%; } +.news-article .cia-bar-w-55 { width: 55%; } +.news-article .cia-bar-w-56 { width: 56%; } +.news-article .cia-bar-w-57 { width: 57%; } +.news-article .cia-bar-w-58 { width: 58%; } +.news-article .cia-bar-w-59 { width: 59%; } +.news-article .cia-bar-w-60 { width: 60%; } +.news-article .cia-bar-w-61 { width: 61%; } +.news-article .cia-bar-w-62 { width: 62%; } +.news-article .cia-bar-w-63 { width: 63%; } +.news-article .cia-bar-w-64 { width: 64%; } +.news-article .cia-bar-w-65 { width: 65%; } +.news-article .cia-bar-w-66 { width: 66%; } +.news-article .cia-bar-w-67 { width: 67%; } +.news-article .cia-bar-w-68 { width: 68%; } +.news-article .cia-bar-w-69 { width: 69%; } +.news-article .cia-bar-w-70 { width: 70%; } +.news-article .cia-bar-w-71 { width: 71%; } +.news-article .cia-bar-w-72 { width: 72%; } +.news-article .cia-bar-w-73 { width: 73%; } +.news-article .cia-bar-w-74 { width: 74%; } +.news-article .cia-bar-w-75 { width: 75%; } +.news-article .cia-bar-w-76 { width: 76%; } +.news-article .cia-bar-w-77 { width: 77%; } +.news-article .cia-bar-w-78 { width: 78%; } +.news-article .cia-bar-w-79 { width: 79%; } +.news-article .cia-bar-w-80 { width: 80%; } +.news-article .cia-bar-w-81 { width: 81%; } +.news-article .cia-bar-w-82 { width: 82%; } +.news-article .cia-bar-w-83 { width: 83%; } +.news-article .cia-bar-w-84 { width: 84%; } +.news-article .cia-bar-w-85 { width: 85%; } +.news-article .cia-bar-w-86 { width: 86%; } +.news-article .cia-bar-w-87 { width: 87%; } +.news-article .cia-bar-w-88 { width: 88%; } +.news-article .cia-bar-w-89 { width: 89%; } +.news-article .cia-bar-w-90 { width: 90%; } +.news-article .cia-bar-w-91 { width: 91%; } +.news-article .cia-bar-w-92 { width: 92%; } +.news-article .cia-bar-w-93 { width: 93%; } +.news-article .cia-bar-w-94 { width: 94%; } +.news-article .cia-bar-w-95 { width: 95%; } +.news-article .cia-bar-w-96 { width: 96%; } +.news-article .cia-bar-w-97 { width: 97%; } +.news-article .cia-bar-w-98 { width: 98%; } +.news-article .cia-bar-w-99 { width: 99%; } +.news-article .cia-bar-w-100 { width: 100%; } + .news-article .cia-bar-pct { flex: 0 0 2.5rem; text-align: right; From 1bac3e0a8f6f40762f31ff3577d0de0175fcbaf5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 19:45:59 +0000 Subject: [PATCH 15/24] =?UTF-8?q?fix:=20resolve=20merge=20conflicts=20with?= =?UTF-8?q?=20main=20=E2=80=94=20adopt=20main's=20interpellations=20genera?= =?UTF-8?q?tor,=20remove=20duplicate=20mottagare=20type,=20align=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .../constants/content-labels-part1.ts | 42 --- .../constants/content-labels-part2.ts | 42 --- .../content-generators/interpellations.ts | 201 ++-------- scripts/types/content.ts | 12 - tests/interpellations.test.ts | 343 +++--------------- 5 files changed, 80 insertions(+), 560 deletions(-) diff --git a/scripts/data-transformers/constants/content-labels-part1.ts b/scripts/data-transformers/constants/content-labels-part1.ts index 80552d388e8..8d679b5de65 100644 --- a/scripts/data-transformers/constants/content-labels-part1.ts +++ b/scripts/data-transformers/constants/content-labels-part1.ts @@ -72,12 +72,6 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Opposition MPs have filed ${n} new motions, mapping the political fault lines in the current Riksdag. These motions reveal not just policy disagreements but the strategic positioning of parties as they prepare for the next electoral contest.`, noInterpellations: 'No interpellations available at this time.', interpellationsBreakdown: (n: number): string => `Opposition MPs have submitted ${n} interpellations to government ministers, demanding accountability and formal responses on pressing policy issues. Interpellations are among the most powerful tools of parliamentary oversight, forcing ministers to defend government decisions before the chamber.`, - debateDynamics: 'Debate Dynamics & Accountability', - ministerAccountability: 'Directed to', - accountabilityAnalysis: 'Accountability Analysis', - interpellationBy: 'Interpellation by', - interpellationsScrutinyContext: (n: number): string => `Interpellations from ${n} different parties demonstrate broad parliamentary scrutiny of government ministers.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `These ${count} interpellations span ${domains} policy domain${domains > 1 ? 's' : ''}, reflecting the breadth of parliamentary accountability demands on the government.`, committeeCountContext: (n: number): string => `${n} reports from this committee signal intensive legislative work within its portfolio.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Parliamentary committees have been active across ${committees} and ${extra} further policy domains.` : `Parliamentary committees have been active across ${committees}.`, committeeMomentumTakeaway: (n: number): string => `A total of ${n} reports demonstrates sustained legislative momentum and ongoing policy prioritisation.`, @@ -184,12 +178,6 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} nya motioner som kartlägger de politiska skiljelinjerna i nuvarande riksdag.`, noInterpellations: 'Inga interpellationer tillgängliga för tillfället.', interpellationsBreakdown: (n: number): string => `Oppositionsledamöter har lämnat in ${n} interpellationer till regeringsministrar och kräver ansvarsskyldighet och formella svar i viktiga politikfrågor. Interpellationer är ett av riksdagens kraftfullaste verktyg för parlamentarisk kontroll och tvingar ministrar att försvara regeringsbeslut inför kammaren.`, - debateDynamics: 'Debattdynamik och ansvarsutkrävande', - ministerAccountability: 'Riktat till', - accountabilityAnalysis: 'Ansvarsutkrävande', - interpellationBy: 'Interpellation av', - interpellationsScrutinyContext: (n: number): string => `Interpellationer från ${n} olika partier visar på bred parlamentarisk granskning av regeringens ministrar.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `Dessa ${count} interpellationer spänner över ${domains} politikområde${domains > 1 ? 'n' : ''}, vilket avspeglar bredden i riksdagens ansvarsutkrävande av regeringen.`, committeeCountContext: (n: number): string => `${n} betänkanden från detta utskott signalerar intensivt lagstiftningsarbete inom dess ansvarsområde.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Riksdagens utskott har varit aktiva inom ${committees} och ${extra} ytterligare områden.` : `Riksdagens utskott har varit aktiva inom ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Totalt ${n} betänkanden visar lagstiftande momentum och pågående politisk prioritering.`, @@ -296,12 +284,6 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} nye forslag.`, noInterpellations: 'Ingen interpellationer tilgængelige på nuværende tidspunkt.', interpellationsBreakdown: (n: number): string => `Oppositionsmedlemmer har indgivet ${n} interpellationer til ministre og kræver ansvarlighed og formelle svar om presserende politiske spørgsmål.`, - debateDynamics: 'Debatdynamik og ansvarlighed', - ministerAccountability: 'Rettet til', - accountabilityAnalysis: 'Ansvarlighed', - interpellationBy: 'Interpellation af', - interpellationsScrutinyContext: (n: number): string => `Interpellationer fra ${n} forskellige partier viser en bred parlamentarisk granskning af regeringens ministre.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `Disse ${count} interpellationer dækker ${domains} politikområde${domains > 1 ? 'r' : ''}, der afspejler bredden i parlamentariske ansvarlighedskrav til regeringen.`, committeeCountContext: (n: number): string => `${n} betænkninger fra dette udvalg signalerer intensivt lovgivningsarbejde.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Parlamentets udvalg har været aktive inden for ${committees} og ${extra} yderligere politikområder.` : `Parlamentets udvalg har været aktive inden for ${committees}.`, committeeMomentumTakeaway: (n: number): string => `I alt ${n} betænkninger viser vedvarende lovgivningsmæssig fremdrift.`, @@ -408,12 +390,6 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} nye forslag.`, noInterpellations: 'Ingen interpellasjoner tilgjengelige for øyeblikket.', interpellationsBreakdown: (n: number): string => `Opposisjonsmedlemmer har innsendt ${n} interpellasjoner til statsråder og krever ansvarlighet og formelle svar om presserende politiske spørsmål.`, - debateDynamics: 'Debattdynamikk og ansvarlighet', - ministerAccountability: 'Rettet til', - accountabilityAnalysis: 'Ansvarlighetsanalyse', - interpellationBy: 'Interpellasjon av', - interpellationsScrutinyContext: (n: number): string => `Interpellasjoner fra ${n} ulike partier viser bred parlamentarisk gransking av regjeringens statsråder.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `Disse ${count} interpellasjonene spenner over ${domains} politikkområde${domains > 1 ? 'r' : ''}, noe som gjenspeiler bredden i parlamentariske ansvarlighetskrav til regjeringen.`, committeeCountContext: (n: number): string => `${n} innstillinger fra denne komiteen signaliserer intensivt lovgivningsarbeid.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Stortingets komiteer har vært aktive innen ${committees} og ${extra} ytterligere politikkområder.` : `Stortingets komiteer har vært aktive innen ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Totalt ${n} innstillinger viser vedvarende lovgivende fremdrift.`, @@ -520,12 +496,6 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} uutta aloitetta.`, noInterpellations: 'Ei interpellaatioita saatavilla tällä hetkellä.', interpellationsBreakdown: (n: number): string => `Oppositiokansanedustajat ovat jättäneet ${n} interpellaatiota ministereille vaatien vastuullisuutta ja virallisia vastauksia kiireellisiin poliittisiin kysymyksiin.`, - debateDynamics: 'Väittelyn dynamiikka ja vastuullisuus', - ministerAccountability: 'Osoitettu', - accountabilityAnalysis: 'Vastuullisuusanalyysi', - interpellationBy: 'Välikysymys esittäjältä', - interpellationsScrutinyContext: (n: number): string => `Välikysymykset ${n} eri puolueelta osoittavat laajaa parlamentaarista hallituksen ministereiden valvontaa.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `Nämä ${count} välikysymystä kattavat ${domains} politiikka-aluetta, mikä heijastaa eduskunnan vastuullisuusvaatimusten laajuutta.`, committeeCountContext: (n: number): string => `${n} mietintöä tästä valiokunnasta osoittavat intensiivistä lainsäädäntötyötä.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Eduskunnan valiokunnat ovat olleet aktiivisia aloilla ${committees} ja ${extra} muulla politiikka-alueella.` : `Eduskunnan valiokunnat ovat olleet aktiivisia aloilla ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Yhteensä ${n} mietintöä osoittaa jatkuvaa lainsäädännöllistä vauhtia.`, @@ -632,12 +602,6 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} neue Anträge eingereicht.`, noInterpellations: 'Derzeit keine Interpellationen verfügbar.', interpellationsBreakdown: (n: number): string => `Oppositionsabgeordnete haben ${n} Interpellationen an Regierungsminister eingereicht und fordern Rechenschaft sowie formelle Antworten zu drängenden politischen Fragen.`, - debateDynamics: 'Debattendynamik und Verantwortlichkeit', - ministerAccountability: 'Gerichtet an', - accountabilityAnalysis: 'Verantwortlichkeitsanalyse', - interpellationBy: 'Interpellation von', - interpellationsScrutinyContext: (n: number): string => `Interpellationen aus ${n} verschiedenen Parteien zeigen eine breite parlamentarische Kontrolle der Regierungsminister.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `Diese ${count} Interpellationen erstrecken sich über ${domains} Politikbereich${domains > 1 ? 'e' : ''}, was die Breite der parlamentarischen Rechenschaftsforderungen an die Regierung widerspiegelt.`, committeeCountContext: (n: number): string => `${n} Berichte dieses Ausschusses signalisieren intensive Gesetzgebungsarbeit.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Die Parlamentsausschüsse waren in den Bereichen ${committees} und ${extra} weiteren Politikfeldern aktiv.` : `Die Parlamentsausschüsse waren in den Bereichen ${committees} aktiv.`, committeeMomentumTakeaway: (n: number): string => `Insgesamt ${n} Berichte zeugen von anhaltendem Gesetzgebungsmomentum.`, @@ -744,12 +708,6 @@ export const CONTENT_LABELS_PART1: Partial> = { motionsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} nouvelles motions.`, noInterpellations: 'Aucune interpellation disponible pour le moment.', interpellationsBreakdown: (n: number): string => `Les députés de l'opposition ont déposé ${n} interpellations auprès des ministres du gouvernement, exigeant des comptes et des réponses formelles sur des questions politiques urgentes.`, - debateDynamics: 'Dynamique du débat et responsabilité', - ministerAccountability: 'Adressée à', - accountabilityAnalysis: 'Analyse de la responsabilité', - interpellationBy: 'Interpellation par', - interpellationsScrutinyContext: (n: number): string => `Des interpellations de ${n} partis différents démontrent un large contrôle parlementaire des ministres du gouvernement.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `Ces ${count} interpellations couvrent ${domains} domaine${domains > 1 ? 's' : ''} politique${domains > 1 ? 's' : ''}, reflétant l'ampleur des exigences de responsabilité parlementaire envers le gouvernement.`, committeeCountContext: (n: number): string => `${n} rapports de cette commission témoignent d'un travail législatif intensif.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Les commissions parlementaires ont été actives dans les domaines ${committees} et ${extra} autres domaines politiques.` : `Les commissions parlementaires ont été actives dans les domaines ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Un total de ${n} rapports démontre un élan législatif soutenu.`, diff --git a/scripts/data-transformers/constants/content-labels-part2.ts b/scripts/data-transformers/constants/content-labels-part2.ts index 0016ed603e8..88925528240 100644 --- a/scripts/data-transformers/constants/content-labels-part2.ts +++ b/scripts/data-transformers/constants/content-labels-part2.ts @@ -72,12 +72,6 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} nuevas mociones.`, noInterpellations: 'No hay interpelaciones disponibles en este momento.', interpellationsBreakdown: (n: number): string => `Los diputados de la oposición han presentado ${n} interpelaciones a ministros del gobierno, exigiendo rendición de cuentas y respuestas formales sobre cuestiones políticas urgentes.`, - debateDynamics: 'Dinámica del debate y responsabilidad', - ministerAccountability: 'Dirigida a', - accountabilityAnalysis: 'Análisis de responsabilidad', - interpellationBy: 'Interpelación de', - interpellationsScrutinyContext: (n: number): string => `Interpelaciones de ${n} partidos diferentes demuestran un amplio escrutinio parlamentario de los ministros del gobierno.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `Estas ${count} interpelaciones abarcan ${domains} ámbito${domains > 1 ? 's' : ''} de política, reflejando la amplitud de las demandas parlamentarias de responsabilidad al gobierno.`, committeeCountContext: (n: number): string => `${n} informes de esta comisión señalan un trabajo legislativo intensivo.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `Las comisiones parlamentarias han estado activas en las áreas de ${committees} y ${extra} dominios políticos más.` : `Las comisiones parlamentarias han estado activas en las áreas de ${committees}.`, committeeMomentumTakeaway: (n: number): string => `Un total de ${n} informes demuestra un impulso legislativo sostenido.`, @@ -184,12 +178,6 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `Oppositieleden hebben ${n} nieuwe moties ingediend.`, noInterpellations: 'Geen interpellaties beschikbaar op dit moment.', interpellationsBreakdown: (n: number): string => `Oppositieleden hebben ${n} interpellaties ingediend bij regeringsministers en eisen verantwoording en formele antwoorden op dringende politieke kwesties.`, - debateDynamics: 'Debatdynamiek en verantwoording', - ministerAccountability: 'Gericht aan', - accountabilityAnalysis: 'Verantwoordingsanalyse', - interpellationBy: 'Interpellatie door', - interpellationsScrutinyContext: (n: number): string => `Interpellaties van ${n} verschillende partijen tonen brede parlementaire controle op de ministers van de regering.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `Deze ${count} interpellaties bestrijken ${domains} beleidsterrein${domains > 1 ? 'en' : ''}, wat de breedte van parlementaire verantwoordingseisen aan de regering weerspiegelt.`, committeeCountContext: (n: number): string => `${n} rapporten van deze commissie signaleren intensief wetgevend werk.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `De parlementaire commissies zijn actief geweest op het gebied van ${committees} en ${extra} verdere beleidsterreinen.` : `De parlementaire commissies zijn actief geweest op het gebied van ${committees}.`, committeeMomentumTakeaway: (n: number): string => `In totaal ${n} rapporten tonen aanhoudend wetgevend momentum.`, @@ -296,12 +284,6 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} اقتراحات جديدة.`, noInterpellations: 'لا توجد استجوابات متاحة حالياً.', interpellationsBreakdown: (n: number): string => `قدم نواب المعارضة ${n} استجواباً لوزراء الحكومة، مطالبين بالمساءلة وردود رسمية على القضايا السياسية الملحّة.`, - debateDynamics: 'ديناميكيات النقاش والمساءلة', - ministerAccountability: 'موجه إلى', - accountabilityAnalysis: 'تحليل المساءلة', - interpellationBy: 'استجواب من', - interpellationsScrutinyContext: (n: number): string => `استجوابات من ${n} أحزاب مختلفة تُظهر رقابة برلمانية واسعة على وزراء الحكومة.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `تغطي هذه الاستجوابات الـ ${count} عدد ${domains} من مجالات السياسة، مما يعكس اتساع مطالب المساءلة البرلمانية تجاه الحكومة.`, committeeCountContext: (n: number): string => `${n} تقارير من هذه اللجنة تشير إلى عمل تشريعي مكثف.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `كانت اللجان البرلمانية نشطة في مجالات ${committees} و${extra} مجالات سياسية أخرى.` : `كانت اللجان البرلمانية نشطة في مجالات ${committees}.`, committeeMomentumTakeaway: (n: number): string => `إجمالي ${n} تقارير يدل على زخم تشريعي مستمر.`, @@ -408,12 +390,6 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} הצעות חדשות.`, noInterpellations: 'אין אינטרפלציות זמינות כרגע.', interpellationsBreakdown: (n: number): string => `חברי האופוזיציה הגישו ${n} אינטרפלציות לשרי הממשלה, ודורשים אחריות ותשובות רשמיות על סוגיות מדיניות דחופות.`, - debateDynamics: 'דינמיקת הדיון ואחריות', - ministerAccountability: 'מופנית אל', - accountabilityAnalysis: 'ניתוח אחריות', - interpellationBy: 'אינטרפלציה מאת', - interpellationsScrutinyContext: (n: number): string => `אינטרפלציות מ-${n} מפלגות שונות מדגימות פיקוח פרלמנטרי רחב על שרי הממשלה.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `${count} אינטרפלציות אלה מכסות ${domains} תחומי מדיניות, המשקפים את רוחב דרישות האחריותיות הפרלמנטריות כלפי הממשלה.`, committeeCountContext: (n: number): string => `${n} דוחות מוועדה זו מעידים על עבודת חקיקה אינטנסיבית.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `ועדות הפרלמנט היו פעילות בתחומי ${committees} ו-${extra} תחומי מדיניות נוספים.` : `ועדות הפרלמנט היו פעילות בתחומי ${committees}.`, committeeMomentumTakeaway: (n: number): string => `סך של ${n} דוחות מעיד על מומנטום חקיקתי מתמשך.`, @@ -520,12 +496,6 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `野党議員が${n}件の新たな動議を提出しました。`, noInterpellations: '現在、質問主意書はありません。', interpellationsBreakdown: (n: number): string => `野党議員が閣僚に対して${n}件の質問主意書を提出し、緊急の政策問題に関する説明責任と正式な回答を求めています。`, - debateDynamics: '討論の動態と説明責任', - ministerAccountability: '宛先', - accountabilityAnalysis: '説明責任分析', - interpellationBy: '質問主意書提出者', - interpellationsScrutinyContext: (n: number): string => `${n}の異なる政党からの質問主意書は、政府大臣に対する広範な議会の監視を示しています。`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `これら${count}件の質問主意書は${domains}の政策分野にまたがり、政府に対する議会の説明責任要求の幅広さを反映しています。`, committeeCountContext: (n: number): string => `この委員会からの${n}件の報告書は、集中的な立法作業を示しています。`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `議会の委員会は${committees}および他${extra}の政策分野で活動しています。` : `議会の委員会は${committees}の分野で活動しています。`, committeeMomentumTakeaway: (n: number): string => `合計${n}件の報告書は、持続的な立法の勢いを示しています。`, @@ -632,12 +602,6 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `야당 의원들이 ${n}개의 새 동의안을 제출했습니다.`, noInterpellations: '현재 이용 가능한 대정부질문이 없습니다.', interpellationsBreakdown: (n: number): string => `야당 의원들이 정부 장관에게 ${n}건의 대정부질문을 제출하여 긴박한 정책 문제에 대한 책임과 공식 답변을 요구하고 있습니다.`, - debateDynamics: '토론 역학 및 책임성', - ministerAccountability: '수신자', - accountabilityAnalysis: '책임성 분석', - interpellationBy: '질의 제출자', - interpellationsScrutinyContext: (n: number): string => `${n}개의 서로 다른 정당의 대정부 질의는 정부 장관에 대한 광범위한 의회 감시를 보여줍니다.`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `이 ${count}건의 대정부 질의는 ${domains}개의 정책 분야에 걸쳐 있으며, 정부에 대한 의회 책임 요구의 폭을 반영합니다.`, committeeCountContext: (n: number): string => `이 위원회의 ${n}건의 보고서는 집중적인 입법 작업을 나타냅니다.`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `의회 위원회는 ${committees} 및 ${extra}개의 추가 정책 분야에서 활동했습니다.` : `의회 위원회는 ${committees} 분야에서 활동했습니다.`, committeeMomentumTakeaway: (n: number): string => `총 ${n}건의 보고서는 지속적인 입법 추진력을 보여줍니다.`, @@ -744,12 +708,6 @@ export const CONTENT_LABELS_PART2: Partial> = { motionsBreakdown: (n: number): string => `反对党议员提交了${n}项新动议。`, noInterpellations: '目前没有可用的质询。', interpellationsBreakdown: (n: number): string => `反对党议员向政府部长提交了${n}份质询,要求对紧迫政策问题承担责任并作出正式答复。`, - debateDynamics: '辩论动态与问责制', - ministerAccountability: '质询对象', - accountabilityAnalysis: '问责分析', - interpellationBy: '质询提出者', - interpellationsScrutinyContext: (n: number): string => `来自${n}个不同政党的质询展示了对政府部长的广泛议会监督。`, - interpellationsAccountabilityContext: (count: number, domains: number): string => `这${count}项质询涵盖${domains}个政策领域,反映了议会对政府问责要求的广度。`, committeeCountContext: (n: number): string => `该委员会的${n}份报告表明密集的立法工作。`, committeeActivityTakeaway: (committees: string, extra: number): string => extra > 0 ? `议会委员会在${committees}以及其他${extra}个政策领域积极开展工作。` : `议会委员会在${committees}领域积极开展工作。`, committeeMomentumTakeaway: (n: number): string => `共${n}份报告展示了持续的立法势头。`, diff --git a/scripts/data-transformers/content-generators/interpellations.ts b/scripts/data-transformers/content-generators/interpellations.ts index c3d0cefe287..13b82579707 100644 --- a/scripts/data-transformers/content-generators/interpellations.ts +++ b/scripts/data-transformers/content-generators/interpellations.ts @@ -1,12 +1,7 @@ /** * @module data-transformers/content-generators/interpellations * @description Generator for "interpellations" article content. Renders parliamentary - * interpellations with debate dynamics analysis, minister accountability assessment, - * and political scrutiny context. - * - * Interpellations are formal parliamentary questions that opposition MPs address to - * ministers, who must respond in chamber debate. They are a key accountability tool - * in the Swedish Riksdag, distinct from opposition motions in purpose and procedure. + * interpellations grouped by submitting party and policy theme, with accountability analysis. * * @author Hack23 AB * @license Apache-2.0 @@ -19,128 +14,13 @@ import type { ArticleContentData, RawDocument } from '../types.js'; import { getPillarTransition } from '../../editorial-pillars.js'; import { L, - svSpan, - sanitizeUrl, normalizePartyKey, - formatDocumentDate, } from '../helpers.js'; -import { detectPolicyDomains, generateDeepPolicyAnalysis } from '../policy-analysis.js'; - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -/** - * Extract minister name or responsible department from an interpellation document. - * Interpellations are directed to a specific minister; this information may appear - * in the `mottagare` field or in the document title (pattern: "till X statsråd/minister"). - */ -function extractMinisterTarget(doc: RawDocument): string { - // Prefer the typed mottagare (recipient) field from the interpellations API - if (typeof doc.mottagare === 'string' && doc.mottagare.trim()) { - return doc.mottagare.trim(); - } - // Fall back to looking for minister-directed phrases in the title. - // Capture the full minister phrase including compound titles like "utrikesminister"/"finansminister". - // Only match when a minister-related term is present to avoid false positives (e.g. "till Gaza"). - const titleText = doc.titel || doc.title || ''; - const ministerMatch = titleText.match( - /(?:till|to)\s+(.{3,60}?\s*(?:statsråd(?:et[s]?)?|statsminister(?:n(?:s)?|s)?|(?:[\p{L}\p{M}]+)?minister(?:n(?:s)?|s)?))/iu - ); - if (ministerMatch?.[1]) { - return ministerMatch[1].trim(); - } - return ''; -} - -/** - * Render a single interpellation entry as an HTML block. - */ -function renderInterpellationEntry(doc: RawDocument, lang: Language | string): string { - const titleText = doc.titel || doc.title || ''; - const escapedTitle = escapeHtml(titleText); - const titleHtml = (doc.titel && !doc.title) - ? svSpan(escapedTitle, lang) - : escapedTitle; - - const docName = escapeHtml(doc.dokumentnamn || doc.dok_id || titleText); - // Filter out "Unknown" sentinel values that leak from enrichDocumentsWithContent() - const rawAuthor = doc.intressent_namn || doc.author || ''; - const authorText = (rawAuthor && rawAuthor.toLowerCase() !== 'unknown') - ? escapeHtml(rawAuthor) - : ''; - // Only show party when the raw value is present and not an "Unknown" sentinel - const rawParti = typeof doc.parti === 'string' ? doc.parti.trim() : ''; - const hasParty = rawParti !== '' && rawParti.toLowerCase() !== 'unknown'; - const partyText = hasParty ? escapeHtml(rawParti.toUpperCase()) : ''; - const dateHtml = formatDocumentDate(doc, lang); - const ministerTarget = extractMinisterTarget(doc); - - const whyItMattersLabel = L(lang, 'whyItMatters'); - const interpellationByLabel = L(lang, 'interpellationBy'); - const ministerAccountabilityLabel = L(lang, 'ministerAccountability'); - const readFullLabel = L(lang, 'readFullInterpellation'); - const defaultText = L(lang, 'interpellationDefault'); - - // Policy analysis — explains what accountability issue is being raised. - // 'ip' is the interpellation document type code. getDomainSpecificAnalysis() - // and generatePolicySignificance() recognise 'ip' and produce - // accountability-focused text referencing minister scrutiny and chamber - // debate rather than committee processing or proposals. - const policyAnalysis = generateDeepPolicyAnalysis(doc, lang, 'ip'); - - let html = `\n
    \n`; - html += `

    ${titleHtml}

    \n`; - if (dateHtml) html += `

    ${dateHtml}

    \n`; - - if (authorText || partyText) { - const authorLine = [ - authorText ? `${escapeHtml(String(interpellationByLabel))}: ${authorText}` : '', - partyText ? `${escapeHtml(String(L(lang, 'party')))}: ${partyText}` : '', - ].filter(Boolean).join('  |  '); - html += `

    ${authorLine}

    \n`; - } - - if (ministerTarget) { - html += `

    ${escapeHtml(String(ministerAccountabilityLabel))}: ${escapeHtml(ministerTarget)}

    \n`; - } - - const summaryText = doc.summary || doc.notis - ? (doc.summary || doc.notis || '') - : String(defaultText); - const summaryHtml = (doc.titel && !doc.title && (doc.summary || doc.notis)) - ? svSpan(escapeHtml(summaryText), lang) - : escapeHtml(summaryText); - - html += `

    ${summaryHtml}

    \n`; - html += `

    ${escapeHtml(String(whyItMattersLabel))}: ${policyAnalysis}

    \n`; - - if (doc.url) { - html += `

    ${escapeHtml(String(readFullLabel))}: ${docName}

    \n`; - } - - html += `
    \n`; - return html; -} - -// --------------------------------------------------------------------------- -// Public API -// --------------------------------------------------------------------------- +import { detectPolicyDomains } from '../policy-analysis.js'; +import { + renderInterpellationEntry, +} from '../document-analysis.js'; -/** - * Generate article content for the "interpellations" article type. - * - * Produces a structured accountability-focused article covering: - * 1. **Lede** — Count and significance of interpellations filed - * 2. **Debate Dynamics** — Per-party breakdown of accountability issues raised - * 3. **Thematic analysis** — Interpellations grouped by policy domain - * 4. **Deep Analysis** — 5W framework (who, what, when, why, impact) - * 5. **Coalition Dynamics** — Cross-party accountability patterns - * - * @param data - Article content data (interpellations loaded in `data.interpellations`) - * @param lang - Target language - * @returns Generated HTML content string - */ export function generateInterpellationsContent(data: ArticleContentData, lang: Language | string): string { const interpellations: RawDocument[] = data.interpellations || []; @@ -155,66 +35,56 @@ export function generateInterpellationsContent(data: ArticleContentData, lang: L const breakdownFn = L(lang, 'interpellationsBreakdown') as string | ((n: number) => string); const breakdownText = typeof breakdownFn === 'function' ? breakdownFn(interpellations.length) - : `${interpellations.length} new interpellations filed.`; + : `${interpellations.length} interpellations submitted.`; content += `

    ${escapeHtml(String(breakdownText))}

    \n`; - // Group by party for party accountability overview + // Group interpellations by submitting party for strategic analysis const byParty: Record = {}; - interpellations.forEach(ip => { - const party = normalizePartyKey(ip.parti); + interpellations.forEach(interp => { + const party = normalizePartyKey(interp.parti); if (!byParty[party]) byParty[party] = []; - byParty[party].push(ip); + byParty[party].push(interp); }); const partyCount = Object.keys(byParty).filter(p => p !== 'other').length; - // Debate dynamics section — how many parties are holding ministers accountable - content += `\n

    ${L(lang, 'debateDynamics')}

    \n`; + // Parliamentary oversight strategy section with per-party analysis if (partyCount > 1) { - const scrutinyFn = L(lang, 'interpellationsScrutinyContext') as string | ((n: number) => string); - const scrutinyContext = typeof scrutinyFn === 'function' - ? scrutinyFn(partyCount) - : `Interpellations from ${partyCount} different parties demonstrate broad parliamentary scrutiny of government ministers.`; - content += `

    ${escapeHtml(String(scrutinyContext))}

    \n`; + content += `\n

    ${L(lang, 'oppositionStrategy')}

    \n`; + const strategyFn = L(lang, 'interpellationStrategyContext') as string | ((n: number) => string); + const strategyContext = typeof strategyFn === 'function' + ? strategyFn(partyCount) + : `Interpellations from ${partyCount} different parties reveal coordinated parliamentary oversight efforts.`; + content += `

    ${escapeHtml(String(strategyContext))}

    \n`; } - // Group interpellations by policy domain for thematic accountability analysis + // Group interpellations by primary policy theme for thematic analysis const byTheme: Record = {}; - interpellations.forEach(ip => { - const domains = detectPolicyDomains(ip, lang); + interpellations.forEach(interp => { + const domains = detectPolicyDomains(interp, lang); const theme = domains[0] || String(L(lang, 'generalMatters')); if (!byTheme[theme]) byTheme[theme] = []; - byTheme[theme].push(ip); + byTheme[theme].push(interp); }); const themeCount = Object.keys(byTheme).length; - // Thematic analysis — grouped by accountability domain if (themeCount > 1) { content += `\n

    ${L(lang, 'thematicAnalysis')}

    \n`; - Object.entries(byTheme).forEach(([theme, themeIps]) => { - content += `\n

    ${escapeHtml(theme)} (${themeIps.length})

    \n`; - themeIps.forEach(ip => { - const entryHtml = renderInterpellationEntry(ip, lang); - // Demote h3 → h4 inside a themed section to maintain hierarchy - content += entryHtml + Object.entries(byTheme).forEach(([theme, themeInterps]) => { + content += `\n

    ${escapeHtml(theme)} (${themeInterps.length})

    \n`; + themeInterps.forEach(interp => { + // Demote entry headings one level when inside a themed section + const entryHtml = renderInterpellationEntry(interp, lang); + const demotedHtml = entryHtml .replace(/]*)?>/g, '') .replace(/<\/h3>/g, ''); + content += demotedHtml; }); }); } else { - // Single theme or ungrouped: flat list under the debate dynamics section - interpellations.forEach(ip => { - content += renderInterpellationEntry(ip, lang); - }); + // Single theme or no detection: flat list + interpellations.forEach(interp => { content += renderInterpellationEntry(interp, lang); }); } - // Accountability Analysis section - content += `\n

    ${L(lang, 'accountabilityAnalysis')}

    \n`; - const accountabilityFn = L(lang, 'interpellationsAccountabilityContext') as string | ((count: number, domains: number) => string); - const accountabilityText = typeof accountabilityFn === 'function' - ? accountabilityFn(interpellations.length, themeCount) - : `These ${interpellations.length} interpellations span ${themeCount} policy domain${themeCount !== 1 ? 's' : ''}, reflecting the breadth of parliamentary accountability demands on the government.`; - content += `

    ${escapeHtml(String(accountabilityText))}

    \n`; - // Deep Analysis section (5W framework) content += generateDeepAnalysisSection({ documents: interpellations, @@ -223,28 +93,27 @@ export function generateInterpellationsContent(data: ArticleContentData, lang: L articleType: 'interpellations', }); - // Coalition dynamics — which parties are driving accountability + // Party accountability breakdown if (partyCount > 0) { - // Narrative bridge before coalition dynamics (inter-pillar transition) const watchTransition = getPillarTransition(lang, 'watchToOpposition'); if (watchTransition) { content += `

    ${escapeHtml(watchTransition)}

    \n`; } content += `\n

    ${L(lang, 'coalitionDynamics')}

    \n`; content += `
    \n
      \n`; - Object.entries(byParty).forEach(([party, partyIps]) => { + Object.entries(byParty).forEach(([party, partyInterps]) => { if (party !== 'other') { const detailFn = L(lang, 'partyInterpellationsFiled') as string | ((party: string, n: number) => string); const detail = typeof detailFn === 'function' - ? detailFn(party, partyIps.length) - : `${party}: ${partyIps.length} interpellation${partyIps.length > 1 ? 's' : ''} filed`; + ? detailFn(party, partyInterps.length) + : `${party}: ${partyInterps.length} interpellations filed`; content += `
    • ${escapeHtml(String(detail))}
    • \n`; } }); content += `
    \n
    \n`; } - // Government department engagement section (from data pipeline) + // Government department engagement section const govDeptData = data.govDeptData ?? []; if (govDeptData.length > 0) { content += `\n

    ${L(lang, 'govEngagement')}

    \n`; diff --git a/scripts/types/content.ts b/scripts/types/content.ts index 8e3c919d29b..3a278cc7ba1 100644 --- a/scripts/types/content.ts +++ b/scripts/types/content.ts @@ -106,18 +106,6 @@ export interface ContentLabelSet { noInterpellations: string; /** Analytical lede for interpellations articles */ interpellationsBreakdown: (n: number) => string; - /** Section heading for debate dynamics */ - debateDynamics: string; - /** Label for the target minister (recipient), e.g. "Directed to" */ - ministerAccountability: string; - /** Section heading for accountability analysis */ - accountabilityAnalysis: string; - /** Author attribution for interpellations (e.g. "Interpellation by") */ - interpellationBy: string; - /** Debate dynamics context — interpellations-specific (replaces oppositionStrategyContext for interpellations) */ - interpellationsScrutinyContext: (n: number) => string; - /** Accountability analysis context — interpellations-specific (replaces policyImplicationsContext for interpellations) */ - interpellationsAccountabilityContext: (count: number, domains: number) => string; // Localized body text labels (avoid hardcoded en/sv fallbacks) committeeCountContext: (n: number) => string; committeeActivityTakeaway: (committees: string, extra: number) => string; diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts index 40dd925b7b1..aec98039074 100644 --- a/tests/interpellations.test.ts +++ b/tests/interpellations.test.ts @@ -2,9 +2,9 @@ * Unit Tests for Interpellations Content Generator * Validates: * - Output does not contain motion/proposition-specific phrasing - * - Minister target extraction does not misparse titles like "... till Gaza" * - Interpellations label keys exist for all 14 languages - * - Party rendering skips missing/unknown parties (no "OTHER") + * - Party rendering and coalition dynamics work correctly + * - Empty state renders appropriately */ import { describe, it, expect, afterEach, vi } from 'vitest'; @@ -41,14 +41,9 @@ describe('Interpellations Content Generator', () => { 'noInterpellations', 'interpellationDefault', 'interpellationsBreakdown', - 'debateDynamics', - 'ministerAccountability', - 'accountabilityAnalysis', 'readFullInterpellation', - 'interpellationBy', - 'interpellationsScrutinyContext', - 'interpellationsAccountabilityContext', 'partyInterpellationsFiled', + 'interpellationStrategyContext', ] as const; ALL_LANGUAGES.forEach((lang) => { @@ -67,22 +62,11 @@ describe('Interpellations Content Generator', () => { expect(fn(3)).toContain('3'); }); - it('should have function-valued interpellationsScrutinyContext for en', () => { - const fn = CONTENT_LABELS.en.interpellationsScrutinyContext; + it('should have function-valued interpellationStrategyContext for en', () => { + const fn = CONTENT_LABELS.en.interpellationStrategyContext; expect(typeof fn).toBe('function'); const result = fn(4); expect(result).toContain('4'); - expect(result).not.toContain('Motion'); - expect(result).not.toContain('motion'); - }); - - it('should have function-valued interpellationsAccountabilityContext for en', () => { - const fn = CONTENT_LABELS.en.interpellationsAccountabilityContext; - expect(typeof fn).toBe('function'); - const result = fn(5, 3); - expect(result).toContain('5'); - expect(result).toContain('3'); - expect(result).not.toContain('proposition'); }); it('should have function-valued partyInterpellationsFiled for en', () => { @@ -94,20 +78,10 @@ describe('Interpellations Content Generator', () => { expect(result).toContain('interpellation'); expect(result).not.toContain('motion'); }); - - it('ministerAccountability should NOT say "Minister Response" in en', () => { - expect(CONTENT_LABELS.en.ministerAccountability).not.toContain('Response'); - expect(CONTENT_LABELS.en.ministerAccountability).toContain('Directed'); - }); - - it('ministerAccountability should NOT say "Ministersvar" in sv', () => { - expect(CONTENT_LABELS.sv.ministerAccountability).not.toContain('Ministersvar'); - expect(CONTENT_LABELS.sv.ministerAccountability).toContain('Riktat'); - }); }); describe('Output content', () => { - it('should generate interpellations content without motion phrasing', () => { + it('should generate interpellations content with interpellation-specific heading', () => { const data = { interpellations: [ { @@ -133,37 +107,15 @@ describe('Interpellations Content Generator', () => { // Should contain interpellations-specific content expect(result).toContain('Interpellation'); - expect(result).toContain('Debate Dynamics'); - expect(result).toContain('Accountability Analysis'); + expect(result).toContain('interpellation-entry'); // Should NOT contain motion-specific phrasing expect(result).not.toContain('Opposition Motions'); expect(result).not.toContain('Read the full motion'); expect(result).not.toContain('motion-entry'); - // Should not use the motions-specific "Motions from X parties" label - expect(result).not.toMatch(/Motions from \d+ different parties/); - }); - - it('should not contain proposition-specific phrasing in accountability section', () => { - const data = { - interpellations: [ - { - titel: 'Om försvaret till statsrådet', - parti: 'M', - url: 'https://riksdagen.se/ip3', - dokumentnamn: 'IP 2025/26:3', - intressent_namn: 'Defense Person', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - // Should not reference propositions in the accountability section - expect(result).not.toContain('propositions touch on'); - expect(result).not.toContain('legislative ambition'); }); - it('should use interpellations-specific party count label', () => { + it('should use interpellations-specific party count in coalition dynamics', () => { const data = { interpellations: [ { titel: 'IP 1', parti: 'V', url: '#', dokumentnamn: 'IP1' }, @@ -176,212 +128,19 @@ describe('Interpellations Content Generator', () => { expect(result).toContain('interpellation'); expect(result).not.toMatch(/\d+ motion[s]? filed/); }); - }); - - describe('Minister target extraction', () => { - it('should extract target from mottagare field', () => { - const data = { - interpellations: [ - { - titel: 'Fråga om humanitär hjälp till Gaza', - parti: 'V', - url: '#', - dokumentnamn: 'IP1', - mottagare: 'Utrikesministern', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('Utrikesministern'); - }); - - it('should NOT extract "Gaza" as minister from title "... till Gaza"', () => { - const data = { - interpellations: [ - { - titel: 'Humanitär hjälp till Gaza – en fråga om solidaritet', - parti: 'V', - url: '#', - dokumentnamn: 'IP1', - // No mottagare field - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - // Should NOT show "Gaza" as a minister target - expect(result).not.toContain('Directed to Gaza'); - expect(result).not.toContain('Directed to: Gaza'); - }); - - it('should extract target from "till X statsråd" pattern', () => { - const data = { - interpellations: [ - { - titel: 'Interpellation till justitie- och inrikesminister', - parti: 'SD', - url: '#', - dokumentnamn: 'IP1', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - // The regex should match "till justitie- och inrikes" before "minister" - expect(result).toContain('Directed to'); - }); - - it('should extract full compound minister title like "utrikesminister"', () => { - const data = { - interpellations: [ - { - titel: 'Fråga till utrikesministern om bistånd', - parti: 'V', - url: '#', - dokumentnamn: 'IP2', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - // The regex should capture "utrikesministern" — not just "utrikes" - expect(result).toContain('utrikesministern'); - }); - - it('should extract full compound "finansministern" target', () => { - const data = { - interpellations: [ - { - titel: 'Interpellation till finansministern', - parti: 'M', - url: '#', - dokumentnamn: 'IP3', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('finansministern'); - }); - - it('should capture possessive "ministerns" suffix (not drop trailing s)', () => { - const data = { - interpellations: [ - { - titel: 'Interpellation till finansministerns ansvar', - parti: 'S', - url: '#', - dokumentnamn: 'IP4', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('finansministerns'); - }); - - it('should capture "statsrådets" inflection', () => { - const data = { - interpellations: [ - { - titel: 'Interpellation till statsrådets politik', - parti: 'V', - url: '#', - dokumentnamn: 'IP5', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('statsrådets'); - }); - - it('should extract compound minister title with Swedish diacritics like "miljöministern"', () => { - const data = { - interpellations: [ - { - titel: 'Interpellation till miljöministern om kemikaliepolitiken', - parti: 'MP', - url: '#', - dokumentnamn: 'IP-MILJO', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - // The regex must match ö in "miljöministern" via Unicode \p{L} - expect(result).toContain('miljöministern'); - }); - - it('should extract compound minister title with ö like "försvarsministern"', () => { - const data = { - interpellations: [ - { - titel: 'Interpellation till försvarsministern om säkerhetspolitik', - parti: 'S', - url: '#', - dokumentnamn: 'IP-FORSVAR', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('försvarsministern'); - }); - }); - - describe('Party rendering', () => { - it('should NOT display "OTHER" when party is missing', () => { - const data = { - interpellations: [ - { - titel: 'Test interpellation', - url: '#', - dokumentnamn: 'IP1', - intressent_namn: 'Some Author', - // parti is not set - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).not.toContain('OTHER'); - }); - - it('should NOT display "OTHER" when party is "Unknown"', () => { - const data = { - interpellations: [ - { - titel: 'Test interpellation', - url: '#', - dokumentnamn: 'IP1', - intressent_namn: 'Some Author', - parti: 'Unknown', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).not.toContain('OTHER'); - expect(result).not.toContain('UNKNOWN'); - }); - it('should display actual party when present', () => { + it('should render parliamentary oversight strategy for multi-party interpellations', () => { const data = { interpellations: [ - { - titel: 'Test interpellation', - url: '#', - dokumentnamn: 'IP1', - intressent_namn: 'Some Author', - parti: 'V', - }, + { titel: 'IP 1', parti: 'V', url: '#', dokumentnamn: 'IP1' }, + { titel: 'IP 2', parti: 'S', url: '#', dokumentnamn: 'IP2' }, + { titel: 'IP 3', parti: 'M', url: '#', dokumentnamn: 'IP3' }, ] as MockInterpellation[], }; const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('Party: V'); + // Should contain multi-party strategy context + expect(result).toContain('Opposition Strategy'); }); }); @@ -391,86 +150,74 @@ describe('Interpellations Content Generator', () => { const result = generateArticleContent(data, 'interpellations', 'en') as string; expect(result).toContain('No interpellations'); }); + + it('should show Swedish no-interpellations message', () => { + const data = { interpellations: [] as MockInterpellation[] }; + const result = generateArticleContent(data, 'interpellations', 'sv') as string; + expect(result).toContain('Inga interpellationer'); + }); }); - describe('Author sentinel filtering', () => { - it('should NOT display "Unknown" as author name', () => { + describe('Data contract', () => { + it('should use data.interpellations when provided', () => { const data = { interpellations: [ { - titel: 'Test interpellation', + titel: 'Interpellation from interpellations field', url: '#', - dokumentnamn: 'IP1', - intressent_namn: 'Unknown', - parti: 'V', + dokumentnamn: 'IP-DOC1', + parti: 'M', }, ] as MockInterpellation[], }; const result = generateArticleContent(data, 'interpellations', 'en') as string; - // Should not render the sentinel value "Unknown" to readers - expect(result).not.toContain('Interpellation by: Unknown'); + expect(result).toContain('Interpellation from interpellations field'); + expect(result).not.toContain('No interpellations'); }); - it('should NOT display "unknown" (lowercase) as author name', () => { + it('should show empty state when interpellations array is empty', () => { const data = { - interpellations: [ - { - titel: 'Test interpellation', - url: '#', - dokumentnamn: 'IP1', - author: 'unknown', - }, - ] as MockInterpellation[], + interpellations: [] as MockInterpellation[], }; const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).not.toContain('Interpellation by: unknown'); + expect(result).toContain('No interpellations'); }); + }); - it('should display real author names', () => { + describe('Analytical lede', () => { + it('should include analytical lede with document count', () => { const data = { interpellations: [ - { - titel: 'Test interpellation', - url: '#', - dokumentnamn: 'IP1', - intressent_namn: 'Anna Andersson', - parti: 'S', - }, + { titel: 'IP 1', parti: 'V', url: '#', dokumentnamn: 'IP1' }, + { titel: 'IP 2', parti: 'S', url: '#', dokumentnamn: 'IP2' }, + { titel: 'IP 3', parti: 'M', url: '#', dokumentnamn: 'IP3' }, ] as MockInterpellation[], }; const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('Interpellation by: Anna Andersson'); + expect(result).toContain('article-lede'); + expect(result).toContain('3'); }); }); - describe('Data fallback', () => { - it('should use data.interpellations when provided', () => { + describe('Policy analysis', () => { + it('should render ip-specific policy analysis text (not committee review)', () => { const data = { interpellations: [ { - titel: 'Interpellation from interpellations field', + titel: 'Interpellation om vård', + parti: 'V', url: '#', - dokumentnamn: 'IP-DOC1', - parti: 'M', + dokumentnamn: 'IP1', }, ] as MockInterpellation[], }; const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('Interpellation from interpellations field'); - expect(result).not.toContain('No interpellations'); - }); - - it('should show empty state when interpellations is empty', () => { - const data = { - interpellations: [] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('No interpellations'); + // The ip-specific policy analysis should reference accountability, not committee review + expect(result).not.toContain('Requires committee review'); }); }); }); From 62f241d17fdb9e90349a9d7af3a2df5edb17a90a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 19:50:37 +0000 Subject: [PATCH 16/24] =?UTF-8?q?Merge=20branch=20'main'=20into=20copilot/?= =?UTF-8?q?enhance-content-generators-pipeline=20=E2=80=94=20resolve=20con?= =?UTF-8?q?flicts,=20remove=20duplicate=20mottagare,=20align=20tests=20wit?= =?UTF-8?q?h=20main's=20interpellations=20generator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- scripts/data-transformers/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/data-transformers/types.ts b/scripts/data-transformers/types.ts index 14e6d64d6ac..e545c4a9d3a 100644 --- a/scripts/data-transformers/types.ts +++ b/scripts/data-transformers/types.ts @@ -45,7 +45,7 @@ export interface RawDocument { intressent_namn?: string; author?: string; parti?: string; - /** Recipient minister for interpellations (mottagare field from the API) */ + /** Recipient minister / target for interpellations (mottagare field from the Riksdag API) */ mottagare?: string; /** Publication date from the MCP API (ISO format YYYY-MM-DD) */ datum?: string; From 19302d73af8c6ba80d7899da3079c2088b9994ca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 19:52:39 +0000 Subject: [PATCH 17/24] fix: remove duplicate mottagare type definition re-introduced by merge Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- scripts/data-transformers/types.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/data-transformers/types.ts b/scripts/data-transformers/types.ts index 7f9a2fddffc..e545c4a9d3a 100644 --- a/scripts/data-transformers/types.ts +++ b/scripts/data-transformers/types.ts @@ -57,8 +57,6 @@ export interface RawDocument { fullContent?: string; /** Whether this document was enriched with full content */ contentFetched?: boolean; - /** Target minister / recipient (used by interpellations) */ - mottagare?: string; /** Related speeches mentioning this document */ speeches?: Array<{ talare?: string; parti?: string; text?: string; anforande_nummer?: string }>; } From d3a17636ef3d967476c3c796121b917a340620ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 19:55:03 +0000 Subject: [PATCH 18/24] test: improve interpellations tests with positive assertions for heading elements, strategy context, and policy analysis Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- tests/interpellations.test.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts index aec98039074..b8afd1d7920 100644 --- a/tests/interpellations.test.ts +++ b/tests/interpellations.test.ts @@ -105,8 +105,9 @@ describe('Interpellations Content Generator', () => { const result = generateArticleContent(data, 'interpellations', 'en') as string; - // Should contain interpellations-specific content - expect(result).toContain('Interpellation'); + // Should contain interpellations-specific heading element + expect(result).toMatch(/]*>.*Interpellation.*<\/h2>/); + // Should have interpellation-entry CSS class expect(result).toContain('interpellation-entry'); // Should NOT contain motion-specific phrasing @@ -139,8 +140,10 @@ describe('Interpellations Content Generator', () => { }; const result = generateArticleContent(data, 'interpellations', 'en') as string; - // Should contain multi-party strategy context + // Should contain multi-party strategy context heading expect(result).toContain('Opposition Strategy'); + // Should contain interpellation-specific strategy context (not motions phrasing) + expect(result).toContain('parliamentary oversight'); }); }); @@ -218,6 +221,8 @@ describe('Interpellations Content Generator', () => { const result = generateArticleContent(data, 'interpellations', 'en') as string; // The ip-specific policy analysis should reference accountability, not committee review expect(result).not.toContain('Requires committee review'); + // Should contain accountability-focused analysis (from ip doktyp in policy-analysis.ts) + expect(result).toContain('Why It Matters'); }); }); }); From 8baa862c686048d93b8f29641f9ea7739b09e24e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 21:01:37 +0000 Subject: [PATCH 19/24] fix: localize ip fallback text for all 14 languages in generatePolicySignificance() Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- scripts/data-transformers/policy-analysis.ts | 20 +++++++++++++++++--- tests/data-transformers.test.ts | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/scripts/data-transformers/policy-analysis.ts b/scripts/data-transformers/policy-analysis.ts index a8db2532414..7336913d15c 100644 --- a/scripts/data-transformers/policy-analysis.ts +++ b/scripts/data-transformers/policy-analysis.ts @@ -596,9 +596,23 @@ export function generatePolicySignificance(doc: RawDocument, lang: Language | st // Interpellation-specific fallback: references minister obligation, not committee review const doktyp2 = doc.doktyp || doc.documentType || impliedDoktyp || ''; if (doktyp2 === 'ip') { - return lang === 'sv' - ? 'Interpellationen debatteras i kammaren där ministern är skyldig att svara och förklara sina beslut.' - : 'The interpellation is debated in the chamber, where the minister is obliged to respond and be held accountable.'; + const ipFallback: Record = { + sv: 'Interpellationen debatteras i kammaren där ministern är skyldig att svara och förklara sina beslut.', + da: 'Interpellationen debatteres i Folketinget, hvor ministeren er forpligtet til at svare og stå til ansvar.', + no: 'Interpellasjonen debatteres i Stortinget, der ministeren er forpliktet til å svare og stå til ansvar.', + fi: 'Välikysymys käsitellään täysistunnossa, jossa ministerin on vastattava ja oltava tilivelvollinen.', + de: 'Die Interpellation wird in der Kammer debattiert, wobei der Minister verpflichtet ist, Rede und Antwort zu stehen.', + fr: "L'interpellation est débattue en séance plénière, où le ministre est tenu de répondre et de rendre des comptes.", + es: 'La interpelación se debate en el pleno, donde el ministro está obligado a responder y rendir cuentas.', + nl: 'De interpellatie wordt besproken in de Kamer, waar de minister verplicht is te antwoorden en verantwoording af te leggen.', + ar: 'تتم مناقشة الاستجواب في الجلسة العامة حيث يتعين على الوزير الرد وتحمل المسؤولية.', + he: 'האינטרפלציה נדונה במליאה, שם השר מחויב להשיב ולתת דין וחשבון.', + ja: '質問主意書は本会議で審議され、大臣は答弁と説明責任を果たす義務があります。', + ko: '대정부질문은 본회의에서 논의되며, 장관은 답변하고 책임을 져야 할 의무가 있습니다.', + zh: '质询在全体会议上进行辩论,部长有义务回应并承担责任。', + }; + return ipFallback[lang as string] + ?? 'The interpellation is debated in the chamber, where the minister is obliged to respond and be held accountable.'; } // Generic significance when no domain detected and no known committee diff --git a/tests/data-transformers.test.ts b/tests/data-transformers.test.ts index 21c393bd431..71d5ae82788 100644 --- a/tests/data-transformers.test.ts +++ b/tests/data-transformers.test.ts @@ -1578,6 +1578,24 @@ describe('Data Transformers', () => { expect(content).toMatch(/minister is obliged to respond|held accountable/i); expect(content).not.toContain('Requires committee review'); }); + + it('should localize interpellation generic fallback for non-en/sv languages', () => { + const deDe = generateArticleContent({ + interpellations: [{ titel: 'Allgemeine Frage', url: 'https://example.com/1', dok_id: 'IP4' }] + } as MockArticlePayload, 'interpellations', 'de') as string; + + // German interpellation fallback references minister accountability + expect(deDe).toMatch(/Minister verpflichtet|Rede und Antwort/i); + expect(deDe).not.toContain('Requires committee review'); + + const frFr = generateArticleContent({ + interpellations: [{ titel: 'Question générale', url: 'https://example.com/1', dok_id: 'IP5' }] + } as MockArticlePayload, 'interpellations', 'fr') as string; + + // French interpellation fallback references minister accountability + expect(frFr).toMatch(/ministre est tenu|rendre des comptes/i); + expect(frFr).not.toContain('Requires committee review'); + }); }); describe('Thematic grouping in motions content', () => { From cf0efda790b4c384a5596b66206603a58aadb00b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 21:02:31 +0000 Subject: [PATCH 20/24] style: rename test variables deDe/frFr to deContent/frContent per code review Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- tests/data-transformers.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/data-transformers.test.ts b/tests/data-transformers.test.ts index 71d5ae82788..2ec1aeacccd 100644 --- a/tests/data-transformers.test.ts +++ b/tests/data-transformers.test.ts @@ -1580,21 +1580,21 @@ describe('Data Transformers', () => { }); it('should localize interpellation generic fallback for non-en/sv languages', () => { - const deDe = generateArticleContent({ + const deContent = generateArticleContent({ interpellations: [{ titel: 'Allgemeine Frage', url: 'https://example.com/1', dok_id: 'IP4' }] } as MockArticlePayload, 'interpellations', 'de') as string; // German interpellation fallback references minister accountability - expect(deDe).toMatch(/Minister verpflichtet|Rede und Antwort/i); - expect(deDe).not.toContain('Requires committee review'); + expect(deContent).toMatch(/Minister verpflichtet|Rede und Antwort/i); + expect(deContent).not.toContain('Requires committee review'); - const frFr = generateArticleContent({ + const frContent = generateArticleContent({ interpellations: [{ titel: 'Question générale', url: 'https://example.com/1', dok_id: 'IP5' }] } as MockArticlePayload, 'interpellations', 'fr') as string; // French interpellation fallback references minister accountability - expect(frFr).toMatch(/ministre est tenu|rendre des comptes/i); - expect(frFr).not.toContain('Requires committee review'); + expect(frContent).toMatch(/ministre est tenu|rendre des comptes/i); + expect(frContent).not.toContain('Requires committee review'); }); }); From 08ee3c10bb2394d3d2ea2c4efee4ab2b65668c15 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 21:56:06 +0000 Subject: [PATCH 21/24] fix: correct da/no/fi ipFallback to reference Swedish Riksdag, not other national parliaments Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- scripts/data-transformers/policy-analysis.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/data-transformers/policy-analysis.ts b/scripts/data-transformers/policy-analysis.ts index 7336913d15c..20bbeeeb18d 100644 --- a/scripts/data-transformers/policy-analysis.ts +++ b/scripts/data-transformers/policy-analysis.ts @@ -598,9 +598,9 @@ export function generatePolicySignificance(doc: RawDocument, lang: Language | st if (doktyp2 === 'ip') { const ipFallback: Record = { sv: 'Interpellationen debatteras i kammaren där ministern är skyldig att svara och förklara sina beslut.', - da: 'Interpellationen debatteres i Folketinget, hvor ministeren er forpligtet til at svare og stå til ansvar.', - no: 'Interpellasjonen debatteres i Stortinget, der ministeren er forpliktet til å svare og stå til ansvar.', - fi: 'Välikysymys käsitellään täysistunnossa, jossa ministerin on vastattava ja oltava tilivelvollinen.', + da: 'Interpellationen debatteres i Riksdagens kammare, hvor ministeren er forpligtet til at svare og stå til ansvar.', + no: 'Interpellasjonen debatteres i Riksdagens kammare, der ministeren er forpliktet til å svare og stå til ansvar.', + fi: 'Välikysymys käsitellään Riksdagenin täysistunnossa, jossa ministerin on vastattava ja oltava tilivelvollinen.', de: 'Die Interpellation wird in der Kammer debattiert, wobei der Minister verpflichtet ist, Rede und Antwort zu stehen.', fr: "L'interpellation est débattue en séance plénière, où le ministre est tenu de répondre et de rendre des comptes.", es: 'La interpelación se debate en el pleno, donde el ministro está obligado a responder y rendir cuentas.', From 2f59a740e776047e7ad6d6c81f48201a2ac56252 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 22:52:08 +0000 Subject: [PATCH 22/24] =?UTF-8?q?fix:=20remove=20duplicate=20interpellatio?= =?UTF-8?q?ns=20JSDoc=20bullet=20and=20fix=20Finnish=20ipFallback=20inflec?= =?UTF-8?q?tion=20(Riksdagenin=20=E2=86=92=20Riksdagin)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- scripts/data-transformers/content-generators/index.ts | 1 - scripts/data-transformers/policy-analysis.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/data-transformers/content-generators/index.ts b/scripts/data-transformers/content-generators/index.ts index 810608b053a..36223d1c57f 100644 --- a/scripts/data-transformers/content-generators/index.ts +++ b/scripts/data-transformers/content-generators/index.ts @@ -7,7 +7,6 @@ * - **committee** — generateCommitteeContent * - **propositions** — generatePropositionsContent * - **motions** — generateMotionsContent - * - **interpellations** — generateInterpellationsContent * - **interpellations** — generateInterpellationsContent (dedicated, separate from motions) * - **generic** — generateGenericContent * - **monthly-review** — generateMonthlyReviewContent diff --git a/scripts/data-transformers/policy-analysis.ts b/scripts/data-transformers/policy-analysis.ts index 20bbeeeb18d..3cfa0045690 100644 --- a/scripts/data-transformers/policy-analysis.ts +++ b/scripts/data-transformers/policy-analysis.ts @@ -600,7 +600,7 @@ export function generatePolicySignificance(doc: RawDocument, lang: Language | st sv: 'Interpellationen debatteras i kammaren där ministern är skyldig att svara och förklara sina beslut.', da: 'Interpellationen debatteres i Riksdagens kammare, hvor ministeren er forpligtet til at svare og stå til ansvar.', no: 'Interpellasjonen debatteres i Riksdagens kammare, der ministeren er forpliktet til å svare og stå til ansvar.', - fi: 'Välikysymys käsitellään Riksdagenin täysistunnossa, jossa ministerin on vastattava ja oltava tilivelvollinen.', + fi: 'Välikysymys käsitellään Riksdagin täysistunnossa, jossa ministerin on vastattava ja oltava tilivelvollinen.', de: 'Die Interpellation wird in der Kammer debattiert, wobei der Minister verpflichtet ist, Rede und Antwort zu stehen.', fr: "L'interpellation est débattue en séance plénière, où le ministre est tenu de répondre et de rendre des comptes.", es: 'La interpelación se debate en el pleno, donde el ministro está obligado a responder y rendir cuentas.', From ad4ab833f374027e5e6f592ac49e2b011a8fd4ac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 23:46:35 +0000 Subject: [PATCH 23/24] refactor: hoist IP_FALLBACK to module-level, update JSDoc, consolidate tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Hoist ipFallback → module-level IP_FALLBACK const (Readonly) to avoid per-call allocation in generatePolicySignificance(). 2. Update JSDoc to document 'ip' in impliedDoktyp param. 3. Replace hard-coded UI text in tests with CONTENT_LABELS references. 4. Delete redundant interpellations.test.ts; merge unique tests into interpellations-content-generator.test.ts (64 tests, no overlap). Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- scripts/data-transformers/policy-analysis.ts | 40 +-- .../interpellations-content-generator.test.ts | 49 ++++ tests/interpellations.test.ts | 228 ------------------ 3 files changed, 72 insertions(+), 245 deletions(-) delete mode 100644 tests/interpellations.test.ts diff --git a/scripts/data-transformers/policy-analysis.ts b/scripts/data-transformers/policy-analysis.ts index 3cfa0045690..738f180c651 100644 --- a/scripts/data-transformers/policy-analysis.ts +++ b/scripts/data-transformers/policy-analysis.ts @@ -538,6 +538,27 @@ export function getDomainSpecificAnalysis(primaryDomain: string, doktyp: string, return entry.en['default'] ?? ''; } +/** + * Localised interpellation fallback text: references minister obligation + * to respond, not committee review. Hoisted to module-level to avoid + * per-call allocation inside generatePolicySignificance(). + */ +const IP_FALLBACK: Readonly> = { + sv: 'Interpellationen debatteras i kammaren där ministern är skyldig att svara och förklara sina beslut.', + da: 'Interpellationen debatteres i Riksdagens kammare, hvor ministeren er forpligtet til at svare og stå til ansvar.', + no: 'Interpellasjonen debatteres i Riksdagens kammare, der ministeren er forpliktet til å svare og stå til ansvar.', + fi: 'Välikysymys käsitellään Riksdagin täysistunnossa, jossa ministerin on vastattava ja oltava tilivelvollinen.', + de: 'Die Interpellation wird in der Kammer debattiert, wobei der Minister verpflichtet ist, Rede und Antwort zu stehen.', + fr: "L'interpellation est débattue en séance plénière, où le ministre est tenu de répondre et de rendre des comptes.", + es: 'La interpelación se debate en el pleno, donde el ministro está obligado a responder y rendir cuentas.', + nl: 'De interpellatie wordt besproken in de Kamer, waar de minister verplicht is te antwoorden en verantwoording af te leggen.', + ar: 'تتم مناقشة الاستجواب في الجلسة العامة حيث يتعين على الوزير الرد وتحمل المسؤولية.', + he: 'האינטרפלציה נדונה במליאה, שם השר מחויב להשיב ולתת דין וחשבון.', + ja: '質問主意書は本会議で審議され、大臣は答弁と説明責任を果たす義務があります。', + ko: '대정부질문은 본회의에서 논의되며, 장관은 답변하고 책임을 져야 할 의무가 있습니다.', + zh: '质询在全体会议上进行辩论,部长有义务回应并承担责任。', +}; + /** * Generate policy significance context for a document based on its metadata. * Uses the localised policySignificanceTouches label plus a domain-specific @@ -546,7 +567,7 @@ export function getDomainSpecificAnalysis(primaryDomain: string, doktyp: string, * when no domain keyword matches but the document's organ field identifies a * known Riksdag committee. * @param impliedDoktyp - document type inferred from the calling context - * ('mot', 'bet', 'prop') when doc.doktyp / doc.documentType is absent. + * ('mot', 'bet', 'prop', 'ip') when doc.doktyp / doc.documentType is absent. */ export function generatePolicySignificance(doc: RawDocument, lang: Language | string, impliedDoktyp?: string): string { const domains = detectPolicyDomains(doc, lang); @@ -596,22 +617,7 @@ export function generatePolicySignificance(doc: RawDocument, lang: Language | st // Interpellation-specific fallback: references minister obligation, not committee review const doktyp2 = doc.doktyp || doc.documentType || impliedDoktyp || ''; if (doktyp2 === 'ip') { - const ipFallback: Record = { - sv: 'Interpellationen debatteras i kammaren där ministern är skyldig att svara och förklara sina beslut.', - da: 'Interpellationen debatteres i Riksdagens kammare, hvor ministeren er forpligtet til at svare og stå til ansvar.', - no: 'Interpellasjonen debatteres i Riksdagens kammare, der ministeren er forpliktet til å svare og stå til ansvar.', - fi: 'Välikysymys käsitellään Riksdagin täysistunnossa, jossa ministerin on vastattava ja oltava tilivelvollinen.', - de: 'Die Interpellation wird in der Kammer debattiert, wobei der Minister verpflichtet ist, Rede und Antwort zu stehen.', - fr: "L'interpellation est débattue en séance plénière, où le ministre est tenu de répondre et de rendre des comptes.", - es: 'La interpelación se debate en el pleno, donde el ministro está obligado a responder y rendir cuentas.', - nl: 'De interpellatie wordt besproken in de Kamer, waar de minister verplicht is te antwoorden en verantwoording af te leggen.', - ar: 'تتم مناقشة الاستجواب في الجلسة العامة حيث يتعين على الوزير الرد وتحمل المسؤولية.', - he: 'האינטרפלציה נדונה במליאה, שם השר מחויב להשיב ולתת דין וחשבון.', - ja: '質問主意書は本会議で審議され、大臣は答弁と説明責任を果たす義務があります。', - ko: '대정부질문은 본회의에서 논의되며, 장관은 답변하고 책임을 져야 할 의무가 있습니다.', - zh: '质询在全体会议上进行辩论,部长有义务回应并承担责任。', - }; - return ipFallback[lang as string] + return IP_FALLBACK[lang as string] ?? 'The interpellation is debated in the chamber, where the minister is obliged to respond and be held accountable.'; } diff --git a/tests/interpellations-content-generator.test.ts b/tests/interpellations-content-generator.test.ts index 6f01b8bfd2e..09422a42387 100644 --- a/tests/interpellations-content-generator.test.ts +++ b/tests/interpellations-content-generator.test.ts @@ -309,6 +309,55 @@ describe('Interpellations Content Generator', () => { } }); }); + + describe('CSS class markers', () => { + it('should use interpellation-entry CSS class, not motion-entry', () => { + const content = generateInterpellationsContent({ interpellations: sampleInterpellations }, 'en'); + expect(content).toContain('interpellation-entry'); + expect(content).not.toContain('motion-entry'); + }); + }); + + describe('Analytical lede', () => { + it('should include article-lede class with document count', () => { + const content = generateArticleContent({ interpellations: sampleInterpellations }, 'interpellations', 'en') as string; + expect(content).toContain('article-lede'); + expect(content).toContain('3'); + }); + }); + + describe('Policy analysis ip fallback', () => { + it('should render ip-specific policy text, not committee review', () => { + const content = generateArticleContent({ + interpellations: [{ titel: 'Interpellation om vård', parti: 'V', url: '#', dokumentnamn: 'IP1' }], + }, 'interpellations', 'en') as string; + expect(content).not.toContain('Requires committee review'); + expect(content).toContain(CONTENT_LABELS.en.whyItMatters); + }); + }); + + describe('Label function smoke tests', () => { + it('should produce interpellationStrategyContext with numeric argument', () => { + const fn = CONTENT_LABELS.en.interpellationStrategyContext; + expect(typeof fn).toBe('function'); + const result = fn(4); + expect(result).toContain('4'); + }); + + it('should have interpellationDefault for all 14 languages', () => { + for (const lang of LANGUAGES) { + const labels = CONTENT_LABELS[lang as Language]; + expect(labels?.interpellationDefault, `interpellationDefault missing for ${lang}`).toBeTruthy(); + } + }); + + it('should have readFullInterpellation for all 14 languages', () => { + for (const lang of LANGUAGES) { + const labels = CONTENT_LABELS[lang as Language]; + expect(labels?.readFullInterpellation, `readFullInterpellation missing for ${lang}`).toBeTruthy(); + } + }); + }); }); describe('Shared Prompts Library', () => { diff --git a/tests/interpellations.test.ts b/tests/interpellations.test.ts deleted file mode 100644 index b8afd1d7920..00000000000 --- a/tests/interpellations.test.ts +++ /dev/null @@ -1,228 +0,0 @@ -/** - * Unit Tests for Interpellations Content Generator - * Validates: - * - Output does not contain motion/proposition-specific phrasing - * - Interpellations label keys exist for all 14 languages - * - Party rendering and coalition dynamics work correctly - * - Empty state renders appropriately - */ - -import { describe, it, expect, afterEach, vi } from 'vitest'; -import { - generateArticleContent, - CONTENT_LABELS, -} from '../scripts/data-transformers.js'; -import type { Language } from '../scripts/types/language.js'; - -const ALL_LANGUAGES: Language[] = ['en', 'sv', 'da', 'no', 'fi', 'de', 'fr', 'es', 'nl', 'ar', 'he', 'ja', 'ko', 'zh']; - -/** Mock interpellation document */ -interface MockInterpellation { - titel?: string; - title?: string; - url?: string; - parti?: string; - dokumentnamn?: string; - dok_id?: string; - intressent_namn?: string; - author?: string; - summary?: string; - datum?: string; - mottagare?: string; -} - -describe('Interpellations Content Generator', () => { - afterEach(() => { - vi.clearAllMocks(); - }); - - describe('Content labels', () => { - const INTERPELLATION_KEYS = [ - 'noInterpellations', - 'interpellationDefault', - 'interpellationsBreakdown', - 'readFullInterpellation', - 'partyInterpellationsFiled', - 'interpellationStrategyContext', - ] as const; - - ALL_LANGUAGES.forEach((lang) => { - INTERPELLATION_KEYS.forEach((key) => { - it(`should have "${key}" label for ${lang}`, () => { - const labels = CONTENT_LABELS[lang]; - expect(labels).toBeDefined(); - expect((labels as unknown as Record)[key]).toBeDefined(); - }); - }); - }); - - it('should have function-valued interpellationsBreakdown for en', () => { - const fn = CONTENT_LABELS.en.interpellationsBreakdown; - expect(typeof fn).toBe('function'); - expect(fn(3)).toContain('3'); - }); - - it('should have function-valued interpellationStrategyContext for en', () => { - const fn = CONTENT_LABELS.en.interpellationStrategyContext; - expect(typeof fn).toBe('function'); - const result = fn(4); - expect(result).toContain('4'); - }); - - it('should have function-valued partyInterpellationsFiled for en', () => { - const fn = CONTENT_LABELS.en.partyInterpellationsFiled; - expect(typeof fn).toBe('function'); - const result = fn('S', 2); - expect(result).toContain('S'); - expect(result).toContain('2'); - expect(result).toContain('interpellation'); - expect(result).not.toContain('motion'); - }); - }); - - describe('Output content', () => { - it('should generate interpellations content with interpellation-specific heading', () => { - const data = { - interpellations: [ - { - titel: 'Om sjukvårdsförsörjning till statsrådet', - parti: 'V', - url: 'https://riksdagen.se/ip1', - dokumentnamn: 'IP 2025/26:1', - intressent_namn: 'Test Person', - mottagare: 'Socialministern', - }, - { - titel: 'Om klimatpolitik till statsrådet', - parti: 'S', - url: 'https://riksdagen.se/ip2', - dokumentnamn: 'IP 2025/26:2', - intressent_namn: 'Other Person', - mottagare: 'Miljöministern', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - - // Should contain interpellations-specific heading element - expect(result).toMatch(/]*>.*Interpellation.*<\/h2>/); - // Should have interpellation-entry CSS class - expect(result).toContain('interpellation-entry'); - - // Should NOT contain motion-specific phrasing - expect(result).not.toContain('Opposition Motions'); - expect(result).not.toContain('Read the full motion'); - expect(result).not.toContain('motion-entry'); - }); - - it('should use interpellations-specific party count in coalition dynamics', () => { - const data = { - interpellations: [ - { titel: 'IP 1', parti: 'V', url: '#', dokumentnamn: 'IP1' }, - { titel: 'IP 2', parti: 'S', url: '#', dokumentnamn: 'IP2' }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - // Coalition dynamics should say "interpellations filed" not "motions filed" - expect(result).toContain('interpellation'); - expect(result).not.toMatch(/\d+ motion[s]? filed/); - }); - - it('should render parliamentary oversight strategy for multi-party interpellations', () => { - const data = { - interpellations: [ - { titel: 'IP 1', parti: 'V', url: '#', dokumentnamn: 'IP1' }, - { titel: 'IP 2', parti: 'S', url: '#', dokumentnamn: 'IP2' }, - { titel: 'IP 3', parti: 'M', url: '#', dokumentnamn: 'IP3' }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - // Should contain multi-party strategy context heading - expect(result).toContain('Opposition Strategy'); - // Should contain interpellation-specific strategy context (not motions phrasing) - expect(result).toContain('parliamentary oversight'); - }); - }); - - describe('Empty state', () => { - it('should show no-interpellations message when empty', () => { - const data = { interpellations: [] as MockInterpellation[] }; - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('No interpellations'); - }); - - it('should show Swedish no-interpellations message', () => { - const data = { interpellations: [] as MockInterpellation[] }; - const result = generateArticleContent(data, 'interpellations', 'sv') as string; - expect(result).toContain('Inga interpellationer'); - }); - }); - - describe('Data contract', () => { - it('should use data.interpellations when provided', () => { - const data = { - interpellations: [ - { - titel: 'Interpellation from interpellations field', - url: '#', - dokumentnamn: 'IP-DOC1', - parti: 'M', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('Interpellation from interpellations field'); - expect(result).not.toContain('No interpellations'); - }); - - it('should show empty state when interpellations array is empty', () => { - const data = { - interpellations: [] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('No interpellations'); - }); - }); - - describe('Analytical lede', () => { - it('should include analytical lede with document count', () => { - const data = { - interpellations: [ - { titel: 'IP 1', parti: 'V', url: '#', dokumentnamn: 'IP1' }, - { titel: 'IP 2', parti: 'S', url: '#', dokumentnamn: 'IP2' }, - { titel: 'IP 3', parti: 'M', url: '#', dokumentnamn: 'IP3' }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - expect(result).toContain('article-lede'); - expect(result).toContain('3'); - }); - }); - - describe('Policy analysis', () => { - it('should render ip-specific policy analysis text (not committee review)', () => { - const data = { - interpellations: [ - { - titel: 'Interpellation om vård', - parti: 'V', - url: '#', - dokumentnamn: 'IP1', - }, - ] as MockInterpellation[], - }; - - const result = generateArticleContent(data, 'interpellations', 'en') as string; - // The ip-specific policy analysis should reference accountability, not committee review - expect(result).not.toContain('Requires committee review'); - // Should contain accountability-focused analysis (from ip doktyp in policy-analysis.ts) - expect(result).toContain('Why It Matters'); - }); - }); -}); From 5aa4b33182c863336a8ccbe1baa5a42359ca452f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 00:27:15 +0000 Subject: [PATCH 24/24] fix: replace weak toContain('3') assertion with specific '3 interpellations' match Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- tests/interpellations-content-generator.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/interpellations-content-generator.test.ts b/tests/interpellations-content-generator.test.ts index 09422a42387..0f71b675eaf 100644 --- a/tests/interpellations-content-generator.test.ts +++ b/tests/interpellations-content-generator.test.ts @@ -322,7 +322,7 @@ describe('Interpellations Content Generator', () => { it('should include article-lede class with document count', () => { const content = generateArticleContent({ interpellations: sampleInterpellations }, 'interpellations', 'en') as string; expect(content).toContain('article-lede'); - expect(content).toContain('3'); + expect(content).toContain('3 interpellations'); }); });