126 lines
19 KiB
Markdown
126 lines
19 KiB
Markdown
# 17 — Закон входной двери шва движок ↔ платформа
|
||
|
||
> **СТАТУС: РАТИФИЦИРОВАНО D39.156 (слово владельца 23.08).** Это НОРМА, ссылаться на неё можно, и у неё есть
|
||
> работающий образец: первая дверь по этому закону ПОСТРОЕНА и заленджена 27.08 — `tmctl bank-apply` (D39.158).
|
||
> Онтология слоя, который дверь обслуживает, — `18-bank-ontology.md`.
|
||
>
|
||
> **Ратифицирована ДИСЦИПЛИНА, а не каталог команд**, и свойство этого выбора такое: новые двери встают паками
|
||
> без возврата к владельцу — ровно так, как код выхода `13` встал в ратифицированную полосу 10–19 через
|
||
> ратификации ОРКЕСТРАТОРА (D39.131 п.2а → D39.132 п.2г → D39.134), а не новым решением владельца.
|
||
>
|
||
> **Зачем понадобилось.** Контракт фронт↔платформа ратифицирован (`14-api-contract/`), а платформа↔движок — нет:
|
||
> исследование шва дошло до «события пишутся в `events.jsonl`» и дальше отвечало только на вопрос «как запускать
|
||
> движок» (`research/23`, транспортная часть §1 superseded D39.106; `research/25` — ратифицированная форма и
|
||
> отвергнутые альтернативы). НАРУЖУ из движка идёт богатый поток — журнал событий по курсору, полоса кодов
|
||
> выхода, версионированные артефакты-проекции, — а ВНУТРЬ не шло ничего, кроме запуска с флагами: данные,
|
||
> которыми владеет пользователь продукта, движок принимать не умел вовсе.
|
||
|
||
## Почему НОРМА, а не каталог команд
|
||
|
||
За осознанный контракт говорят три прожитых инцидента гниения нератифицированных входных конвенций:
|
||
дубль дефолта `<book_id>.db` у платформы (строка бэклога **213**) · ручная константа версии API,
|
||
не поднятая при синке (D39.153) · три мины формата карты подписи, вскрытые только трассировкой
|
||
строки 199.
|
||
|
||
Против ПОЛНОГО каталога — то, что половина будущих дверей гейтится нерешёнными развилками: строка
|
||
**192** прямо запрещена к проектированию до полигонных итогов (D39.144 п.1; ⚠ область сужена D39.165 — открыть дверь к построенному ≠ проектировать цикл, см. ⚠-строку под таблицей дверей), **161**/**185** ждут
|
||
дизайн-пака, **170** — «после беты формы Б». Каталогизировать их сейчас значит выдумывать.
|
||
|
||
Прецедент — исходящее направление: там выиграла ратификация ПОЛОСЫ и
|
||
ДИСЦИПЛИНЫ, а не перечня.
|
||
|
||
## Закон: семь пунктов
|
||
|
||
1. **Вход — это аргументы при спавне и файлы, названные аргументами.** Данные, которыми владеет
|
||
пользователь продукта, доезжают движковым ГЛАГОЛОМ; движок — единственный писатель файлов своей
|
||
схемы и владелец их путей и конвенций. Платформа читает только версионированные артефакты по
|
||
путям, которые ей СООБЩЕНЫ ДВИЖКОМ **ИЛИ РАТИФИЦИРОВАНЫ КОНТРАКТОМ ШВА**, и никогда не выводит путь сама.
|
||
⚠ **Два выноса, без которых буква закона объявляет вне закона уже ратифицированное:**
|
||
**(а) форма Б** — одноразовый рендер стартового `book.yaml` при рождении книги (D39.130 п.2б): платформа пишет пять
|
||
ключей идентичности, всё остальное проходит из шаблона непрочитанным
|
||
(`platform/internal/books/render.go:29-33`=`comes from the template unread and unjudged`); живёт до формы В (строка 170).
|
||
**(б) фикс-имена ратифицированной конвенции шва** — `events.jsonl` никто платформе не «сообщает», это константа
|
||
(`platform/internal/ingest/tail.go:17`=`const JournalFile = "events.jsonl"`, D39.106); без этого выноса буква ломает главный канал самого шва.
|
||
*Прецеденты:* потолок аргументом (D39.110 п.2б → `--ceiling-usd`, D39.122) · `--verify-bank`
|
||
флагом (`platform/internal/runner/engine.go:116`=`func TranslateArgs(workdir string, verifyBank bool`) · дефолт `project_db` по конвенции каталога
|
||
(`backend/internal/config/book.go:200`=`b.ProjectDB = filepath.Join(dir, b.BookID+".db")`) · конвенционное опциональное чтение `.auto-bank.yaml`
|
||
(`backend/internal/pipeline/mining.go:674`=`.auto-bank.yaml`).
|
||
|
||
2. **Посреди прогона внутрь идёт ТОЛЬКО сигнал.** `SIGTERM` = «дочти чанк и отпусти лок» — ⚠ **это ратифицированная ЦЕЛЬ, а не сегодняшнее дерево (пометка 10.09).** Владелец ратифицировал две остановки (`D39.234` п.1а): первый сигнал — мягкий, он и означает «дочти и отпусти». В КОДЕ его ещё нет: `backend/cmd/tmctl/main.go:207` отменяет контекст прогона, летящий вызов рвётся, книжится по оценке и метится `cancelled`. ⇒ норму НЕ переписывать под дерево, дерево догоняет норму; носитель работы — строка бэклога **381**, где этот адрес назван третьим. Всё
|
||
остальное — на границах процессов. Арбитр — эксклюзивный flock проекта на всю жизнь прогона
|
||
(`backend/internal/store/store.go:55`=`EXCLUSIVE flock on`), мутирующий глагол берёт его неблокирующе и при занятости
|
||
отказывает **ровно классом 12** (`platform/internal/ingest/exit.go:69`=`ExitProjectLocked = 12`), а НЕ 10:
|
||
полоса различает их не косметически — 10 (`platform/internal/ingest/exit.go:62`=`ExitConfigInvalid = 10`) значит «деплой сломан,
|
||
чинит человек, книги ждут», 12 значит «ничего не произошло, подожди и повтори» (D39.134 п.2а). Запись через слэш
|
||
позволила бы исполнителю выбрать 10, и платформа отнесла бы занятый лок к операторским ошибкам.
|
||
*Почему жёстко:* движок пере-читает сид ПОСРЕДИ прогона (`mining.go:221`), запись в живой прогон
|
||
въехала бы в снапшот недетерминированно. Потолок посреди прогона менять НЕЛЬЗЯ — он едет со
|
||
стартом и после не меняется, лекарство — новый прогон
|
||
(`platform/internal/runs/reconcile.go` (греп `202` в обработчике возобновления; ⚠ номер строки снят 30.08 — файл правит живая сессия P12)). Дверь «менять потолок файлом» закрыта;
|
||
не выдумывать её заново из `research/25`.
|
||
|
||
3. **Каждый JSON-выход глагола несёт версию документа — п. ИСПОЛНЕН, асимметрии нет:** `tm-bank-v1`
|
||
(`pipeline/bankexport.go:30`=`const bankExportVersion = "tm-bank-v1"`), `tm-manifest-v2` (`pipeline/manifest.go:46`=`const manifestVersion = "tm-manifest-v2"`)
|
||
и `status --json` (`backend/internal/pipeline/status.go:155-156`=`const statusVersion = "tm-status-v1"`, проставляется `:485`).
|
||
|
||
4. **Незнакомый флаг = громкий отказ при спавне.** Сегодня это так по устройству разбора аргументов
|
||
(`backend/cmd/tmctl/invocation.go:128`=`flag.NewFlagSet(cmd, flag.ContinueOnError)`); зафиксировать законом, а не полагаться на случайность.
|
||
|
||
5. **Глагол зовётся ТЕМ ЖЕ пиннутым бинарём, что и возобновляемая попытка** (`runs/spawn.go:250-258`=`engineBinary is the path an attempt is PINNED to`,
|
||
строка бэклога 139). Иначе файл, записанный новым бинарём, читается старым — а при нестрогом
|
||
загрузчике сида (строка **212**) новое поле теряется МОЛЧА (⚠ **адрес испр. 10.09: строки 212 в живом трекере НЕТ** — она закрыта; факт оставлен как довод, номер снят, чтобы не звать читателя в пустоту).
|
||
|
||
6. **Словарь движка ↔ словарь контракта — одна таблица, два направления, один файл.**
|
||
Сегодня перевод односторонний, на чтении (`platform/internal/ingest/bank.go:15-21`=`Every vocabulary crossing here is TRANSLATED and none is passed through`). Запись
|
||
делает таблицу двунаправленной, и две копии разъедутся.
|
||
|
||
7. **Мутирующий глагол сначала даёт проекцию, потом мутирует.** «Почём ДО подтверждения».
|
||
*Прецеденты:* rebill-смета с порогом согласия (D20.2-Q2, `pipeline/rebill.go`) · строка 185
|
||
(«сколько глав и почём до подтверждения») · `--dry-run` строки 124(в).
|
||
|
||
## Совместимость без второго рукосинхронизируемого контракта
|
||
|
||
Версий у КОМАНД нет и заводить их не нужно: совместимость выводится из трёх уже существующих ног —
|
||
пиннинг попытки к бинарю (строка 139) · незнакомый флаг роняет спавн громко (п.4) · версионированный
|
||
конверт на каждом JSON-выходе (п.3). Плюс одна асимметричная норма: движок не меняет семантику флага
|
||
или поля без мажора, новые флаги едут «движок-первым» — расширение уже ратифицированного порядка
|
||
деплоя `migrate` (D39.134 п.3).
|
||
Хендшейк или `capabilities`-глагол движка — НЕ строить: один хост, «least mechanism»
|
||
(`12-go-style-notes.md` §1), а судьбу ручной константы показал инцидент `ContractVersion`.
|
||
⚠ Предположение, не механизм: окно «старая платформа + новый движок» считается покрытым обратной
|
||
совместимостью флагов. Если деплои разъедутся сильнее — вернуться к вопросу.
|
||
|
||
## Двери, УЖЕ подразумеваемые живыми строками
|
||
|
||
| дверь | откуда следует |
|
||
|---|---|
|
||
| `resnapshot --dry-run` | строка **124(в)**, глагол поименован там дословно. ⚠ **Испр. 10.09: ссылка на «родню строки 231» СНЯТА — такой строки в живом трекере нет,** а обоснование стояло на ней и на слове «скорее всего», то есть на догадке без хозяина. Прежний текст: родня строки 231 просит ту же способность («оценить, ничего не покупая») со стороны ШВА, и решаться они, скорее всего, одним глаголом |
|
||
| ✅ **И ПРОЧИТАНА платформой с 06.09** — механика ОДНИМ носителем: `15-money-path.md` §3 (акт `D39.208`). Прежняя редакция: ⚠ движковая оценка $/глава — **ПОСТРОЕНА и опубликована 05.09** (`expected_usd` · `book_once_usd` · `step_max_usd` в `manifest --json`/`status --json`, лендинг `81a89e9`, акт D39.206); непостроена ПЛАТФОРМЕННАЯ половина — числа никем не читаются (`git grep -lE "StepMax|step_max" -- platform/` пусто) | строка **166**. Приоритет поднят: D39.165 §1 ратифицировал цену ОТ ОБЪЁМА исходника, и оценка перестала быть удобством. ⚠ Числа цены главы и их пере-пин под цены с 16.08 — только `15-money-path.md` §3 п.3, здесь копии нет |
|
||
| `tmctl init` (форма В) | строка **170**; приоритет ПАДАЕТ после двери 199(а) — та забирает её главный довод |
|
||
|
||
⚠ **ПОСТРОЕНЫ по этому закону и из таблицы сняты** (форма читается по ним как по образцам): приём правок банка движковым глаголом `tmctl bank-apply` — 27.08, D39.158, развилка владения `book.yaml` (строка 199а) растворена · `--keys-file` — движок 27.08 (D39.158), передача с платформы 28.08 (P9/D39.162), строка **211** ЗАКРЫТА и блокером живого прогона больше не является (⚠ ⚠ испр. 05.09: строка **202** ЗАКРЫТА D39.200 — прогон отработал; её остаток о непокрытых поверхностях живёт строкой **255**) · путь артефакта в ответе движка — конверт `StatusArtifacts` в `status --json`/`manifest --json` (`project_db` · `bank_export` · `mined_delta` · `mined_rejects` · `book_files`, D39.162/D39.175), строка **213** сужена до одной строки ДЕВ-супервизора · consent-флаги `--resnapshot`/`--accept-rebill` от платформы — 28.08, D39.166, продаются членом `RunRequest.re_pass` (канон контракта 0.7.0+); гейт «строка 192» СНЯТ владельцем (D39.165).
|
||
|
||
**Не установлено, проверить при постановке:** строка **198** (апгрейд стирает замечания) — возможно, подразумевает
|
||
глагол пере-анонса или бэкфилла, потому что движок анонсирует юнит один раз за жизнь книги
|
||
(`backend/internal/pipeline/events.go:435`=`func unitOnceKey(bookID string, k unitWave) string`); направление фикса консилиум НЕ установил.
|
||
|
||
**Из чего двери НЕ следует** (экономия паков): **255** (бывшая **202**, закрыта D39.200) — потребитель всего набора, своих команд не
|
||
несёт · ~~**208**~~ · ~~**210**~~ (⚠ **испр. 10.09: обеих строк в живом трекере НЕТ, закрыты; довод «дверь из них не следует» остаётся, номера сняты**) · вайр-батч 182 ·
|
||
**164** — не команда, а починка: смета уже едет в `status --json`.
|
||
|
||
## Чего НЕ строить
|
||
|
||
- **Инбокс-каталог входящих команд** как зеркало `events.jsonl`. Все трассированные нужды садятся на
|
||
границы процессов; единственный мид-ран кандидат закрыт каноном (п.2); инбокс-файл возвращает
|
||
гонку, ради исключения которой существует flock.
|
||
⚠ **Эррата, которую ратифицирующая нота обязана поставить на `research/25`:** там «поднятие потолка файлом через
|
||
atomic rename» отнесено к носителям D39.106 — в теле D39.106 этого механизма НЕТ вовсе (проверять по ЖИВОМУ телу ноты **D39.106** («ШОВ: прогон переживает деплой платформы») в `architecture/05-decisions-log.md`, адресуя НОМЕРОМ, а не строкой; ⚠ в слайсе
|
||
`archive/architecture/05-decisions-D39-106-123.md` тела D39.106 НЕТ — греп по нему подтвердил бы эррату вакуумно). Строка была КАНДИДАТОМ и
|
||
никогда не ратифицировалась, а живой канон решил ось иначе (п.2).
|
||
|
||
- **Пере-открытие транспорта** (gRPC/сокет/брокер/пуш) — `research/25`, у каждой альтернативы 0
|
||
голосов из ~18, решение владельца принято.
|
||
- **Общий Go-пакет типов на две системы** — сварка независимых деплоев против D39.81/85; перевод
|
||
словаря на шве — сознательный контракт, а не недоделка.
|
||
- **Быстрый poll статуса** — `status` дорог по построению (пере-ингест книги), свежесть даёт журнал
|
||
событий; «status — это ремонт, а не поллинг» подтверждено вхолодную независимыми панелями.
|