Land the platform documentation sweep: the seam comment no longer argues from a premise the engine pack retired, and the battery gate count now lives in one place instead of three

This commit is contained in:
heaven 2026-08-29 13:15:32 +03:00
parent 9207c08717
commit 4a11df2d08
8 changed files with 190 additions and 46 deletions

View file

@ -7,12 +7,17 @@
`docs/platform-PROGRESS.md` (весь прогресс зоны здесь, решение владельца 04.08), стек —
`docs/STACK_DECISIONS.md`.
Батарея зоны: `make check` (build · vet · fmt · lint · test -race). Тесты со схемой требуют
`TM_PLATFORM_TEST_DSN` (как поднять Postgres без root — `docs/STACK_DECISIONS.md`); без него они
пропускаются, и `check` называет пропуски вслух. Живая проба рендера конфигурации требует второго
гейта — `TM_PLATFORM_TEST_ENGINE_BIN` + `TM_PLATFORM_TEST_BOOK_TEMPLATE`. `make vuln` и `make fuzz`
Батарея зоны: `make check` (build · vet · fmt · lint · test -race). `make vuln` и `make fuzz`
отдельными целями.
**Сколько у батареи условий — НЕ ЗДЕСЬ, и это правка 29.08.** Число жило в трёх местах сразу
(здесь — два, `docs/ENGINEERING_STANDARDS.md` — три, `docs/STACK_DECISIONS.md` — четыре), и это самый
дешёвый способ получить ложную приёмку: сессия, честно исполнившая §3.1 по этому файлу, объявит
«скипов ноль» при красном тесте, о котором её версия списка не знала. **Единственный носитель —
`docs/STACK_DECISIONS.md`, раздел «Гейты батареи»**; здесь его сознательно не дублируем. Коротко и
без числа: без переменных окружения батарея МОЛЧА пропускает около трёхсот тестов и остаётся
зелёной, поэтому «зелено» без сверки со списком условий не значит ничего.
**`TM_PLATFORM_LANGUAGE_PAIRS` — обязательная настройка деплоя** (форма `zh>ru,ja>ru:unavailable`):
её отвечает `GET /capabilities`, по ней же интейк отклоняет неподдерживаемую пару кодом
`unsupported_pair`. Какие пары существуют, решают ДАННЫЕ (пакет промптов оператора), а не список в Go.
@ -91,7 +96,11 @@ Prometheus; на контрактную поверхность они не вы
реконсилятор, который читает мир (Postgres · журнал книги · маркер выхода) и не ждёт процесса;
- читающая поверхность контракта — **есть (P7)**: дерево глав и пары с текстом, замечания, ЧТЕНИЕ
банка (⚠ ЗАПИСЬ в банк снята 22.08 вместе с пер-термной моделью подписи — `PD-370`, D39.144: подпись
это `resume`, а ручки «поправить/добавить термин» нет ни в зоне, ни в каноне), `GET /capabilities`, машинная модель ошибок (`code` + `request_id`), условные чтения
это `resume`. ⚠⚠ **А вот «ручки поправить/добавить термин нет» — УЖЕ НЕВЕРНО, и это правка 29.08:**
дверь `POST /books/{bookId}/bank/corrections` построена паком P9 и смонтирована — `httpapi/v0.go`
в `contractSurface`, обработчик `httpapi/bank.go`, канон `14-api-contract/openapi.yaml`, акты
D39.161/162/166, монтаж по `Capabilities.bank_corrections_enabled`. Снята была ПЕР-ТЕРМНАЯ ПОДПИСЬ,
а не правка термина, и предложение выше склеивало две разные вещи), `GET /capabilities`, машинная модель ошибок (`code` + `request_id`), условные чтения
(`ETag`/304) и сжатие JSON, `Idempotency-Key` на двух создающих вызовах;
- SSE-поток прогресса во фронт — **есть (P7)**: поток на КНИГЕ (не на прогоне), кадры минтит
писатель в свою транзакцию, клиент продолжает по `Last-Event-ID` (⚠ денежные суммы на провод и на
@ -117,9 +126,11 @@ Prometheus; на контрактную поверхность они не вы
| `internal/pricing`, `internal/money` | шкала глав и целые микро-доллары |
| `internal/metrics`, `internal/reqid`, `internal/jobs`, `internal/config`, `internal/gates` | телеметрия, id запроса, очередь, конфигурация, гейты тулчейна |
Три канала движка, и других нет: `tmctl manifest --json` (структура), `tmctl export --json --pairs`
(ТЕКСТ пар — единственный носитель), `<project_db>.bank.json` (банк), плюс `tmctl status --json` как
канал ремонта и `events.jsonl` как поток. Живой SQLite движка не читается никогда (D39.85).
Каналов движка ПЯТЬ, и других нет (⚠ до 29.08 фраза начиналась «три канала, и других нет» и тут же
перечисляла пять — счёт правился, а слово нет): `tmctl manifest --json` (структура),
`tmctl export --json --pairs` (ТЕКСТ пар — единственный носитель), `<project_db>.bank.json` (банк),
`tmctl status --json` (канал ремонта; с лендингом `6ec9f8a` он ещё и оценивает пере-проход ДО
покупки) и `events.jsonl` (поток). Живой SQLite движка не читается никогда (D39.85).
## Чего здесь НЕ будет

View file

@ -81,8 +81,27 @@ TM_PLATFORM_LANGUAGE_PAIRS=zh>ru
TM_PLATFORM_BOOKS_DIR=/srv/textmachine/books
TM_PLATFORM_BOOK_TEMPLATE=/srv/textmachine/book-template.yaml
TM_PLATFORM_METRICS_ADDR=127.0.0.1:9464
# ⚠⚠ ДОПИСАНО 29.08. БЕЗ ЭТИХ ЧЕТЫРЁХ ИНСТАНС НЕ ЗАПУСТИТ НИ ОДНОГО ПЕРЕВОДА, и это не
# «неполный пример», а окружение, при котором каждый ОПЛАЧЕННЫЙ прогон падает.
TM_PLATFORM_ENGINE_BIN=/opt/textmachine/engine/<версия>/tmctl # пусто = инстанс только читает
TM_PLATFORM_CTL_BIN=/opt/textmachine/bin/tmplatformctl # его зовёт юнит как ExecStopPost
TM_PLATFORM_STATE_DIR=/var/lib/tmplatform/state # маркеры выхода; ТОЛЬКО абсолютный
TM_PLATFORM_ENGINE_KEYS_PATH=/etc/tmplatform/engine-keys # KEY=VALUE, едет --keys-file
```
⚠ **Почему каждая из четырёх обязательна, а не желательна:**
- **`ENGINE_BIN`** пусто — инстанс объявляет себя читающей репликой и не спавнит ничего;
- **`CTL_BIN`** — путь, который юнит зовёт в `ExecStopPost`, чтобы записать маркер выхода; без него
прогон завершается, а платформа об этом не узнаёт никогда;
- **`STATE_DIR`** — каталог маркеров. ⚠ Дефолт лежит ВНЕ `ReadWritePaths=` юнита, а `deploy/`
ставит `ProtectSystem=strict`, поэтому без явного значения запись маркера запрещена файловой
системой, а не логикой;
- **`ENGINE_KEYS_PATH`** — ЕДИНСТВЕННЫЙ канал провайдерских ключей в движок (едет аргументом
`--keys-file`, мимо процесса платформы и мимо окружения юнита). Без него движок ищет `.env` рядом
с `book.yaml`, которого SaaS-путь не пишет, и каждый платный прогон падает `exit 10`
(«missing API keys»). На буте это WARN, а не отказ, — то есть тихо.
**`TM_PLATFORM_SIGNUP_GRANT_USD` здесь НЕТ намеренно** (PD-104, слово владельца 16.08): на бете
грант по умолчанию НОЛЬ и начисляется руками — `tmplatformctl grant`. Строка `=5` в этом блоке
включала обратно ровно тот самообслуживаемый безлимитный грант, который дефолт выключает.
@ -169,11 +188,15 @@ Read-only команды движка отказывают файлу проек
даёт `tmctl status --json` → exit 13 с этим токеном; `tmctl migrate --config <book.yaml>` делает
пред-миграционный бэкап и переводит v14 → v15; тот же `status` после неё — exit 0.
**Жёсткое правило: эмиттер-бинарь не выкатывается, пока `tmctl migrate` не заленден** ($0-команда
движка, открывающая файл на запись без прогона). До того новую платформу проверяют против журналов,
писанных своей сборкой движка вне репозитория.
**Правило БЫЛО: эмиттер-бинарь не выкатывается, пока `tmctl migrate` не заленден** ($0-команда
движка, открывающая файл на запись без прогона). ⚠⚠ **УСЛОВИЕ ВЫПОЛНЕНО — правка 29.08.** Глагол
существует и заленджен: `backend/cmd/tmctl/migrate.go`, в диспетчере `main.go` он стоит в списке
команд, и у него есть свой код выхода (`exitSchemaMismatch = 13`, «run `tmctl migrate`»). Правило
блокировало выкат по причине, которой больше нет, — и это худший род блокировки, потому что снаружи
он неотличим от осторожности. Порядок ниже действует; читать его как ДЕЙСТВУЮЩИЙ, а не как «когда
приедет».
Порядок, когда `migrate` приедет. ⚠ Ключевое: **`migrate` гоняется НОВЫМ бинарём** — старый уводит
Порядок (действующий). ⚠ Ключевое: **`migrate` гоняется НОВЫМ бинарём** — старый уводит
файл в свою же схему, то есть не делает ничего, и деадлок остаётся. Поэтому бинарь кладётся ДО
миграции, а `TM_PLATFORM_ENGINE_BIN` переключается ПОСЛЕ.

File diff suppressed because one or more lines are too long

View file

@ -60,7 +60,12 @@
## 3. Критерии приёмки сессии (Definition of Done)
1. `make check` зелёный офлайн; скипы названы вслух. ⚠ **«Скипов ноль» требует ТРЁХ условий, а не
1. `make check` зелёный офлайн; скипы названы вслух. ⚠ **СКОЛЬКО условий — смотреть в
`docs/STACK_DECISIONS.md`, раздел «Гейты батареи», он единственный носитель** (правка 29.08:
число жило в трёх файлах и они расходились — два здесь, три там, четыре в третьем, и сессия,
честно исполнившая этот пункт по устаревшей копии, объявляла приёмку выполненной при красном
тесте). Список ниже оставлен как ИСТОРИЯ поправки 22.08, а не как действующий счёт.
**«Скипов ноль» требует ТРЁХ условий, а не
одного** (испр. 22.08 — прежняя редакция называла только DSN и позволяла объявить приёмку
выполненной с молча пропущенной третью батареи): `TM_PLATFORM_TEST_DSN` (схема) ·
`TM_PLATFORM_TEST_ENGINE_BIN` + `TM_PLATFORM_TEST_BOOK_TEMPLATE` (живой рендер конфигурации) ·

View file

@ -155,7 +155,7 @@ hosted IdP (связка PII + доступность, а свою сессию
| Добавление | Пин | Статус |
|---|---|---|
| OIDC-вход | x/oauth2 v0.36.0 + go-oidc/v3 v3.20.0 | ратифицировано (§1) |
| Кодоген сервера из ратифицированной спеки OpenAPI 3.1 | `oapi-codegen/v2` v2.8.0 (17.07.2026) | **ВЗЯТЬ — доказано исполнением 05.08** (ниже); фолбэк overlay 3.1→3.0 не понадобился. ⚠ **НЕ ИСПОЛНЕНО на 14.08** — см. баннер статуса выше |
| Кодоген сервера из ратифицированной спеки OpenAPI 3.1 | `oapi-codegen/v2` v2.8.0 (17.07.2026) | **НЕ БРАТЬ — решение P7, пере-подписано `D39.132` п.2б** (баннер выше несёт довод). ⚠ **Правка 29.08:** в этой клетке стояло «**ВЗЯТЬ** — доказано исполнением 05.08», и это ПРОТИВОРЕЧИЛО баннеру того же файла, который говорит «Решение P7 по обоим — НЕ БРАТЬ». Замер 05.08 остаётся верным и лежит ниже — он доказал, что инструмент РАБОТАЕТ, а не что его берут; слово вердикта было прочитано из замера. Сегодняшний статус: КАНДИДАТ, решение за паком, который его возьмёт |
| ~~`sqlc` для денежных/квотных таблиц~~`sqlc` для поверхности контрактных ручек и read-model | v1.31.1 | **ПЕРЕСМОТРЕНО приёмкой P1 (05.08), доказано исполнением** — см. абзац ниже. Денежный пакет остаётся рукописным. ⚠ **НЕ ИСПОЛНЕНО на 14.08**, срок «до первого хендлера» пройден — см. баннер статуса выше (PD-44) |
| `golang.org/x/time/rate` | v0.15.0 | лимиты в процессе; долговечные пер-пользовательские — в Postgres |

View file

@ -552,9 +552,9 @@ export TM_PLATFORM_TEST_DSN='postgres://postgres@/postgres?host=/tmp&port=55432&
- **Правил миграцию — пере-создай базу стенда** (`dropdb tmstand && createdb tmstand`): goose
ключуется НОМЕРОМ и правку уже применённого файла не видит (то же правило, что в `deploy/README.md`).
После пере-создания базы старый cookie-jar мёртв — повторить `/auth/dev-login`.
- **Красный `runner.TestARunIsBoundedByItsOwnCgroup` — обычно НЕ регрессия кода:** у `tm.slice`
- **Красный `runner.TestARunIsBoundedByItsOwnCgroup` — обычно НЕ регрессия кода:** у `tm-runs.slice`
опустел `cgroup.subtree_control`. Лечение без root: `echo "+memory +pids" >
/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/tm.slice/cgroup.subtree_control`.
/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/tm-runs.slice/cgroup.subtree_control`.
- **Демон нельзя убивать `pkill -f <путь к бинарю>`** — шаблон совпадает с собственной командной
строкой оболочки, и она убивает сама себя (exit 144). Убивать по PID.
- **Postgres стенда переживает не всё:** сокет живёт в `/tmp`, и уборка `/tmp` (или smart shutdown)

View file

@ -4,6 +4,88 @@
> вопросы, предложения на ратификацию. В `docs/PROGRESS.md` платформа не пишет; оркестратор
> читает этот журнал при каждом лендинге зоны (свип «решений владельца» — норма D39.99 п.4).
## ПОСЛЕ ЛЕНДИНГА (2): аудит документации зоны — семь позиций разобраны, комментарий шва исправлен (сессия `textmachine-c0`, 29.08)
Оркестратор передал список аудита доков по моей зоне («не заказ, а список»). Разобрала все семь,
каждую сперва ПРОВЕРИЛА в дереве. Ни одна не отвергнута — все подтвердились.
**Комментарий шва (`PD-427`) — ИСПРАВЛЕН, оркестратор дал «правь».** В `internal/ingest/resync.go`
теперь сказано, что снята и ПОСЫЛКА (слепое окно закрыто лендингом `6ec9f8a`), и ПРЕДСКАЗАНИЕ (нового
движкового глагола не будет, починили существующий `status`), и названа причина, по которой пара не
берётся СЕГОДНЯ — гейт проводки вместе с `--max-units`. Вторая половина важнее первой, и довод его:
неверный ДОВОД сессия перепроверит, неверное ОЖИДАНИЕ она примет как карту.
**⛔ Что было ложью в доках зоны:**
1. `README.md` объявлял, что «ручки поправить/добавить термин нет ни в зоне, ни в каноне» — дверь
`POST /books/{bookId}/bank/corrections` построена паком P9 и смонтирована. Предложение склеивало
снятую ПЕР-ТЕРМНУЮ ПОДПИСЬ с правкой термина. Исправлено.
2. Там же «Три канала движка, и других нет» — и тут же перечислялись ПЯТЬ. Счёт правился, слово нет.
3. **Число условий батареи жило в ТРЁХ файлах и они расходились** (README — два, `ENGINEERING_STANDARDS` — три,
`STACK_DECISIONS` — четыре). Это самый дешёвый способ получить ложную приёмку: сессия, честно
исполнившая §3.1 по устаревшей копии, объявит «скипов ноль» при красном тесте. **Носитель теперь
ОДИН** — `STACK_DECISIONS`, «Гейты батареи»; две другие копии заменены ссылкой на него.
4. `STACK_DECISIONS` в лечебной команде самой частой грабли стенда указывал на несуществующий срез
`tm.slice` (в коде `runner.go``tm-runs.slice`), причём двумя абзацами выше тот же файл писал
правильное имя. Оператор получал «No such file or directory».
5. `deploy/README.md` держал жёсткое правило «эмиттер не выкатывается, пока `tmctl migrate` не
заленден». Глагол существует и заленджен (`backend/cmd/tmctl/migrate.go`, свой код выхода 13).
Правило блокировало выкат по несуществующей причине — худший род блокировки, снаружи неотличимый
от осторожности.
6. **`deploy/README.md` показывал боевой env, при котором инстанс не запустит НИ ОДНОГО перевода:**
нет `ENGINE_BIN`, `CTL_BIN`, `STATE_DIR` (дефолт вне `ReadWritePaths=` при `ProtectSystem=strict`)
и `ENGINE_KEYS_PATH` — единственного канала ключей. Каждый ОПЛАЧЕННЫЙ прогон падал бы `exit 10`, а
на буте это WARN, то есть тихо. Дописаны все четыре, с объяснением, почему каждая обязательна.
7. `PLATFORM_DIRECTION.md` нёс в таблице «кодоген — **ВЗЯТЬ**, доказано 05.08» при баннере того же
файла «решение P7 по обоим — НЕ БРАТЬ». Замер 05.08 верен и остаётся: он доказал, что инструмент
РАБОТАЕТ, а не что его берут. Слово вердикта было прочитано из замера.
**⛔ И самое неприятное — в РЕГИСТРЕ, и половина этого моя.** Секция и статус разошлись у десяти
строк: девять `fixed` и один `accepted-risk` лежали под заголовками «Открытые», то есть человек,
читающий открытый список, видел закрытую работу. Встречно и хуже — **`PD-424` и `PD-425`, оба
`major`, лежали в секции «Открытые — info»**, и `PD-425` денежный. Это МОЯ ошибка: я вставляла все
новые строки к одному якорю, не сверяя вес. Перенесены все; закрытые — в новую секцию «перенос по
секциям», где сказано, что статуса они не меняли. ⚠ Счёт по статусам от переноса не изменился
(`counts.py` ключуется формой строки, а не секцией) — изменилось то, что видит читатель.
**Своя ошибка счёта, в третий раз за сутки:** мой разбор дал 19 «неуместных» строк против десяти у
аудита. Прав был аудит: три строки (`PD-115`, `PD-407`, `PD-122`) несут статус `open (…)` с
оговоркой в скобках, и мой парсер прочёл его как не-`open`. Тот же класс, что и раньше: вывод из
формы вместо чтения.
**Слайсы регистра (444 КБ) НЕ трогала** и предупреждаю о том же, о чём предупредил аудит:
`docs/scripts/counts.py` держит регистр ОДНИМ путём и слайсы не глобит, поэтому вынос молча уронит
счёт 428 → 284. Скрипт в зоне оркестратора; браться за слайсы — только после того, как он научится
глобить.
Гейты после всего: форма регистра зелёная (**428 строк, 107 open, 7 major**), битых якорей в зоне 0,
`go build` и `go vet` чисты.
## ПОСЛЕ ЛЕНДИНГА: движковый пак снял посылку одного из решений зоны — заведено двумя строками (сессия `textmachine-c0`, 29.08)
P11 залендён (`e548e5a`, канон 0.8.0), следом лёг движковый пак «деньги» (`6ec9f8a`, D39.170) — и он
**опроверг довод, на котором стоит комментарий шва в моей зоне**. Пришло пингом оркестратора,
проверено мной чтением ОБЕИХ сторон, а не принято на слово.
**`PD-427` — комментарий несёт снятую посылку.** `internal/ingest/resync.go:37-43` объясняет, почему
платформа сознательно НЕ берёт `rebill_units`/`rebill_usd`: «status проецирует СОХРАНЁННУЮ память, и
сразу после `bank-apply` честно читает ноль». Движковый пак починил ровно это — `foldMemoryForRead`
стал ПЕРВЫМ ответом читающего пути, `projectStoredMemory` понижена до фолбэка, и комментарий движка
это объявляет дословно («IT IS NO LONGER THE READ PATH'S FIRST ANSWER»). ⚠ Комментарий неверен
ДВАЖДЫ: он ещё и предсказывает, что пара вернётся с НОВЫМ движковым глаголом, — а нового глагола не
появилось, починили существующий `status`.
**Проводку полей строка НЕ открывает** — она гейчена вместе с `--max-units`, и тот гейт в силе
(слово оркестратора). Предмет строки — ровно устаревший ДОВОД.
⚠ Правку самого комментария я НЕ делаю: оркестратор при передаче сказал «чинить прямо сейчас не
надо», а дерево только что залендено. Текст правки предложен ему пингом — решение его.
**`PD-428` — калибровка цены, не дефект.** Замер движкового охотника: терминолог переигрывается на
КАЖДОЙ покупке ЦЕЛИКОМ по книге (три покупки по одному юниту — три полнокнижных консолидации по
$0.005460). Накладные масштабируются КНИГОЙ, а не грантом. Сегодня беспредметно, потому что продажа
идёт главами; строка существует, чтобы факт не потерялся к появлению мелкой нарезки, при которой
накладные обгонят полезную работу на самых дешёвых покупках.
Регистр после: **428 строк, 107 open, 7 major**, форма зелёная, битых якорей в зоне 0.
## ПАК P11 ОТРАБОТАН — отзыв сессии стал действием, застрявший расчёт стал виден и управляем; 7 строк из 7 (сессия платформы `textmachine-c0`, 29.08, промт `docs/PLATFORM_P11_SESSION_PROMPT.md`)
Записка-план — следующей шапкой ниже. Здесь: числа С КОМАНДАМИ · что доказано живьём · таблица
@ -365,8 +447,7 @@ P11», каждая с телом (чем закрыта · чем доказа
дефект в `sqlgate_test`), `PD-417` (цена широких выборок) — все четыре сразу `fixed` этим паком; и
`PD-418``PD-422` — открытые (settling-строка живого прогона без ручки · слепота гейта миграций к
пере-подписи · два теста, не выдерживающих параллельных батарей · остаток `PD-379` по подметанию ·
условный `--resnapshot`). Гейт формы: `python3 docs/scripts/counts.py --check` → **422 строки,
`open` 101, `major` 5** (было 103 и 7), битая форма пустая, хвост вне словаря пустой.
условный `--resnapshot`). Гейт формы: `python3 docs/scripts/counts.py --check`**428 строк, `open` 107, `major` 7** — счёт на момент сдачи был 422/101/5, и вырос от строк, заведённых уже ПОСЛЕ лендинга (`PD-423``PD-428`), битая форма пустая, хвост вне словаря пустой.
⚠ Отдельно: `PD-159` этот пак НЕ пере-открывает. Она стоит `fixed` с токеном `ОСПОРЕНО(PD-376)`
(D39.159 §5), и теперь пробел, который несла `PD-376`, закрыт пином — то есть двусторонняя ссылка
осталась целой, а спор разрешён в пользу строки: пин был нужен, мутация проходила батарею.

View file

@ -34,13 +34,28 @@ type StatusReport struct {
ETASeconds float64 `json:"eta_seconds"`
// UnsignedBankTerms backs the signing screen's "N of M decided" while a stop is standing.
UnsignedBankTerms int `json:"unsigned_bank_terms"`
// The engine's re-bill projection (rebill_units/rebill_usd) is deliberately NOT taken, and the
// reason is not vocabulary but TIMING: status projects the STORED memory, and a correction
// reaches it only when the next translate folds the bank in — so right after an apply, the one
// moment a consent would want the figure, it honestly reads zero (P10 errata 28.08-к). The
// consents are funded from the run's own hold instead, and a field without a consumer is the
// defect class this project names — the pair returns with the engine verb that can fold and
// price a correction OUTSIDE a run (orchestrator's backlog row).
// The engine's re-bill projection (rebill_units/rebill_usd) is deliberately NOT taken — but the
// REASON changed under this comment, and both halves of what it used to say are now retired
// (register row PD-427).
//
// ⚠ The old reason was TIMING, and it was true when it was written: status projected the STORED
// memory, so right after a `bank-apply` — the one moment a consent would want the figure — it
// honestly read zero (P10 errata 28.08-к). That blind window is CLOSED: the engine's read path
// now folds the bank first and falls back to the stored glossary only if it cannot
// (`pipeline.projectStoredMemory`, "IT IS NO LONGER THE READ PATH'S FIRST ANSWER", landing
// 6ec9f8a / D39.170). `status` will price a re-pass BEFORE it is bought, and still writes nothing.
//
// ⚠ The old comment also predicted the SHAPE of the fix — "the pair returns with the engine verb
// that can fold and price a correction outside a run" — and that is the half worth correcting
// loudest, because a wrong REASON gets re-checked while a wrong EXPECTATION gets used as a map.
// No such verb was built and none is planned: the existing `status` was fixed instead.
//
// Why the pair is still not taken TODAY: its wiring is gated together with the engine's
// `translate --max-units`, which waits on a decision of its own (the volume ceiling was broken on
// a book that mines its bank; the cure landed, the wiring did not). So the figure exists and the
// seam simply does not carry it yet — which is a different sentence from the one this comment
// used to make, and the difference is what a later session would otherwise design against.
// The consents are meanwhile funded from the run's own hold.
// Spend is the engine's committed spend, converted to integer micro-USD AT THE SEAM. The wire
// value is a JSON decimal; binding it to a float64 would put drift one step before the integer
// column that exists to prevent drift (PD-15). It is stored in the credit tables and NEVER