textmachine/docs/architecture/18-bank-ontology.md

100 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 18 — Онтология банка памяти: роли носителей, писатели, свежесть
> **СТАТУС: РАТИФИЦИРОВАНО D39.158 (27.08.2026).** Топикальный вход (D39.126): НЕ источник истины,
> при конфликте побеждает журнал решений. Собрано оркестратором №19 по консилиуму со сторонним
> архитектором при приёмке четырёх паков входной двери шва.
>
> **Зачем страница.** Вокруг одного понятия — «состояние банка этой книги» — живут десять носителей.
> Форма СТРОГАЯ: поток однонаправленный, у каждого источника единственный писатель, вид пересобирается
> полностью на каждой границе прогона (D39.158 п.8).
## Четыре роли, и они не смешиваются
| Роль | Носители | Писатель | Момент фиксации |
|---|---|---|---|
| **ИСТОЧНИКИ** — то, из чего банк собирается | `glossary_seed` · `<book_id>.mined-delta.yaml` · `<book_id>.mined-rejects.yaml` (слово ВЛАДЕЛЬЦА, рядом с `book.yaml`, едут с книгой в бэкапе и экспорте) · `<project_db>.auto-bank.yaml` (слово ДВИЖКА, сидит при базе) · ruby-чтения в хранилище | у каждого РОВНО один. ⚠ Дверь `bank-apply` пишет ТОЛЬКО два файла решений: правка сид-терма ею ОТКЛОНЯЕТСЯ по имени (`internal/membank/decisions.go:359`=`Scoped to APPROVE`), потому что это перенос базового снапшота и пере-оплата черновой волны — другое решение (щель ратифицирована, строка 192). `glossary_seed` — рука владельца · `auto-bank` — прогон · ruby — хранилище | запись двери — под эксклюзивным флоком проекта; рука владельца флока не берёт (остаток — строка 222) |
| **ВИД** — банк, каким его видят роли | строки глоссария в хранилище | только `seedGlossary`, и только полным пересбором | каждая граница прогона; один загрузчик, все гарды |
| **ПРОЕКЦИИ** — то, что банк рассказывает наружу | `<project_db>.bank.json` (единственный канал банка наружу) · карта подписи `<project_db>.mined-signature.yaml` · человеческая таблица стопа `.bank-stop.txt` | прогон, на границе | ⚠ не одинаков: `bank.json` — ПОСЛЕ смены вида; карта подписи и `.bank-stop.txt` — при вычислении стопа, ДО пере-сида, а на стоп-границе вид вообще не меняется (остановившийся прогон авто-банк не пишет) |
| **ПАМЯТЬ ФЛАЖКА** — что стоп уже предъявлял | таблица `bank_stop_presented` | стоп | ПОСЛЕ карты, никогда до |
**Поток однонаправленный: источники → вид → проекции.** Отсюда главное свойство, из-за которого
рефактор не нужен: **расхождение источников с видом структурно невозможно дольше одной границы
прогона**, потому что вид не патчится, а пересобирается целиком.
**Единственная петля — авто-провод, и она названа здесь нарочно:** прогон пишет собственный ИСТОЧНИК
(`auto-bank`) и пере-сидит вид в той же границе. Это первое, куда ткнёт проверяющий однонаправленности.
Петля реальна и защищена с двух сторон — `unsignedEngineSurfaces` держит границу подписанного, а
`loadAutoBank` отказывает строке со статусом `approved`, — то есть слово движка не может присвоить себе
слово владельца. Однонаправленность формулируется точно так: **петля есть, но она не может изменить
подписанное.**
## Почему слово владельца и слово движка не сливаются
Соблазн понятный: одобрения владельца и авто-строки движка — одинаковые по форме строки банка. Но
слитые, они делают неразличимым то, ради чего банк существует: **какие переводы человек действительно
одобрил.** Поэтому источники разнесены и по писателю, и по каталогу — решения владельца едут с книгой,
состояние движка сидит при базе.
## Дисциплина проекции — норма, а не стиль
Три правила, каждое куплено дефектом:
1. **У проекции обязан быть НАЗВАННЫЙ читатель.** Машинная таблица стопа `.bank-stop.json` была
написана для пер-термного экрана подписи; экран упразднён D39.144, читателя не осталось ни в одной
зоне — снесена D39.158. Свип наследия отменённой модели шёл по коду, гейтам и текстам, а её
АРТЕФАКТ пережил его, потому что никто не смотрел на множество публикаций.
2. **У проекции обязан быть якорь свежести ВНУТРИ документа.** Отказ записи банк-экспорта — «громко,
но не фатально» (деньги дороже проекции), и это верно; но «может быть устаревшим» жило только в
ЛОГЕ. Потребитель не мог отличить проекцию этой границы от прошлой. Закрыто конвертом с меткой
границы и идентификатором прогона.
3. **Порядок между носителями — инвариант, а не деталь.** Память флажка пишется ПОСЛЕ карты: падение
между ними даёт лишний доброкачественный стоп, обратный порядок — запомненную поверхность, чьей
карты не существует, то есть молчаливую потерю навсегда. Инвариант, живущий в комментарии, не
держится ничем — держит его пин.
⚠ **КЛАСС A, названный этой страницей: проекция без контракта потребителя и без якоря свежести
в самой себе.** Два известных члена: снесённая таблица стопа · банк-экспорт до конверта. Третий живёт
строкой бэклога 224: банк-экспорт на стопе ПУСТ — проекция, наиболее пустая ровно в тот момент, когда
её читает экран подписи.
**КЛАСС B — другой, и путать их нельзя: межносительный инвариант, живущий комментарием без пина.**
Член — порядок «карта → память» до фикс3. Он НЕ подпадает под класс A: там дефект в том, что документ
не несёт контракта, здесь — в том, что связь ДВУХ носителей держится только текстом, который ничего не
проверяет.
**Второй ярус КЛАССА A (D39.159): носитель с ДВУМЯ писателями и НОЛЬ читателей.** Здесь опасность не в отсутствии
потребителя — она в том, что расхождение писателей ничем не судится, пока читателя нет, а в день,
когда читатель появится, разница СТАНЕТ семантикой, и разрешать её придётся задним числом по уже
накопленной истории. Отсутствие потребителя из симптома делается отсрочкой платежа. Живой член —
`books.chunker_version` в платформе (`PD-396`); родня по эту сторону шва — мёртвые поля шва и
`ingest.StatusReport.UnsignedBankTerms`. Отсюда правило таблицы выше — «у каждого источника РОВНО
один писатель» — читается в обе стороны: **два писателя без читателя судятся так же строго, как
проекция без потребителя, и чинятся раньше, а не когда читатель придёт.**
⚠ **ЭРРАТА 07.09 (оркестратор, акт лендинга пака «гейт вместо прозы»): ТРЕТИЙ ЧЛЕН КЛАССА A ЗАКРЫТ СО
СТОРОНЫ ДВИЖКА.** Стоп-граница теперь публикует свои предложения отдельной секцией того же сайдкара,
который экран подписи уже читает, — то есть исполнено ровно то, чего требует правило этой страницы
(«чинятся раньше, а не когда читатель придёт»), а не рефактор под несуществующий экран. Следствия для
текста ниже: клауза «на стоп-границе вид вообще не меняется» тоже стала неточной — секция едет в проекцию, не проходя
через вид (⚠ при этом правило однонаправленного потока НЕ нарушено: глоссарий не тронут, снапшот байт-равен);
фраза «читаемый банк несёт итоги БЕЗ предложений» с этого дня ЛОЖНА и читается как описание
до-паковой формы; оговорка «когда экран закажут» относится к полному ДЖОЙНУ «предложено × решено ×
нерешено», который по-прежнему живёт только внутри движка и наружу не выносится. ⛔ **Долг, названный
вслух:** у новой секции сегодня НОЛЬ читателей — платформа разбирает сайдкар аллоулистом и поле
игнорирует; носитель обязательства сделать читателя названным — платформенная половина строки **224** (⚠ испр. 08.09: первая редакция эрраты называла 253, но её половина (а) ЗАКРЫВАЕТСЯ лендингом 224, а живая (б) — другой предмет; адрес, не разрешающийся в обязательство, которое он называет, — ровно класс, которым куплено правило 1 этой страницы).
Пока она открыта, это проекция без потребителя, то есть класс A, заведённый сознательно и с адресом.
## Чего эта форма НЕ несёт
Джойн «предложено × решено × нерешено» существует только внутри движка. Читаемый банк несёт итоги без
предложений, карта — предложения без решений. Экран подписи при разморозке фронта заставит платформу
пере-реализовать движковый закон у себя, что запрещено п.6 закона шва. Лечение — публиковать
нерешённость движком, когда экран закажут; сейчас это ноль кода и строка бэклога, а не рефактор под
несуществующий экран.
## Если проектировать с нуля
То же самое, с одним отличием: дельта и список отказов — **один документ с двумя секциями**. Это
правильнее с нуля (одна запись, одна точка фиксации, невозможность указать двумя ключами на один
файл), но не стоит миграции схемы в полёте: снятие объявляемых ключей D39.158 уже закрыло тот дефект,
ради которого слияние в первую очередь понадобилось бы.