/** * Research Engine — canonical V2 mixed-methods store. * * Canonical persistence: * research/store.v2.json * * Legacy migration: * research/insights.json -> research/store.v2.json */ import type { MemoireEvent } from "../engine/core.js"; import type { StickyNote } from "../figma/bridge.js"; import { type ParsedResearch } from "../figma/stickies.js"; import type { TranscriptAnalysis } from "./transcript-parser.js"; import type { WebResearchResult } from "./web-researcher.js"; export interface ResearchConfig { outputDir: string; onEvent?: (event: MemoireEvent) => void; } export type ResearchConfidence = "high" | "medium" | "low"; export type ResearchSentiment = "positive" | "negative" | "neutral" | "mixed"; export type ResearchMethod = "qualitative" | "quantitative" | "mixed" | "netnography" | "desk"; export type ResearchObservationKind = "survey-response" | "transcript-segment" | "sticky" | "web-finding" | "netnography-observation"; export type ResearchSourceKind = "qualitative" | "quantitative" | "mixed" | "netnography" | "desk"; export interface ResearchObservation { id: string; sourceId: string; kind: ResearchObservationKind; text: string; actor?: string; cohort?: string; timestamp?: string; numericFields?: Record; tags: string[]; entities: string[]; sentiment: ResearchSentiment; createdAt: string; } export interface ResearchFinding { id: string; statement: string; category: string; confidence: ResearchConfidence; themeIds: string[]; evidenceObservationIds: string[]; evidenceSourceIds: string[]; sourceTypeCount: number; method: ResearchMethod; caveats: string[]; tags: string[]; entities: string[]; sentiment?: ResearchSentiment; signalTags: string[]; createdAt: string; source?: string; evidence?: string[]; } export interface ResearchTheme { id: string; name: string; description: string; findingIds: string[]; frequency: number; sourceCount: number; sourceTypeCount: number; confidence: ResearchConfidence; signalTags: string[]; positiveCount: number; negativeCount: number; } export interface ResearchHighlight { id: string; sourceId: string; observationId?: string; text: string; note?: string; tags: string[]; codeIds: string[]; sentiment: ResearchSentiment; createdAt: string; } export interface ResearchCodebookEntry { id: string; label: string; description: string; color?: string; parentId?: string; highlightIds: string[]; createdAt: string; } export interface ResearchEvidenceLink { id: string; sourceId: string; findingId?: string; highlightId?: string; label: string; href?: string; sourcePath?: string; createdAt: string; } export interface ResearchReportArtifact { id: string; title: string; kind: "opportunity-map" | "theme-matrix" | "evidence-table" | "quote-reel" | "journey-map" | "recommendations"; summary: string; artifactPath?: string; evidenceFindingIds: string[]; createdAt: string; } export interface ResearchSourceRecord { id: string; name: string; type: string; processedAt: string; itemCount?: number; qualityScore?: number; sampleSize?: number; missingRate?: number; sourceKind?: ResearchSourceKind; notes?: string[]; } export interface ResearchInterval { low: number; high: number; } export interface ResearchBucket { label: string; count: number; percentage: number; } export interface ResearchCohortComparison { cohort: string; sampleSize: number; mean: number; median: number; deltaFromOverall: number; } export interface ResearchQuantitativeMetric { id: string; source: string; field: string; label: string; sampleSize: number; missingCount: number; missingRate: number; min: number; max: number; mean: number; median: number; stdDev: number; p25: number; p75: number; confidenceInterval95?: ResearchInterval; scaleType: "nps-0-10" | "likert-1-5" | "likert-1-7" | "scale-0-10" | "continuous"; buckets: ResearchBucket[]; nps?: { promoterPct: number; passivePct: number; detractorPct: number; score: number; }; outlierCount: number; cohortComparisons: ResearchCohortComparison[]; } export interface ResearchDataQualitySnapshot { overallScore: number; sampleSize: number; completenessScore: number; sourceDiversityScore: number; triangulationScore: number; structureScore: number; notes: string[]; generatedAt: string; } export interface ResearchPersona { name: string; role: string; goals: string[]; painPoints: string[]; behaviors: string[]; source: string; quote?: string; confidence?: ResearchConfidence; evidenceFindingIds?: string[]; } export interface ResearchOpportunity { title: string; summary: string; theme: string; priority: "high" | "medium" | "low"; confidence: ResearchConfidence; evidenceFindingIds: string[]; sourceCount: number; } export interface ResearchRisk { title: string; summary: string; theme: string; severity: "high" | "medium" | "low"; evidenceFindingIds: string[]; sourceCount: number; } export interface ResearchContradiction { topic: string; positiveFindingIds: string[]; negativeFindingIds: string[]; summary: string; } export interface ResearchMethods { analysisMode: "decision-grade"; quantitativeApproach: string; qualitativeApproach: string; limitations: string[]; } export interface ResearchSummarySnapshot { narrative: string; topThemes: string[]; topOpportunities: string[]; topRisks: string[]; contradictionCount: number; nextActions: string[]; generatedAt: string; qualityScore: number; sampleSize: number; quantitativeMetrics: number; coverage: { observations: number; findings: number; highConfidence: number; personas: number; themes: number; sources: number; quantitativeMetrics: number; }; } export interface ResearchStore { version: 2; sources: ResearchSourceRecord[]; observations: ResearchObservation[]; highlights: ResearchHighlight[]; codebook: ResearchCodebookEntry[]; findings: ResearchFinding[]; themes: ResearchTheme[]; evidenceLinks: ResearchEvidenceLink[]; personas: ResearchPersona[]; quantitativeMetrics: ResearchQuantitativeMetric[]; opportunities: ResearchOpportunity[]; risks: ResearchRisk[]; contradictions: ResearchContradiction[]; reports: ResearchReportArtifact[]; quality: ResearchDataQualitySnapshot; summary?: ResearchSummarySnapshot; methods: ResearchMethods; } export declare class ResearchEngine { private log; private config; private store; private observationCounter; private findingCounter; constructor(config: ResearchConfig); load(): Promise; fromStickies(stickies: StickyNote[]): Promise; fromFile(filePath: string): Promise; fromTranscript(filePath: string, label?: string): Promise; fromUrls(topic: string, urls: string[]): Promise; synthesize(): Promise<{ themes: ResearchTheme[]; summary: string; }>; generateReport(): Promise; assessQuality(): ResearchDataQualitySnapshot; getFindings(): ResearchFinding[]; getInsights(): ResearchFinding[]; getStore(): ResearchStore; private refreshComputedState; private syncCounters; private nextObservationId; private nextFindingId; private addObservation; private addFinding; private upsertSource; private updateSource; private upsertQuantitativeMetrics; /** * Archive the current store before a destructive re-ingest purges prior * observations/findings for a source. Research data is expensive to * collect — a re-import must never be able to silently destroy it. * Snapshots land in research/snapshots/ with a retention cap. */ private snapshotBeforePurge; private purgeSourceData; private invalidateDerivedArtifacts; private save; private writeMarkdownNotes; private emitEvent; }