diff --git a/docs/architecture/05-decisions-index.md b/docs/architecture/05-decisions-index.md index eefa680f..5cdd1278 100644 --- a/docs/architecture/05-decisions-index.md +++ b/docs/architecture/05-decisions-index.md @@ -291,7 +291,7 @@ | D39.230 | 08.09 | **СЛОВО ВЛАДЕЛЬЦА по оборванным вызовам** (по итогу консилиума с чистым Fable 5, строка 360): форма **А** — оценка списывается с читателя, но **пометка (`attempt_timeout` + `estimated=1` + публикация счёта и суммы оценочных строк рядом с `committed_usd`) есть УСЛОВИЕ решения**, не опция; платная проба баланса ОТКЛОНЕНА ⇒ вопрос «биллит ли вендор оборванное» осознанно остаётся открытым; ожидание провайдера до ~20 мин РАЗРЕШЕНО ⇒ дедлайн считается от бюджета. Бит закрыт, пак пишется целиком | деньги · транспорт · слово владельца | | D39.231 | 10.09 | **ДИСПОЗИЦИИ, РОЗДАННЫЕ ПИСЬМАМИ, РАТИФИЦИРОВАНЫ ЗАДНИМ ЧИСЛОМ.** Три ответа оркестратора движковой сессии жили только в межсессионных сообщениях; сессия упала, tmpfs-канал умер — слово перестало существовать. ⇒ **норма: диспозиция ратифицируется ИЛИ получает строку ТЕМ ЖЕ движением, каким отправляется ответ.** Ратифицировано удаление h2-write-бонда; два продуктовых вопроса заведены строками **374**/**375** вместо обещания; откат разведения предикатов отклонён уликой ($0.80 за отклонённый провайдером запрос) | процесс · транспорт · деньги | | D39.232 | 10.09 | **АКТ: движковый пак «вызов, который оборвали мы» ПРИНЯТ** (`3f05fab`, 28 путей, $0). Движок перестал платить дважды за одну генерацию: граница денег — ДОСТАВКА, дедлайн считается от бюджета, оплаченный обрыв не перепокупается. Закрыты 360 · 369 · 78. ⛔ Приёмка двумя рубежами ДО ратификации нашла 5 блокеров: **пять мутаций выжили на зелёной батарее, все на носителях ВИДИМОСТИ денег** — то есть на УСЛОВИИ слова владельца. ⭐ Куплены четыре нормы об измерении (знаменатель по одному глаголу · глаголы по имени · знаменатель закрывает одну ось) и три класса вырожденных пинов | приёмка · транспорт · деньги · метод | -| D39.233 | 10.09 | **ДВА СТОПА получают носитель; две строки закрыты замером; условие слова владельца — без потребителя.** У человека одна кнопка и она ЖЁСТКАЯ (`cmd/tmctl/main.go:207`); мягкий глагол построен, но локален волне и дёргает его только потолок (`waverun.go:353`); второй Ctrl-C НЕ ДЕЛАЕТ НИЧЕГО. ⛔ Контракт (`openapi.yaml:762`) и платформа (`runner.go:56-58`) уже ОБЕЩАЮТ мягкий стоп, которого нет, а `stopGrace` 600 с меньше потолка ожидания 1240 с. ⛔ Пометка «оценка» по шву НЕ едет (0 хитов в кадрах, контроль 6) ⇒ `D39.230` п.1 без потребителя — строка **382**. ✅ Закрыты **374** (вердикт главы остановка уже не портит — пере-снято, пин зелён) и **371** (переплата невозможна: `Billable` требует 2xx). Рекомендована форма «повтор ужесточает», коды выхода не трогать | стоп · деньги · шов · метод | +| D39.233 | 10.09 | **ДВА СТОПА получают носитель; две строки закрыты замером; условие слова владельца — без потребителя.** У человека одна кнопка и она ЖЁСТКАЯ (`backend/cmd/tmctl/main.go:208`=`signal.NotifyContext(context.Background()` — ⚠ испр. 16.09: было :207 (пустая строка)); мягкий глагол построен, но локален волне и дёргает его только потолок (`waverun.go:353`); второй Ctrl-C НЕ ДЕЛАЕТ НИЧЕГО. ⛔ Контракт (`openapi.yaml:762`) и платформа (`runner.go:56-58`) уже ОБЕЩАЮТ мягкий стоп, которого нет, а `stopGrace` 600 с меньше потолка ожидания 1240 с. ⛔ Пометка «оценка» по шву НЕ едет (0 хитов в кадрах, контроль 6) ⇒ `D39.230` п.1 без потребителя — строка **382**. ✅ Закрыты **374** (вердикт главы остановка уже не портит — пере-снято, пин зелён) и **371** (переплата невозможна: `Billable` требует 2xx). Рекомендована форма «повтор ужесточает», коды выхода не трогать | стоп · деньги · шов · метод | | D39.234 | 10.09 | **СЛОВО ВЛАДЕЛЬЦА по двум остановкам + сверка носителей, которую `D39.232` объявил и не сделал.** Ратифицировано: форма «повтор ужесточает» (первое нажатие — мягкая, второе — жёсткая), потолок ожидания ~20 мин (одна летящая попытка, не цепочка), пак мягкой остановки и строка **377** — одним заходом; коды выхода не трогать. Пак движка ПАРНЫЙ платформенному (грейс 600 с < 1240 с). ⛔ Эррата 10.09-б: три объявленные закрытыми строки сутки стояли в таблице — **78** · **369** · **360** закрыты и убраны, плюс незамеченная **331** (с названным сужением, остаток — **376**). Инвентарь живых доков: `15-money-path.md` чинен в трёх местах и получил новую денежную границу; реестр требований против брифа владельца сходится (V0–V6) | стоп · деньги · слово владельца · сверка | | D39.235 | 10.09 | **ФОРМА КАДРА ОСТАНОВКИ ЗАФИКСИРОВАНА ДО РАБОТ; два парных промта выданы; сплошной инвентарь живых доков.** Кадр: `Finished.Stop{mode,…}` присутствием (прецедент `Volume`), `estimated{rows,micro_usd}` на ОБОИХ кадрах, `Money` на `stopped` всегда, запасной канал — обязанность платформы; минор потока `1.3→1.4`. ⛔ Эррата к `D39.234` п.1б: «отказ на гейте» ратифицированных ~20 мин НЕ даёт — резервация держит ВСЮ цепочку ретраев, нужен и `retryLoop`. ⛔ Замер исполнением: платформенный `Stop` без `--no-block` блокируется на весь грейс (15,21 с против 0,019 с) ⇒ сломал бы `202` контракта. Инвентарь: **19** мест починено, самый урожайный класс — УСЛОВНОЕ ЗАКРЫТИЕ. Контракт `0.13.0→0.13.1`: `Run.required` требовал отставленный член ⇒ каждый ответ был невалиден. Закрыта строка **166** | стоп · шов · контракт · инвентарь | | D39.236 | 10.09 | **ДИСПОЗИЦИИ ПО ЭХО-ПРОТОКОЛАМ обеих сессий пака «две остановки»: шесть возражений, шесть приняты, ТРИ — мои ошибки.** (1) счётчик оценки СИДИРУЕТСЯ из стора, а не копится с нуля — иначе пин равенства двух каналов зелен на пустом сценарии; (2) расширение правила присутствия `Money` формально МАЖОР, везём минором `1.3→1.4` как осознанное исключение (мажор читателем отвергается целиком); (3) «опора готова» для строки оператору — преувеличение, прогонного реестра летящих вызовов нет; (4) платформа поднимает `StreamVersion` и тем же движением пишет, каких полей 1.2/1.3 не читает; (5) «свип встанет на 20 минут» — неверный механизм, настоящий ущерб — ложная тревога `StalledAfter`; (6) 1240 с — свойство ПАРЫ, не константа платформы. ⭐ Все шесть пришли ПОСЛЕ двух кругов ревью: эхо-протокол — третий рубеж, а не формальность | стоп · шов · метод | diff --git a/docs/architecture/14-api-contract/README.md b/docs/architecture/14-api-contract/README.md index 01ceee55..6a85cdc8 100644 --- a/docs/architecture/14-api-contract/README.md +++ b/docs/architecture/14-api-contract/README.md @@ -290,7 +290,7 @@ SQL и до 0.3.0 не отдавалась: `b.chapter_count - (select count(*) Флагнутый юнит легально приходит С ТЕКСТОМ в двух случаях: косметическая зачистка санитайзера («the chunk is NOT lost — the cleaned text is committed as the export», `backend/internal/pipeline/disposition.go:160`=`the cleaned text is committed as the export` — ⚠ испр. 16.09: было :96-104) и -c-lite member-drop (`pipeline/export.go:203-207`). Поэтому состояние выводится из ПАРЫ (вердикт + +c-lite member-drop (`pipeline/export.go:203-207`=`if exp.GhostRows > 0`). Поэтому состояние выводится из ПАРЫ (вердикт + наличие финального текста): флаг+текст → `translated` с замечанием; флаг+пусто → `withheld`. Причина флага при этом ПРОИЗВОЛЬНА («единственный легальный случай» снято ревью round-2): при c-lite @@ -315,8 +315,8 @@ drop юнит несёт причину ПЕРВОГО выпавшего чле | `status` | `proposed · in_progress · approved` | `auto · draft · approved` | `backend/internal/store/migrate.go:192`=`auto|draft|approved (term status machine` — ⚠ испр. 16.09: было :191; только `approved` — канон | | `kind` ◆ | `name · place · title · term · nickname` **плюс `null`** | то же | `backend/internal/terminology/classify.go:27`=`var CandidateTypes = map[string]bool` (`CandidateTypes` — единственный носитель `nickname`; ⚠ испр. 07.09: прежние `classify.go:15` + `banknote.go:74` мертвы — `Types` не несёт `nickname`, а `bankTypeOK` снесён паком «гейт вместо прозы» и сведён к `CandidateTypes`); пустое — `backend/internal/membank/memseed.go:323-326`=`if nk == "" || seen[nk]` | | `origin` | `given · annotated · found` | `seed · ruby · mined` | пути записи, см. ниже | -| `sense` | свободный текст, пустая строка = «нет различителя» | то же | `store/migrate.go:182` | -| окно | `since_chapter`/`until_chapter`, **`null` = без границы** | целые, `0` = без границы | `store/migrate.go:189-190` | +| `sense` | свободный текст, пустая строка = «нет различителя» | то же | `backend/internal/store/migrate.go:183`=`A3 polysemy disambiguator` — ⚠ испр. 16.09: было :182 (колонка type) | +| окно | `since_chapter`/`until_chapter`, **`null` = без границы** | целые, `0` = без границы | `backend/internal/store/migrate.go:189-190`=`spoiler window start` | **Почему слова пере-назначены (0.3.0, Б-0/Б-17).** `ruby` — японская фуригана, то есть паро-специфика в общем слое; `mined` — имя стадии конвейера; `draft` — имя волны; `auto` читается @@ -340,8 +340,8 @@ drop юнит несёт причину ПЕРВОГО выпавшего чле единственное место, где пустая строка и `null` означают одно, и потому оно записано. **Фантом `auto` в провенансе убран ещё в 0.2.0:** комментарий схемы движка (`migrate.go:193`: -`seed|ruby|auto`) устарел — `"auto"` пишет СТАТУС, не провенанс (`membank/memseed.go:328`: -`Status:"auto", Source:"ruby"`), майнинг ставит `mined` (`pipeline/mining.go:424`). +`seed|ruby|auto`) устарел — `"auto"` пишет СТАТУС, не провенанс (`backend/internal/membank/memseed.go:424`=`Status: "auto", Source: "ruby"` — ⚠ испр. 16.09: было :328: +`Status:"auto", Source:"ruby"`), майнинг ставит `mined` (`backend/internal/pipeline/mining.go:763`=`e.Source = "mined"` — ⚠ испр. 16.09: было :424). **Ложный друг `source`.** У движка колонка `source` — это ПРОВЕНАНС. Поэтому в контракте провенанс зовётся `origin`, формы термина — `src`/`dst`, а имя `source` в схеме банка не используется вовсе. @@ -350,7 +350,7 @@ drop юнит несёт причину ПЕРВОГО выпавшего чле **Окно термина (0.3.0, Б-18).** `since_chapter`/`until_chapter` стоят на НОМЕРЕ главы, а номер сам контракт называет не-ключом: нумерация плотная, правка исходника сдвигает хвост, и для книги без нумерации номера легально нет. Перевести окно на `chapter_id` нельзя — оно входит в ключ уникальности -термина у движка (`store/migrate.go:202`), и смена ключа это работа движка, не контракта. Поэтому +термина у движка (`store/migrate.go:202`=`boundary is two rows with different windows`), и смена ключа это работа движка, не контракта. Поэтому записано ЯВНО: окно живёт в координатах текущего `structure_version` и пересчитывается при его смене; два смысла нуля разведены на `null` («без границы»), потому что `Chapter.number` начинается с единицы и `0` был сентинелом с двумя значениями в двух полях. diff --git a/docs/architecture/15-money-path.md b/docs/architecture/15-money-path.md index 4b0bbe65..d4526fbd 100644 --- a/docs/architecture/15-money-path.md +++ b/docs/architecture/15-money-path.md @@ -23,7 +23,7 @@ - **Потолки — wiring, не семантика:** `Ceilings` намеренно исключены из `BriefHash` («Wiring fields (paths, ceilings, db) deliberately excluded», `backend/internal/config/book.go:345`=`Wiring fields (paths, ceilings, db) deliberately excluded`; канон — `backend/internal/config/book.go:369`=`canon := struct {`) ⇒ смена ДЕНЕЖНОГО потолка не двигает ни снапшот, ни ре-билл (D39.110 п.2б). ⚠ Верно ровно про ДЕНЬГИ: потолок СЕГМЕНТАЦИИ `edit_ceiling_out` в снапшот ВХОДИТ и меняет границы чанков (`backend/internal/pipeline/snapshot.go:34`=`EditCeilingOut int`). - **`status --json` отдаёт фигуры платформе:** `committed_usd`, `reserved_usd`, `book_ceiling_usd`, `ceiling_pct` = 100·(committed+reserved)/book_ceiling (`backend/internal/pipeline/status.go:342`=`book_ceiling_usd,omitempty`; арифметика `ceiling_pct` — `backend/internal/pipeline/status.go:949`=`100 * (committed + reserved)`). Дневной фигуры в status нет (PD-157 — `platform/docs/DEFECT_REGISTER.md`, греп `PD-157`). - **Леджер = НИЖНЯЯ граница; строка 78 снята с трекера 10.09, но её ЖИВОЙ ОСТАТОК несёт строка 377.** ⛔ **Испр. 10.09 — прежняя редакция этого пункта, написанная в тот же день, была НЕВЕРНА, и ошибка моя.** Она утверждала, будто оплаченный `2xx` с нечитаемым телом «прежде в `request_log` не попадал» и что это починил пак оборванных вызовов. Замер: ветвь `BilledDecodeError` писала свою строку с пометкой оценки ещё до пака — на `3f05fab^` это `stagerun.go:629`, введено коммитом `2f91b04` **24.07**, а пак этой ветви не касался вовсе (хитов `Estimated` в его диффе по `stagerun.go` — **0**). И носитель я процитировал ЧУЖОЙ: `backend/internal/pipeline/cutcall.go:133`=`rl := r.baseRequestLog(c.stage, c.chunk` обслуживает `*llm.AttemptCutError` (`backend/internal/pipeline/stagerun.go:802`=`var cut *llm.AttemptCutError` — ⚠ испр. 16.09: было :741), а речь шла о `*llm.BilledDecodeError` (`stagerun.go:709`) — это разные классы, и второй до `cutcall.go` не доходит. **Что пак действительно купил:** строку `request_log` за вызов, который оборвали МЫ (прежде он падал в ветвь «ничего не куплено» и получал $0-строку) — вот это `cutcall.go:133`. **Что остаётся правдой:** леджер — нижняя граница, потому что обрыв, за которым ретрай УСПЕЛ, исчезает бесследно (строка **377**). Живой замер `D39.86` — 3 вызова из 14, неизвестность $0.015111 при леджере $0.114378. -- **ГРАНИЦА ДЕНЕГ ЗА ОБОРВАННЫЙ НАМИ ВЫЗОВ — ФАКТ ДОСТАВКИ, а не статус-строка** (пак строки 360, акт `D39.232`; слово владельца — `D39.230`). Деньги берутся, только когда провайдер подтвердил запрос ответным объектом: `backend/internal/llm/attemptcut.go:221`=`Billable: answered && tr.afterHeaders()`. Наш собственный дедлайн даёт свой класс `attempt_timeout` без ретрая; дедлайн вычисляется ИЗ БЮДЖЕТА вызова, а не назначается (`backend/internal/llm/attemptcut.go:269`=`func (p RetryProfile) deriveDeadline`), так что эскалация, удвоившая `max_tokens`, удваивает и время. Оплаченный обрыв получает чекпойнт, и резюм его НЕ перепокупает. ⛔ Пометка «это оценка» — УСЛОВИЕ слова владельца (`D39.230` п.1) — сегодня в движке живёт ТРЕМЯ носителями (колонка `Estimated` в `request_log` — `backend/internal/store/requestlog.go:41`=`Estimated marks a row whose CostUSD`; пара `estimated_rows`/`estimated_usd` в `status --json` — `backend/internal/pipeline/status.go:911`=`Re-payment projection (spec`; печать оператору — `backend/cmd/tmctl/render.go:402`), но НАРУЖУ не выходит ни одним каналом шва: строка **382**. ⚠ Формулировка испр. 10.09 — прежняя («живёт только в `status --json`») звала следующего чинить пометку, которая уже есть. +- **ГРАНИЦА ДЕНЕГ ЗА ОБОРВАННЫЙ НАМИ ВЫЗОВ — ФАКТ ДОСТАВКИ, а не статус-строка** (пак строки 360, акт `D39.232`; слово владельца — `D39.230`). Деньги берутся, только когда провайдер подтвердил запрос ответным объектом: `backend/internal/llm/attemptcut.go:221`=`Billable: answered && tr.afterHeaders()`. Наш собственный дедлайн даёт свой класс `attempt_timeout` без ретрая; дедлайн вычисляется ИЗ БЮДЖЕТА вызова, а не назначается (`backend/internal/llm/attemptcut.go:269`=`func (p RetryProfile) deriveDeadline`), так что эскалация, удвоившая `max_tokens`, удваивает и время. Оплаченный обрыв получает чекпойнт, и резюм его НЕ перепокупает. ⛔ Пометка «это оценка» — УСЛОВИЕ слова владельца (`D39.230` п.1) — сегодня в движке живёт ТРЕМЯ носителями (колонка `Estimated` в `request_log` — `backend/internal/store/requestlog.go:41`=`Estimated marks a row whose CostUSD`; пара `estimated_rows`/`estimated_usd` в `status --json` — `backend/internal/pipeline/status.go:911`=`Re-payment projection (spec`; печать оператору — `backend/cmd/tmctl/render.go:402`=`the degraded/err tail`), но НАРУЖУ не выходит ни одним каналом шва: строка **382**. ⚠ Формулировка испр. 10.09 — прежняя («живёт только в `status --json`») звала следующего чинить пометку, которая уже есть. - **Согласие на пере-оплату — ДВИЖКОВЫЙ гейт**. Прогон, который пере-покупает уже оплаченные юниты дороже порога, ОСТАНАВЛИВАЕТСЯ и требует явного согласия. Порог по умолчанию ратифицирован D20.2-Q2: `min($0.50, 5% × ProjectedBookUSD)`. Носитель — `backend/internal/pipeline/rebill.go`, греп `rebillConsentThreshold`; единственная ручка книги — `rebill_consent_usd` (валидатор `backend/internal/config/book.go`, греп `rebill_consent_usd`; 0 = ратифицированный дефолт). ⚠ Порог считается БЕЗ контура банк-ролей (D39.182 §3), то есть срабатывает позже, чем подсказывает полная проекция. diff --git a/docs/architecture/17-seam-inbound-law.md b/docs/architecture/17-seam-inbound-law.md index 54bcd9e0..18d6f058 100644 --- a/docs/architecture/17-seam-inbound-law.md +++ b/docs/architecture/17-seam-inbound-law.md @@ -46,7 +46,7 @@ (`backend/internal/config/book.go:200`=`b.ProjectDB = filepath.Join(dir, b.BookID+".db")`) · конвенционное опциональное чтение `.auto-bank.yaml` (`backend/internal/pipeline/mining.go:703`=`.auto-bank.yaml`). -2. **Посреди прогона внутрь идёт ТОЛЬКО сигнал.** `SIGTERM` = «дочти чанк и отпусти лок» — ⚠ **это ратифицированная ЦЕЛЬ, а не сегодняшнее дерево (пометка 10.09).** Владелец ратифицировал две остановки (`D39.234` п.1а): первый сигнал — мягкий, он и означает «дочти и отпусти». В КОДЕ его ещё нет: `backend/cmd/tmctl/main.go:207` отменяет контекст прогона, летящий вызов рвётся, книжится по оценке и метится `cancelled`. ⇒ норму НЕ переписывать под дерево, дерево догоняет норму; носитель работы — строка бэклога **381**, где этот адрес назван третьим. Всё +2. **Посреди прогона внутрь идёт ТОЛЬКО сигнал.** `SIGTERM` = «дочти чанк и отпусти лок» — ⚠ **это ратифицированная ЦЕЛЬ, а не сегодняшнее дерево (пометка 10.09).** Владелец ратифицировал две остановки (`D39.234` п.1а): первый сигнал — мягкий, он и означает «дочти и отпусти». В КОДЕ его ещё нет: `backend/cmd/tmctl/main.go:208`=`signal.NotifyContext(context.Background()` — ⚠ испр. 16.09: было :207 (пустая строка) отменяет контекст прогона, летящий вызов рвётся, книжится по оценке и метится `cancelled`. ⇒ норму НЕ переписывать под дерево, дерево догоняет норму; носитель работы — строка бэклога **381**, где этот адрес назван третьим. Всё остальное — на границах процессов. Арбитр — эксклюзивный flock проекта на всю жизнь прогона (`backend/internal/store/store.go:55`=`EXCLUSIVE flock on`), мутирующий глагол берёт его неблокирующе и при занятости отказывает **ровно классом 12** (`platform/internal/ingest/exit.go:69`=`ExitProjectLocked = 12`), а НЕ 10: diff --git a/docs/architecture/18-bank-ontology.md b/docs/architecture/18-bank-ontology.md index 90b6b9f6..11f54704 100644 --- a/docs/architecture/18-bank-ontology.md +++ b/docs/architecture/18-bank-ontology.md @@ -12,7 +12,7 @@ | Роль | Носители | Писатель | Момент фиксации | |---|---|---|---| -| **ИСТОЧНИКИ** — то, из чего банк собирается | `glossary_seed` · `.mined-delta.yaml` · `.mined-rejects.yaml` (слово ВЛАДЕЛЬЦА, рядом с `book.yaml`, едут с книгой в бэкапе и экспорте) · `.auto-bank.yaml` (слово ДВИЖКА, сидит при базе) · ruby-чтения в хранилище | у каждого РОВНО один. ⚠ Дверь `bank-apply` пишет ТОЛЬКО два файла решений: правка сид-терма ею ОТКЛОНЯЕТСЯ по имени (`internal/membank/decisions.go:359`), потому что это перенос базового снапшота и пере-оплата черновой волны — другое решение (щель ратифицирована, строка 192). `glossary_seed` — рука владельца · `auto-bank` — прогон · ruby — хранилище | запись двери — под эксклюзивным флоком проекта; рука владельца флока не берёт (остаток — строка 222) | +| **ИСТОЧНИКИ** — то, из чего банк собирается | `glossary_seed` · `.mined-delta.yaml` · `.mined-rejects.yaml` (слово ВЛАДЕЛЬЦА, рядом с `book.yaml`, едут с книгой в бэкапе и экспорте) · `.auto-bank.yaml` (слово ДВИЖКА, сидит при базе) · ruby-чтения в хранилище | у каждого РОВНО один. ⚠ Дверь `bank-apply` пишет ТОЛЬКО два файла решений: правка сид-терма ею ОТКЛОНЯЕТСЯ по имени (`internal/membank/decisions.go:359`=`Scoped to APPROVE`), потому что это перенос базового снапшота и пере-оплата черновой волны — другое решение (щель ратифицирована, строка 192). `glossary_seed` — рука владельца · `auto-bank` — прогон · ruby — хранилище | запись двери — под эксклюзивным флоком проекта; рука владельца флока не берёт (остаток — строка 222) | | **ВИД** — банк, каким его видят роли | строки глоссария в хранилище | только `seedGlossary`, и только полным пересбором | каждая граница прогона; один загрузчик, все гарды | | **ПРОЕКЦИИ** — то, что банк рассказывает наружу | `.bank.json` (единственный канал банка наружу) · карта подписи `.mined-signature.yaml` · человеческая таблица стопа `.bank-stop.txt` | прогон, на границе | ⚠ не одинаков: `bank.json` — ПОСЛЕ смены вида; карта подписи и `.bank-stop.txt` — при вычислении стопа, ДО пере-сида, а на стоп-границе вид вообще не меняется (остановившийся прогон авто-банк не пишет) | | **ПАМЯТЬ ФЛАЖКА** — что стоп уже предъявлял | таблица `bank_stop_presented` | стоп | ПОСЛЕ карты, никогда до |