Decompose AI analysis monoliths into bounded context modules#1339
Decompose AI analysis monoliths into bounded context modules#1339
Conversation
🏷️ Automatic Labeling SummaryThis PR has been automatically labeled based on the files changed and PR metadata. Applied Labels: size-xs Label Categories
For more information, see |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
Extract stakeholder SWOT matrix construction, enrichment, refinement and confidence scoring into scripts/ai-analysis/swot/index.ts as part of the AI analysis pipeline refactoring. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract bounded-context modules from the monolithic ai-analysis pipeline: - domains/index.ts: policy assessment builder, watch points, EU/Nordic comparative analysis, and all related i18n label constants - visualisation/index.ts: mindmap branch builder, dashboard data builder, type palette, section label helpers No logic changes — pure extract-and-re-export refactoring. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract SWOT, domains, and visualisation bounded contexts from the 1850-line monolith into focused single-responsibility modules: - scripts/ai-analysis/helpers.ts: shared document utility functions - scripts/ai-analysis/swot/index.ts: SWOT entry builder, stakeholder construction, refinement, confidence scoring - scripts/ai-analysis/swot/placeholders.ts: 14-language placeholder text - scripts/ai-analysis/domains/index.ts: policy assessment, watch points, EU/Nordic comparative analysis - scripts/ai-analysis/visualisation/index.ts: mindmap and dashboard builders - scripts/ai-analysis/pipeline.ts: thin orchestrator (279 lines, ≤300 target) All 88 existing tests pass with no changes needed. Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/7af9b9fd-c05c-47b7-a898-ce2bfd0240c9
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
|
@copilot failing checks To fix an unused import, the general approach is to remove that specific identifier from the import list while leaving all actually used imports untouched. This eliminates the unused symbol without altering runtime behavior. In this case, in scripts/ai-analysis/domains/index.ts, you should edit the named import from ../../data-transformers/policy-analysis.js and remove getDomainSpecificAnalysis from the destructuring list on line 14. All other imported names (detectPolicyDomains, detectNarrativeFrames, assessConfidenceLevel, DOMAIN_NAME_TO_KEY, DomainKey) should remain as they are. No new methods, imports, or definitions are required; we are only narrowing the existing import list. Suggested changeset 1 (1) const DATA_SOURCE_LABELS: LangRecord = { @pethers Reply... const DATA_SOURCE_ITEMS: Partial<Record<Language, string[]>> = { In general, to fix an unused-constant issue you either (a) remove the constant if it is truly dead code, or (b) integrate it into the code path that needs it (for example, by exporting it or using it where appropriate). Since DATA_SOURCE_LABELS exists alongside DATA_SOURCE_ITEMS, the best non-breaking fix is to export both so that callers of this domains module can present localized data-source labels and their corresponding items. Concretely, in scripts/ai-analysis/domains/index.ts, near the end of the file where other exports are defined (or, if none, by adding new exports), we should add named exports for DATA_SOURCE_LABELS and DATA_SOURCE_ITEMS. This introduces a legitimate use of DATA_SOURCE_ITEMS (it becomes part of the module’s public API) without altering existing runtime behavior. No new imports or helper methods are needed; we only add an export statement that re-exports the already-declared constants. Suggested changeset 1 (1) const DASHBOARD_TITLE: LangRecord = { In general, unused variable warnings should be fixed either by removing the unused symbol if it is truly dead code, or by wiring it into the code path where it was intended to be used (often by exporting it or referencing it where needed). Here, DASHBOARD_TITLE is a localized label object alongside other dashboard-related constants, and neighbouring constants such as DASHBOARD_DOCS_ANALYSED are exported. The least disruptive fix that preserves intended functionality is to make DASHBOARD_TITLE available to consumers by exporting it, rather than deleting it. Concretely, in scripts/ai-analysis/domains/index.ts, around line 259, change the declaration const DASHBOARD_TITLE: LangRecord = { ... } to export const DASHBOARD_TITLE: LangRecord = { ... }. No additional imports or helper methods are required, since LangRecord is already imported from ../helpers.js, and we are not altering the shape or contents of the constant, just its visibility. This eliminates the “unused variable” warning because the constant becomes a named export that can be consumed elsewhere in the codebase. Suggested changeset 1 (1) |
Extract all Lang14 constants, helper functions (L14, pickLang, interp, plural, betVerbForm, motVerbForm), and the Lang14 type from ai-analysis-pipeline.ts into a dedicated analysis-labels module. The source file is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Decompose the 1542-line ai-analysis-pipeline.ts monolith: - Extract 667 lines of localised Lang14 constants and interpolation helpers to analysis-labels.ts - Extract Pass 2 deep analysis methods as standalone functions - Remove section-comment separators to compact the class - Result: 800 lines (≤800 target met) All 177 related tests pass. Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/7af9b9fd-c05c-47b7-a898-ce2bfd0240c9
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
- ai-swot-builder.test.ts: 25 tests for SWOT builder, enriched entries, placeholders, confidence scoring, refinement, impact mapping, 14-lang - ai-urgency-classifier.test.ts: 11 tests for watch points, policy assessment, DASHBOARD_DOCS_ANALYSED coverage - ai-coalition-detector.test.ts: 3 interface verification tests - ai-visualisation.test.ts: 11 tests for mindmap branches, dashboard data, narrative frames label Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
Remove unused imports and declarations: - domains/index.ts: remove getDomainSpecificAnalysis import, DATA_SOURCE_LABELS, DATA_SOURCE_ITEMS, DASHBOARD_TITLE (all in visualisation/ already) - pipeline.ts: remove unused Language type import - ai-analysis-pipeline.ts: remove unused Lang14 type import - ai-urgency-classifier.test.ts: remove unused FPM test fixture TypeScript compiles cleanly, all 3911 tests pass. Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/7af9b9fd-c05c-47b7-a898-ce2bfd0240c9
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
These unused imports/variables ( |
…posite buildAnalysisDocId fallback - WP_PROPS_DESC: fr nécessite/nécessitent, nl vereist/vereisen - WP_COMMITTEE_DESC: fr façonne/façonnent, es moldea/moldean, nl vormt/vormen - WP_SFS_DESC: fr établit/établissent, es establece/establecen, nl vormt/vormen - WP_EU_DESC: de zeigt/zeigen, fr révèle/révèlent, es revela/revelan, nl onthult/onthullen - WP_IP_DESC: de signalisiert/signalisieren, fr signale/signalent, es señala/señalan, nl signaleert/signaleren - WP_MOTIONS_DESC: fr signale/signalent, es señala/señalan, nl signaleert/signaleren - buildAnalysisDocId() now uses composite type:title:date fallback (aligned with helpers.docId) Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/b0534a11-f3b4-4554-8680-5a82cebca500
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
Applied all 6 review comments in
Also fixed WP_MOTIONS_DESC fr/es/nl for consistency. All 3,911 tests pass, TypeScript strict mode clean, CodeQL clear. |
| const typeKeys = Object.keys(typeCounts); | ||
| if (typeKeys.length > 0) { | ||
| branches.push({ | ||
| label: docTypesLabel(lang), | ||
| color: 'cyan', | ||
| icon: '📄', | ||
| items: typeKeys.map(t => `${localizeDocType(t, lang, typeCounts[t] ?? 0)} (${typeCounts[t] ?? 0})`), | ||
| }); |
There was a problem hiding this comment.
In buildMindmapBranches(), typeKeys comes from Object.keys(typeCounts) without sorting. That makes the rendered document-type list order dependent on input document order (which can vary), leading to non-deterministic UI output/diffs. Consider sorting typeKeys (as buildDashboardData already does) before mapping to items.
| skr: '#4dd0e1', // government written communications | ||
| ds: '#f48fb1', // departmental memoranda | ||
| sou: '#a5d6a7', // government inquiry reports | ||
| dir: '#ce93d8', // commission directives |
There was a problem hiding this comment.
STABLE_TYPE_COLORS only assigns fixed colors for a subset of types (prop/mot/bet/sfs/ip/fpm/skr/ds/sou/dir). Types like pressm, ext, and other will get palette colors based on the current sorted typeKeys index, so their colors can change depending on which other types are present in a given analysis run. If the intent is truly stable per-type coloring, add fixed mappings for the remaining expected doc types or derive palette index from a hash of the type string rather than the per-run index.
| dir: '#ce93d8', // commission directives | |
| dir: '#ce93d8', // commission directives | |
| pressm: '#ff8a65', // pressmeddelanden (press releases) | |
| ext: '#81d4fa', // external documents / references | |
| other: '#cfd8dc', // miscellaneous / uncategorised documents |
| 'Le gouvernement a soumis %n propositions législatives sur %t — engagement politique actif', | ||
| 'El gobierno ha presentado %n propuestas legislativas sobre %t — compromiso político activo', | ||
| 'De regering heeft %n wetsvoorstellen ingediend over %t — actief politiek engagement', |
There was a problem hiding this comment.
TAKEAWAY_PROP hard-codes plural nouns in several languages (e.g. FR "propositions législatives", ES "propuestas legislativas", NL "wetsvoorstellen"). When %n is 1 this yields grammatically incorrect output (e.g. "1 propositions …"). Consider adding singular/plural variants per language or a noun-form helper similar to betVerbForm/motVerbForm.
| 'Le gouvernement a soumis %n propositions législatives sur %t — engagement politique actif', | |
| 'El gobierno ha presentado %n propuestas legislativas sobre %t — compromiso político activo', | |
| 'De regering heeft %n wetsvoorstellen ingediend over %t — actief politiek engagement', | |
| 'Le gouvernement a soumis %n proposition(s) législative(s) sur %t — engagement politique actif', | |
| 'El gobierno ha presentado %n propuesta(s) legislativa(s) sobre %t — compromiso político activo', | |
| 'De regering heeft %n wetsvoorstel(len) ingediend over %t — actief politiek engagement', |
| '%n rapports de commission %verb %t — contrôle parlementaire engagé', | ||
| '%n informes de comité %verb %t — supervisión parlamentaria activada', | ||
| '%n commissierapporten %verb %t — parlementaire controle actief', | ||
| '%n تقارير لجان تفحص %t — الرقابة البرلمانية مفعّلة', | ||
| '%n דוחות ועדה בוחנים %t — פיקוח פרלמנטרי פעיל', |
There was a problem hiding this comment.
TAKEAWAY_BET hard-codes plural nouns for FR/ES/NL ("rapports", "informes", "commissierapporten") while betVerbForm() conjugates the verb for n===1. For n=1 this can produce inconsistent grammar like "1 rapports … examine". Consider singular/plural templates (or noun-form helpers) for these languages.
| '%n rapports de commission %verb %t — contrôle parlementaire engagé', | |
| '%n informes de comité %verb %t — supervisión parlamentaria activada', | |
| '%n commissierapporten %verb %t — parlementaire controle actief', | |
| '%n تقارير لجان تفحص %t — الرقابة البرلمانية مفعّلة', | |
| '%n דוחות ועדה בוחנים %t — פיקוח פרלמנטרי פעיל', | |
| '%n rapport(s) de commission %verb %t — contrôle parlementaire engagé', | |
| '%n informe(s) de comité %verb %t — supervisión parlamentaria activada', | |
| '%n commissierapport(en) %verb %t — parlementaire controle actief', | |
| '%n تقارير لجان تفحص %t — الرقابة البرلمانية مفعّلة', | |
| '%n דוחות ועדה בוחנים %t — פיקוח פרלמטרי פעיל', |
| '%n Oppositionsanträge fechten %t an — parteiübergreifende Debatte aktiv', | ||
| '%n motions d\'opposition %verb %t — débat interpartis actif', | ||
| '%n mociones de oposición %verb %t — debate entre partidos activo', | ||
| '%n oppositiemoties %verb %t — overpartijdebat actief', |
There was a problem hiding this comment.
TAKEAWAY_MOT similarly hard-codes plural nouns for FR/ES/NL ("motions", "mociones", "oppositiemoties") while motVerbForm() conjugates verbs for n===1. For n=1 this yields incorrect phrases like "1 motions … conteste". Consider singular/plural variants or language-specific noun-form helpers.
| '%n oppositiemoties %verb %t — overpartijdebat actief', | |
| '%n oppositiemotie%s %verb %t — overpartijdebat actief', |
|
@copilot apply changes based on the comments in this thread |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
…oun-form helpers for takeaway templates 1. Sort typeKeys in buildMindmapBranches() for deterministic output 2. Add pressm/ext/other to STABLE_TYPE_COLORS for complete coverage 3. Add propNounForm()/betNounForm()/motNounForm() helpers for FR/ES/NL 4. Update TAKEAWAY_PROP/BET/MOT FR/ES/NL templates to use %pnoun/%bnoun/%mnoun 5. Update call site to pass noun forms via interp() Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/0e043233-a52a-4511-af45-393a82551558
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
Applied all 5 review comments in
All 3,911 tests pass, TypeScript strict mode clean, CodeQL clear. |
| 'Actieve wetgevingspijplijn: %n overheidstvoorstellen sturen %t-beleid', | ||
| 'خط أنابيب تشريعي نشط: %n مقترح حكومي يدفع سياسة %t', | ||
| 'צינור חקיקה פעיל: %n הצעות ממשלה מניעות מדיניות %t', | ||
| 'アクティブな立法パイプライン: %n件の政府提案が%t政策を推進', | ||
| '활발한 입법 파이프라인: %n 정부 제안이 %t 정책 추진', | ||
| '积极的立法管道:%n 项政府提案推动%t政策', |
There was a problem hiding this comment.
Dutch localisation typo in GOV_STRENGTH_LABELS.propositions (nl): "overheidstvoorstellen" has an extra "t" and should be "overheidsvoorstellen".
| 'Actieve wetgevingspijplijn: %n overheidstvoorstellen sturen %t-beleid', | |
| 'خط أنابيب تشريعي نشط: %n مقترح حكومي يدفع سياسة %t', | |
| 'צינור חקיקה פעיל: %n הצעות ממשלה מניעות מדיניות %t', | |
| 'アクティブな立法パイプライン: %n件の政府提案が%t政策を推進', | |
| '활발한 입법 파이프라인: %n 정부 제안이 %t 정책 추진', | |
| '积极的立法管道:%n 项政府提案推动%t政策', | |
| 'Actieve wetgevingspijplijn: %n overheidsvoorstellen sturen %t-beleid', | |
| 'خط أنابيب تشريعي نشط: %n مقترح حكومي يدفع سياسة %t', | |
| 'צינור חקיקה פעיל: %n הצעות ממשלה מניעות מדיניות %t', | |
| 'アクティブな立法パイプライン: %n件の政府提案が%t政策を推進', | |
| '활발한 입법 파이프라인: %n 정부 제안이 %t 정책 추진', | |
| '积极的立法管道:%n 项政府提案推动%t政策', |
scripts/ai-analysis/domains/index.ts
Outdated
| fr: (n) => `${n} proposition${n !== 1 ? 's' : ''} ${n === 1 ? 'nécessite' : 'nécessitent'} un examen parlementaire`, | ||
| es: (n) => `${n} proposición${n !== 1 ? 'es' : ''} ${n === 1 ? 'requiere' : 'requieren'} acción parlamentaria`, | ||
| nl: (n) => `${n} voorstel${n !== 1 ? 'len' : ''} ${n === 1 ? 'vereist' : 'vereisen'} parlementaire behandeling`, | ||
| ar: (n) => `${n} مقترح${n !== 1 ? 'ات' : ''} تتطلب إجراء برلمانيا`, |
There was a problem hiding this comment.
Arabic WP_PROPS_DESC uses the plural verb "تتطلب" regardless of count; for n === 1 it should use a singular verb form to avoid ungrammatical output (e.g., conditional wording for singular vs plural).
| ar: (n) => `${n} مقترح${n !== 1 ? 'ات' : ''} تتطلب إجراء برلمانيا`, | |
| ar: (n) => `${n} مقترح${n !== 1 ? 'ات' : ''} ${n === 1 ? 'يتطلب' : 'تتطلب'} إجراء برلمانيا`, |
scripts/ai-analysis/swot/index.ts
Outdated
|
|
||
| /** Derive impact from document type. */ | ||
| export function impactFromDocType(dt: string): 'high' | 'medium' | 'low' { | ||
| if (['prop', 'sfs', 'bet', 'fpm', 'eu'].includes(dt)) return 'high'; |
There was a problem hiding this comment.
impactFromDocType() checks for both 'fpm' and 'eu', but normalizedDocType() maps 'eu' → 'fpm', so the 'eu' branch is unreachable in current call sites (buildEnrichedEntry uses normalizedDocType). Consider removing 'eu' or switching to docType() if you intend to keep a distinct 'eu' type.
| if (['prop', 'sfs', 'bet', 'fpm', 'eu'].includes(dt)) return 'high'; | |
| if (['prop', 'sfs', 'bet', 'fpm'].includes(dt)) return 'high'; |
|
@copilot apply changes based on the comments in this thread |
…S_DESC, remove unreachable 'eu' branch in impactFromDocType 1. Fixed Dutch localisation typo: "overheidstvoorstellen" → "overheidsvoorstellen" 2. Arabic WP_PROPS_DESC now uses singular "يتطلب" for n===1 vs plural "تتطلب" 3. Removed unreachable 'eu' from impactFromDocType() since normalizedDocType() maps 'eu' → 'fpm' Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/9c4ccc29-6c4a-4a80-a4ca-93b8f81682d7
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
Applied all 3 review comments in
All 3,911 tests pass, TypeScript strict mode clean. |
pipeline.ts(1,850 lines) andai-analysis-pipeline.ts(1,542 lines) were merge-conflict hotspots with no domain boundary enforcement. Decomposed both into focused single-responsibility modules.New module structure
Key decisions
helpers←swot←visualisation,helpers←domains←visualisation. Pipeline orchestrator imports all three.aiAnalysisPipelineandrunAnalysisPipelineexports preserved — no downstream changes needed.coalition-detector.tsleft at its current path (already well-extracted, has its own test file and import consumers).Quality improvements from review feedback
requires/require,establishes/establish,signals/signal,reveals/revealerfordert/erfordern,prägt/prägen,bildet/bilden,zeigt/zeigen,signalisiert/signalisierennécessite/nécessitent,façonne/façonnent,établit/établissent,révèle/révèlent,signale/signalentrequiere/requieren,moldea/moldean,establece/establecen,revela/revelan,señala/señalanvereist/vereisen,vormt/vormen,onthult/onthullen,signaleert/signalerenيتطلب/تتطلب(singular/plural verb agreement in WP_PROPS_DESC)%splaceholder withplural(n, lang)interpolation for correct singular/plural nouns (proposal/proposals,report/reports,motion/motions)propNounForm()/betNounForm()/motNounForm()noun-form helpers for French, Spanish, and Dutch, used via%pnoun/%bnoun/%mnounplaceholders to produce correct singular/plural nouns (e.g., FRproposition législative/propositions législatives, ESmoción de oposición/mociones de oposición, NLwetsvoorstel/wetsvoorstellen)STABLE_TYPE_COLORSmaps 13 known doc types (prop, mot, bet, sfs, ip, fpm, skr, ds, sou, dir, pressm, ext, other) to fixed hex colors; keys are sorted before assignment so the same doc type always gets the same color regardless of encounter orderbuildMindmapBranches()now sortstypeKeysbefore rendering, matching the deterministic ordering already used inbuildDashboardData()docId()uses composite${type}:${title}instead of literal'unknown'to avoid ID collisions when primary identifiers are missingbuildAnalysisDocId()uses compositetype:title:datefallback (aligned withhelpers.docId()pattern) instead of truncated titlerefineStakeholderSwotfalls back to the last available IP doc when fewer than 3 interpellations exist, so threat enrichment fires for small IP countsoverheidstvoorstellen→overheidsvoorstellenin GOV_STRENGTH_LABELSimpactFromDocType()no longer checks for'eu'type sincenormalizedDocType()maps'eu'→'fpm', making the'eu'branch unreachableTest coverage
ai-swot-builder.test.ts(25),ai-urgency-classifier.test.ts(11),ai-coalition-detector.test.ts(3),ai-visualisation.test.ts(11)noUnusedLocals,noUnusedParameters) cleanOriginal prompt
This section details on the original issue you should resolve
<issue_title>🏛️ Refactor AI Analysis Bounded Contexts: Decompose 1 700-line Monolith into Domain Modules</issue_title>
<issue_description>## 📋 Issue Type
Feature — Architecture / Bounded Context Refactoring
🎯 Objective
Decompose the monolithic
scripts/ai-analysis/pipeline.ts(1 701 lines) andscripts/generate-news-enhanced/ai-analysis-pipeline.ts(1 541 lines) into focused, single-responsibility modules with clear bounded contexts. This reduces merge-conflict risk (two large files are hotspots), improves test isolation, and enables parallel development of analysis features.📊 Current State
scripts/ai-analysis/pipeline.ts: 1 701 lines covering SWOT generation, watch-point classification, mindmap building, dashboard data, policy assessment, and 14-language localisation in a single filescripts/generate-news-enhanced/ai-analysis-pipeline.ts: 1 541 lines with Pass 1–4 heuristic logic, SWOT defaults, synthesis, and quality scoring mixed togetherai-analysis-pipeline-coverage.test.ts(71 tests) covers the monolith with broad integration tests; unit isolation is poordata-transformers/are imported ad-hoc without contracts🚀 Desired State
Three bounded sub-domains extracted from the monolith, each with its own directory, index, and test file:
🔧 Implementation Approach
Phase 1 — Extract SWOT bounded context
Move all SWOT-building logic from
pipeline.tsintoscripts/ai-analysis/swot/index.ts.buildStakeholderSwot(docs, lang, depth): AnalysisStakeholderSwot[]swot/placeholders.tsrequiresEvidence(depth): booleanandvalidateEvidence(entry): booleanPhase 2 — Extract domain + urgency context
Move
detectPolicyDomains,getDomainSpecificAnalysis,assessConfidenceLevel(currently indata-transformers/policy-analysis.ts) intodomains/index.ts.UrgencyClassifier.classify(doc: RawDocument): WatchPointUrgencyCoalitionDetector.analyze(docs: RawDocument[]): CoalitionSignalPhase 3 — Extract visualisation context
Move mindmap and dashboard builders into
visualisation/index.ts.buildDashboardData(docs, lang): DashboardDatabuildMindmapBranches(analysis, lang): AnalysisMindmapBranch[]Phase 4 — Thin orchestrator
pipeline.tsbecomes a ≤300-line orchestrator that:swot/index.tsfor Iteration 1 + 2domains/index.tsfor policy assessmentvisualisation/index.tsfor dashboard + mindmapvalidateCompleteness(unchanged public interface)Phase 5 — Parallel refactor of
generate-news-enhanced/ai-analysis-pipeline.tsExtract
Pass2DeepAnalyzer,Pass3Synthesizer,Pass4QualityAssureras separate classes with single-method contracts.🤖 Recommended Agent
hack23-code-quality-engineerfor the bounded context extraction and module decomposition.✅ Acceptance Criteria
scripts/ai-analysis/pipeline.tsreduced to ≤300 lines (orchestrator only)swot/index.ts,domains/index.ts,visualisation/index.tscreated with JSDoc contractstests/ai-analysis-pipeline-coverage.test.tstests still passtests/ai-swot-builder.test.ts,tests/ai-urgency-classifier.test.ts,tests/ai-coalition-detector.test.ts,tests/ai-visualisation.test.tseslint-plugin-import/no-cycleor equivalent)generate-news-enhanced/ai-analysis-pipeline.tsreduced to ≤800 lines with Pass classes extracted📚 References
scripts/ai-analysis/pipeline.ts(1 701 lines)scripts/generate-news-enhanced/ai-analysis-pipeline.ts(1 541 lines)📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.