import Foundation /// One topic section: a contiguous run of transcript entries `[startIndex...endIndex]` /// (inclusive, indices into the canonical entries array) with an LLM title + summary. struct TopicSection: Equatable { var title: String var summary: String var startIndex: Int var endIndex: Int } /// Structured "meeting extras" extracted from the named transcript. Mirrors /// recap-relay's schema; speakers are real names (we already have them from /// label-merge), not anonymous cluster ids. struct MeetingExtras: Equatable { struct TLDR: Equatable { var summary: String; var primarySpeakers: [String] } struct Decision: Equatable { var statement: String; var agreedBy: [String]; var supportingOffset: Int? } struct ActionItem: Equatable { var description: String; var owner: String?; var dueHint: String?; var supportingOffset: Int? } struct OpenQuestion: Equatable { var question: String; var raisedBy: String? } struct KeyQuote: Equatable { var speaker: String?; var offset: Int?; var quote: String; var whyNotable: String } var tldr: TLDR var decisions: [Decision] var actionItems: [ActionItem] var openQuestions: [OpenQuestion] var keyQuotes: [KeyQuote] var isEmptyBeyondTLDR: Bool { decisions.isEmpty && actionItems.isEmpty && openQuestions.isEmpty && keyQuotes.isEmpty } } /// The assembled recap for one session: the topic sections + (optional) extras, /// over the session's transcript. Rendered to `transcript.md` / `recap.html`. struct RecapResult: Equatable { var sections: [TopicSection] var extras: MeetingExtras? }