package store import "database/sql" // voice.go: durable storage for the two D21 bank record types added by pack-19 (schema v13) — the // per-character VOICE profile and the ordered ADDRESS pair. Dumb storage like glossary.go: this layer // replaces and reads back, the pipeline owns validation, projection and matching. // // Both are BANK CONTENT: they are replaced in the same transaction as the glossary (ReplaceBank) and // fold into the same memory_version, so a signature on either is a bank-only snapshot move and stays // re-pinnable per unit (pipeline/repin.go) instead of re-buying an edit wave. // VoiceProfile is one character's voice record for a chapter window (research/15 §1.5). The character is // identified by the STABLE (Src, Sense) key of its glossary term — never by glossary.id, which is a fresh // autoincrement on every replace. The list-valued columns are stored as JSON; membank owns the encoding. type VoiceProfile struct { Src string Sense string Register string SelfRef string AddressDefault string // informal|formal|"" — the T/V default outside the pair registry LexiconMarkers string // JSON []string NGLexicon string // JSON []string Exemplars string // JSON []string Brightness string SinceCh int UntilCh int } // AddressPair is one ORDERED (speaker → addressee) register record for a chapter window — one entry of // the ты/вы transition journal (D7-Amendment-1). Register is ABSTRACT (informal|formal); which target // surfaces realise it is target data, so a target without a T/V distinction simply matches nothing. type AddressPair struct { SpeakerSrc string SpeakerSense string AddresseeSrc string AddresseeSense string Register string Form string Closeness string SinceCh int UntilCh int } // VoiceProfilesForBook returns a book's voice profiles in a DETERMINISTIC order — the stable // materialization the bank version hashes over. func (s *Store) VoiceProfilesForBook(bookID string) ([]VoiceProfile, error) { return queryAll(s.r, ` SELECT src, sense, register, self_ref, address_default, lexicon_markers, ng_lexicon, exemplars, brightness, since_ch, until_ch FROM voice_profiles WHERE book_id = ? ORDER BY src, sense, since_ch, until_ch`, func(rows *sql.Rows) (VoiceProfile, error) { var v VoiceProfile err := rows.Scan(&v.Src, &v.Sense, &v.Register, &v.SelfRef, &v.AddressDefault, &v.LexiconMarkers, &v.NGLexicon, &v.Exemplars, &v.Brightness, &v.SinceCh, &v.UntilCh) return v, err }, bookID) } // AddressPairsForBook returns a book's address-register journal in a DETERMINISTIC order. func (s *Store) AddressPairsForBook(bookID string) ([]AddressPair, error) { return queryAll(s.r, ` SELECT speaker_src, speaker_sense, addressee_src, addressee_sense, register, form, closeness, since_ch, until_ch FROM address_pairs WHERE book_id = ? ORDER BY speaker_src, speaker_sense, addressee_src, addressee_sense, since_ch, until_ch`, func(rows *sql.Rows) (AddressPair, error) { var p AddressPair err := rows.Scan(&p.SpeakerSrc, &p.SpeakerSense, &p.AddresseeSrc, &p.AddresseeSense, &p.Register, &p.Form, &p.Closeness, &p.SinceCh, &p.UntilCh) return p, err }, bookID) }