Widen the blocked reason on the wire so the order form stops promising a start admission will refuse

This commit is contained in:
heaven 2026-09-11 03:40:06 +03:00
parent 0fcd8d8707
commit ba8542a5b9
5 changed files with 62 additions and 12 deletions

View file

@ -1,6 +1,6 @@
# Журнал прогресса
> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-11, голова D39.243КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №23** (единственный носитель — здесь; роль передана 06.09, №22 закрыт нотой передачи D39.217; ⚠ испр. 06.09: коммит передачи `c992ee5` бампнул голову и НЕ тронул номер — гейта на номер очереди нет вовсе, `counts.py` сверяет только голову, поэтому носитель разошёлся молча). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ИСПОЛНЕН, испр. 08.09 (висел как «первым» неделю после исполнения):** (0а) ревизия документации на протухшее ОТРАБОТАНА 01.09 воркфлоу `docs-staleness-revision-A` (15 срезов), провенанс находок — `D39.185`; (0б) планы доработок в бэкенд и платформу — исполняются ПАКАМИ, за 0708.09 закрыты два движковых (`D39.225`, `D39.226`); (0в) вынос неактуального в архив идёт батчами `DOC_CLEANUP_PLAN.md` (Б14/Б15/Б17 живы). ⇒ строка ниже — не заказ, а история: (0а) ревизия документации на ПРОТУХШЕЕ — по всем зонам; (0б) планы доработок в БЭКЕНД и ПЛАТФОРМУ; (0в) вынос неактуального в АРХИВ (`docs/archive/`, `platform/docs/archive/`) — за сутки 31.08 закрыто много, и часть носителей стала историей. ⚠ Трезвость по масштабу (пере-считано 02.09): **94** живых дока в `docs/` (пере-счёт 04.09: `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`), 8 в `platform/docs`, 9 в `backend/docs` — ⚠ из 94 ВРЕМЕННЫЙ остался ОДИН (`DOC_CLEANUP_PLAN`, живой до закрытия батчей Б14/Б15/Б17); два прежних временных уехали в `archive/reports/` 02.09; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 109 (major 1), всего рядов 465 (пере-счёт `python3 docs/scripts/counts.py`; с 04.09 оба числа под гардом `--check`, прежние 95/3 разошлись молча) ⚠ (ревизией 02.09 ряды **154** и **157** переведены из «скоро» в «когда-нибудь»: их гейтом стоял первый холодный прогон, он ОТРАБОТАЛ 31.08 и оба предусловия оказались другими — разбор в самих ячейках, ни одна НЕ закрыта). ⚠ Числа доков `counts.py` НЕ сторожит — при переносе файлов пере-считывать руками командой `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`. ⚠ И предупреждение о МЕТОДЕ, купленное сменой №21: «выглядит протухшим» ≠ «протухло». Три факта оркестратора опровергнуты ЗАМЕРОМ сессий, а якоря `15-money-path.md` в девяти случаях из двенадцати РОДИЛИСЬ верными и сгнили дрейфом — то есть ревизия обязана быть исполнением, а не чтением. ⚠ **ОЧЕРЕДЬ, унаследованная от №21** (три лендинга 31.08 — секция «СОСТОЯНИЕ ПАКОВ» ниже): **(1) ~~РАЗРЫВ ЦИКЛА~~ ЗАМКНУТ ЖИВЬЁМ 04.09** — пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0 (книга `bk_SS5VES2JELESJSTR`, потрачено $0.278319 из гранта $0.60 при потолке пака $1.5, санкция D39.189). Дверь выдачи построена и проверена исполнением: `202`+`Location`, поллинг с `Retry-After`, Range 206 · второй клиент 200 · аноним 401 · чужая книга 404, TTL с GC, идемпотентность третьего создающего вызова. Все ТРИ сценария строки 216 предъявлены живьём (подпись банка · halt на потолке с exit 4 · `409 run_not_resumable/ceiling_reached` и лечение новым прогоном).
> **⟶ ТЕКУЩЕЕ СОСТОЯНИЕ** (на 2026-09-11, голова D39.244КУРС: движок и платформа до «работает и отдаёт результат», фронт ЗАМОРОЖЕН и P7 его НЕ размораживает (D39.147). **ОЧЕРЕДЬ №23** (единственный носитель — здесь; роль передана 06.09, №22 закрыт нотой передачи D39.217; ⚠ испр. 06.09: коммит передачи `c992ee5` бампнул голову и НЕ тронул номер — гейта на номер очереди нет вовсе, `counts.py` сверяет только голову, поэтому носитель разошёлся молча). ⚠ **ЗАКАЗ ВЛАДЕЛЬЦА 01.09 — ИСПОЛНЕН, испр. 08.09 (висел как «первым» неделю после исполнения):** (0а) ревизия документации на протухшее ОТРАБОТАНА 01.09 воркфлоу `docs-staleness-revision-A` (15 срезов), провенанс находок — `D39.185`; (0б) планы доработок в бэкенд и платформу — исполняются ПАКАМИ, за 0708.09 закрыты два движковых (`D39.225`, `D39.226`); (0в) вынос неактуального в архив идёт батчами `DOC_CLEANUP_PLAN.md` (Б14/Б15/Б17 живы). ⇒ строка ниже — не заказ, а история: (0а) ревизия документации на ПРОТУХШЕЕ — по всем зонам; (0б) планы доработок в БЭКЕНД и ПЛАТФОРМУ; (0в) вынос неактуального в АРХИВ (`docs/archive/`, `platform/docs/archive/`) — за сутки 31.08 закрыто много, и часть носителей стала историей. ⚠ Трезвость по масштабу (пере-считано 02.09): **94** живых дока в `docs/` (пере-счёт 04.09: `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`), 8 в `platform/docs`, 9 в `backend/docs` — ⚠ из 94 ВРЕМЕННЫЙ остался ОДИН (`DOC_CLEANUP_PLAN`, живой до закрытия батчей Б14/Б15/Б17); два прежних временных уехали в `archive/reports/` 02.09; счёт бэклога — бюллетенем ниже, открытых рядов регистра платформы — 109 (major 1), всего рядов 465 (пере-счёт `python3 docs/scripts/counts.py`; с 04.09 оба числа под гардом `--check`, прежние 95/3 разошлись молча) ⚠ (ревизией 02.09 ряды **154** и **157** переведены из «скоро» в «когда-нибудь»: их гейтом стоял первый холодный прогон, он ОТРАБОТАЛ 31.08 и оба предусловия оказались другими — разбор в самих ячейках, ни одна НЕ закрыта). ⚠ Числа доков `counts.py` НЕ сторожит — при переносе файлов пере-считывать руками командой `find docs -name '*.md' -not -path 'docs/archive/*' | wc -l`. ⚠ И предупреждение о МЕТОДЕ, купленное сменой №21: «выглядит протухшим» ≠ «протухло». Три факта оркестратора опровергнуты ЗАМЕРОМ сессий, а якоря `15-money-path.md` в девяти случаях из двенадцати РОДИЛИСЬ верными и сгнили дрейфом — то есть ревизия обязана быть исполнением, а не чтением. ⚠ **ОЧЕРЕДЬ, унаследованная от №21** (три лендинга 31.08 — секция «СОСТОЯНИЕ ПАКОВ» ниже): **(1) ~~РАЗРЫВ ЦИКЛА~~ ЗАМКНУТ ЖИВЬЁМ 04.09** — пользователь получил EPUB настоящего ПЛАТНОГО перевода ЧЕРЕЗ API, `epubcheck` 5.3.0 на СКАЧАННОМ файле 0/0/0/0 (книга `bk_SS5VES2JELESJSTR`, потрачено $0.278319 из гранта $0.60 при потолке пака $1.5, санкция D39.189). Дверь выдачи построена и проверена исполнением: `202`+`Location`, поллинг с `Retry-After`, Range 206 · второй клиент 200 · аноним 401 · чужая книга 404, TTL с GC, идемпотентность третьего создающего вызова. Все ТРИ сценария строки 216 предъявлены живьём (подпись банка · halt на потолке с exit 4 · `409 run_not_resumable/ceiling_reached` и лечение новым прогоном).
> ⛔ **ПЯТЬ ПОТОКОВ РАБОТЫ — состояние на 07.09 (пере-снято лендингами смены №23).**
> **(1) ПОЛИГОН — «ремонт прибора»** (`docs/POLYGON_INSTRUMENT_REPAIR_SESSION_PROMPT.md`, строки 319 · 300 · 265 · 143):
> ⚠ **ЕДИНСТВЕННЫЙ ПОТОК, НЕ СДВИНУВШИЙСЯ ЗА СМЕНУ — сессия по промту так и не стартовала.** Блокирующая линза

View file

@ -1,4 +1,4 @@
# Реестр D-нот — карта актуальности v2 (D1D39.243; титул — носитель головы, бампать при каждом аппенде)
# Реестр D-нот — карта актуальности v2 (D1D39.244; титул — носитель головы, бампать при каждом аппенде)
> ⚠ **Колонку «тело» `counts.py --check` НЕ сторожит по устройству:** он сверяет полноту НОМЕРОВ, а не
> место тела, поэтому колонка держится дисциплиной лендинга. Не нашёл тело по колонке — иди в слайсы,
@ -302,3 +302,4 @@
| D39.241 | 11.09 | **АКТ ЗАКРЫТИЯ обеих сессий пака «две остановки».** Движок ОТКАЧЕН полностью (18 путей + 6 файлов; батарея вернулась к входной строке: 19 `ok` · 0 FAIL · 4 скипа). ⛔ Откат не смогла сделать НИ ОДНА сессия, включая оркестраторскую — запрет сработал одинаково, обходить его не стала ни одна, дерево снял владелец одной командой. Платформа сдала `--no-block` **с пином**: три мутации засчитаны по ТЕКСТУ, живой пин ×5 зелёный, argv-пин отдельно — чтобы флаг не остался непроверенным на хосте без systemd. ⭐ Куплен пин на ЧУЖОЕ поведение: «повторный стоп бесплатен и идемпотентен» держалось на недокументированном systemd. От отменённого пака осталось 7 нот и 23 строки трекера; одна МОЯ строка (399) была ложной и пере-написана до заказа | приёмка · стоп · метод |
| D39.242 | 11.09 | **АКТ: доработка по инварианту `D39.240` ПРИНЯТА обеими зонами** (`3084766`+`f8a968e` движок, `560ca20`+`00d590e` платформа). Батарея вернулась к входной строке, мутации **152 · RED 151 · SURVIVED 0 · ROTTED 1**, после починки якоря сверка 373 якорей — протухших 0. ⛔ Подтверждающего перепрогона «152 одной строкой» НЕТ — дважды убит сторожем памяти, третьей попытки не делали ОСОЗНАННО (косметика, ни одной новой проверки). ⭐ Почти всё дорогое нашли САМИ сессии и у СЕБЯ: выживший мутант вскрыл дефект в собственном пине · непримени́вшаяся собственная правка · знаменатель 8 сайтов против моих 2 · три класса вырожденности за заход · три опровержения СВОИХ находок. ⚠ И ПЯТЬ моих ошибок, снятых сессиями, — все названы в п.6, четыре нормы куплены | приёмка · стоп · метод |
| D39.243 | 11.09 | **АУДИТ БАНКА ПАМЯТИ против приоритета №1 владельца** (`research/34`: банк держит инъекцию, но КНИЖНОГО вопроса не задаёт ни один прибор ⇒ приоритет непроверяем; строки **406410**) · полигонный слот сведён к ОДНОМУ промту (два припаркованы баннерами) · два пака выданы после опровергателей, шесть классов их находок · ⛔ санкция владельца «со слов» носителем не является · три мои ошибки о прогоне A и его деньги ДВУМЯ числами · эррата к атрибуции `D39.108` п.1 | процесс/качество |
| D39.244 | 11.09 | **КОНТРАКТНЫЙ МИНОР 0.14.0**`Blocked.code` получает `run_in_flight`: форма заказа перестаёт обещать старт, в котором допуск откажет из-за уже идущего прогона ЭТОЙ книги (`PD-455`). `verdict` не трогается — он закрытый enum и правда про ДЕНЬГИ; расширен смысл `Blocked`. Форма выведена зоной, ратифицирована ДО стройки; провенанс — §2.25 README канона | контракт |

View file

@ -1,4 +1,4 @@
# Журнал решений оркестратора — контракт D1D39.243 (живой файл: карта · эрраты · живые тела · голова D39.124+ (подрезка D39.139); тела закрытых эр — в слайсах `docs/archive/architecture/`, указатель ниже; реестр всех нот — `05-decisions-index.md`)
# Журнал решений оркестратора — контракт D1D39.244 (живой файл: карта · эрраты · живые тела · голова D39.124+ (подрезка D39.139); тела закрытых эр — в слайсах `docs/archive/architecture/`, указатель ниже; реестр всех нот — `05-decisions-index.md`)
> **КАРТА АКТУАЛЬНОСТИ (ревизия D31, продлена до D38.2 [12.07]; исторические записи ниже НЕ переписываются — дисциплина D23.3).** Работая с контрактом (греп номера: живой файл → слайсы, целиком НЕ читать — D39.125), держи под рукой, что чем перекрыто:
> ⚠ **Эррата 09.08 (D39.125):** D39.111 п.1 предписывал промту S3 «максимум = баланс МИНУС открытые холды» — формула ОШИБОЧНА (вычитание дважды), исправлена D39.115 п.2(а): максимум = Balance КАК ЕСТЬ; тело D39.111 — в слайсе `../archive/architecture/05-decisions-D39-106-123.md` (испр. 05.09: прежнее «живёт ниже в этом файле» протухло подрезкой D39.139) (голова D39.106+).
@ -3476,3 +3476,15 @@ bought NOTHING». ⇒ **`tmctl manifest` есть НИЖНЯЯ граница,
**7. ЭРРАТА К РОЛЕВОМУ ПРОМТУ.** Норма «не влезает в бюджет — стоп и пинг» приписана в нём `D39.108` п.1; в теле ноты этой формулировки НЕТ (нота — приёмка экспов 18/19/20). Норма верна, атрибуция снята, носитель — сам ролевой промт.
**8. ⛔ И ПЕРЕ-ОПРЕДЕЛЁН ПРЕДМЕТ «ВХОДА», который я числил дырой из трёх частей.** Замер: пригласительного гейта действительно нет, но грант новичку 0 — РЕШЕНИЕ владельца (`D39.176` п.1), а байтовый потолок загрузки ПОСТРОЕН (64 МиБ на маршруте интейка). Живой остаток — операционная гигиена, и та же нота называет её инженерной; её гейт — открытая регистрация, которой в закрытой бете нет, поэтому из платформенного пака она снята подписанным пропуском.
## D39.244 — КОНТРАКТНЫЙ МИНОР 0.14.0: `Blocked.code` получает `run_in_flight`; форма выведена платформенной сессией и ратифицирована ДО стройки (11.09, оркестратор №23). ✅
**Основание.** Контрактный пинг платформенной сессии по паку «ПРАВДА У ДВЕРИ» (`PD-455`): форма заказа отвечает «хватает на всю книгу» над книгой, по которой уже идёт прогон, и молчит о том, что клик получит отказ. Промт задавал точку роста (`Blocked.code`, не `verdict`), сессия вывела форму и прислала её мне — правка канона моя, не её.
**1. Что решено.** `Blocked.code` получает второе значение **`run_in_flight`**. `verdict` НЕ трогается: он остаётся закрытым enum из трёх значений и правдой ПРО ДЕНЬГИ; клиент читает `verdict` и `blocked` вместе. Вместе со значением расширен СМЫСЛ схемы: было «что держит шкалу КОРОЧЕ», стало «почему старт не предлагается так, как обещал бы один баланс» — с двумя формами внутри (шкала короче доступного · старт невозможен вовсе). `book_id` при `run_in_flight` — ЭТА ЖЕ книга: место, где пользователь может действовать, — карточка её собственного прогона.
**2. Почему не четвёртое значение `verdict`.** Он ЗАКРЫТЫЙ, и новое значение ломает генерённых клиентов. `Blocked.code` — предавторизованная точка роста: его собственное описание требует от клиента терпеть неизвестное значение под минором. Тот же класс, что купил `D39.226`: расширяемость живёт не там, где кажется, и её спрашивают у канона ПРЕЖДЕ, чем заводят новый член.
**3. Порядок — форма ратифицирована ДО стройки** (как `D39.235` п.6 для кадра остановки). Иначе сессия зоны либо ставит константу версии и красит собственный гейт (`gates.TestTheAnnouncedContractVersionIsTheOneTheCanonRatified` читает канон ВЫШЕ корня модуля), либо строит механизм, объявляя старую версию. Константа `httpapi.ContractVersion` уезжает ТЕМ ЖЕ лендингом, что и код зоны.
**4. Провенанс и носители.** `docs/architecture/14-api-contract/openapi.yaml` (`version: 0.14.0`, схема `Blocked`) + §2.25 README канона. ⚠ Байт-зеркало фронта не синкается: на время фриза зоны его отставание ратифицировано (`D39.142` п.5).

View file

@ -3,7 +3,7 @@
батч 0.3.0 — D39.138, 16.08.2026, оркестратор №17;
синк с платформой 0.4.0 — 20.08.2026, контрактная сессия; РАТИФИЦИРОВАН D39.152, 20.08.2026, оркестратор №18)
СТАТУС: РАТИФИЦИРОВАН как контракт API v0 по **0.13.1** включительно (0.13.1 — `D39.235` п.6: корректирующий минор, `Run.required` требовал отставленный член) (0.11.0 — D39.208 · 0.12.0 — D39.211 · **0.13.0 — D39.221: четыре исхода `POST /v0/books` после синхронного разреза на приёме**; перечень миноров и их
СТАТУС: РАТИФИЦИРОВАН как контракт API v0 по **0.14.0** включительно (0.14.0 — `D39.244`: `Blocked.code` получает `run_in_flight`, форма выведена платформенной сессией и ратифицирована ДО стройки — провенанс §2.25; 0.13.1 — `D39.235` п.6: корректирующий минор, `Run.required` требовал отставленный член) (0.11.0 — D39.208 · 0.12.0 — D39.211 · **0.13.0 — D39.221: четыре исхода `POST /v0/books` после синхронного разреза на приёме**; перечень миноров и их
провенанс — ниже по файлу, здесь НЕ дублируется). Нормативная поверхность — openapi.yaml РЯДОМ.
⚠ Зонная копия frontend/docs/api-contract/ ВРЕМЕННО ОТСТАЁТ (0.2.3 при каноне
0.13.0 — ОДИННАДЦАТЬ миноров) — ратифицировано D39.142 п.5 на время фриза фронта; синк байт-в-байт + перегенерация типов =
@ -57,7 +57,12 @@ cmp-сверка обязательна (D39.138 п.3).
> капабилити, которой никто не строил; третий адрес двери получает `operationId` `downloadExport`
> до этого единственная поверхность, по которой браузер НАВИГИРУЕТ, жила в каноне только прозой;
> шестнадцатая причина замечания `off_target_lang` получает код `wrong_language` (Приложение А).
> Провенанс — §2.23**.
> Провенанс — §2.23** ·
> **0.14.0 (D39.244, 11.09; ратифицирован ДО стройки, форма выведена платформенной сессией) —
> `Blocked.code` получает второе значение `run_in_flight`: форма заказа перестаёт обещать старт,
> в котором допуск откажет из-за уже идущего прогона ЭТОЙ книги. `verdict` НЕ трогается — он остаётся
> правдой про деньги, и четвёртого значения у него нет; расширяется смысл `Blocked`, потому что
> живой прогон шкалу не укорачивает, а делает старт невозможным. Провенанс — §2.25**.
> Дом канона — этот каталог;
> `frontend/docs/api-contract/openapi.yaml` — байт-зеркало ⚠ НА ФРИЗЕ ЗОНЫ РАВЕНСТВО ПРИОСТАНОВЛЕНО (D39.142 п.5): зеркало 0.2.3, канон **0.13.0** (⚠ число испр. 10.09 — здесь стояло 0.10.0, отставшее на два минора: 0.12.0 — `D39.211`, 0.13.0 — `D39.221`; шапка файла и `openapi.yaml` всё это время несли верное). Прежний текст: канон 0.10.0.
>
@ -952,6 +957,29 @@ first built format»), и условие наступило. Набор — то
**Шестнадцатая причина замечания.** `off_target_lang``wrong_language`; разбор и ⚠ о ранге — в
Приложении А.
### 2.25. `run_in_flight` — форма заказа перестаёт обещать то, в чём дверь откажет (0.14.0) — ✓ форма выведена зоной, ратифицирована ДО стройки
**Дефект (`PD-455`).** Вердикт формы считается из остатка и баланса и про ЖИВОЙ прогон по книге не
спрашивает; допуск же вторую покупку отвергает. Человек видит «хватает на всю книгу», жмёт и получает отказ,
причину которого форма знала до клика. Денег это не теряет — отказ случается ДО взятия холда, — ломается
ровно обещание формы «вердикт ДО клика», ради которого пак и писался.
**Почему НЕ четвёртое значение `verdict`.** `verdict` — ЗАКРЫТЫЙ enum (`covers_all` · `covers_part` ·
`covers_none`), и новое значение в нём ломает генерённых клиентов. `Blocked.code` — наоборот,
ПРЕДАВТОРИЗОВАННАЯ точка роста: его собственное описание требует от клиента терпеть неизвестное значение под
минором. Это тот же урок, что купил `D39.226` про корневой `code` против `cause.code`: расширяемость живёт
не там, где кажется, и её надо спросить у канона прежде, чем заводить новый член.
**Что расширено вместе со значением.** Смысл `blocked` был «почему шкала КОРОЧЕ, чем аккаунт мог бы себе
позволить», и живой прогон в него не влезает: он шкалу не укорачивает, он делает старт невозможным. Поэтому
минор правит и описание схемы — «почему старт не предлагается так, как обещал бы один баланс», с двумя
формами внутри. `book_id` при `run_in_flight` — ЭТА ЖЕ книга: место, где пользователь может действовать, —
карточка её собственного прогона, а не чужой.
**Порядок.** Форма ратифицирована ДО стройки (как `D39.235` п.6 для кадра остановки): иначе сессия зоны либо
ставит константу версии и красит собственный гейт, либо строит механизм, объявляя старую версию. Константа
`httpapi.ContractVersion` уезжает ТЕМ ЖЕ лендингом, что и код.
### 2.24. ⛔ ЗАПРЕТ «ДЕНЕГ В ИНТЕРФЕЙСЕ НЕТ» ОТОЗВАН ВЛАДЕЛЬЦЕМ (05.09, прозой — не минор)
**Что случилось.** Норма «оплат в MVP нет, денежных полей в UI нет вовсе» месяц определяла форму

View file

@ -2,7 +2,7 @@ openapi: 3.1.0
info:
title: TextMachine API
version: 0.13.1
version: 0.14.0
summary: Ratified contract between the frontend and the TextMachine platform.
description: |
**RATIFIED contract.** Canonical copy: `docs/architecture/14-api-contract/`;
@ -2845,21 +2845,30 @@ components:
Blocked:
type: object
description: |
What is holding the scale down, and which book is doing it.
Why a start is not offered the way the balance alone would promise, and which book is the
reason. Two shapes of that: the scale is SHORTER than the credit could buy, or a start is
impossible right now however much credit there is. `verdict` stays the truth about MONEY; a
client reads the two together (§RunOptions).
required: [code, book_id]
properties:
code:
type: string
description: |
`credit_held` — another book of this account has a run holding the credit, released when
that run settles. A client MUST tolerate an unknown value under a minor bump and show a
neutral "something else is using the balance" state.
enum: [credit_held]
that run settles; the scale is shorter than the balance suggests.
`run_in_flight` — THIS book already has a run in flight, so admission will refuse a
second purchase whatever the scale says. The form knew it before the click, and saying
nothing was the defect this value closes (PD-455).
A client MUST tolerate an unknown value under a minor bump and show a neutral
"something else is using the balance" state.
enum: [credit_held, run_in_flight]
book_id:
$ref: '#/components/schemas/Id'
description: |
The book that holds it — an id, so a client that wants to name it reads that book's
card, which is also where the user can act.
The book that is the reason — an id, so a client that wants to name it reads that book's
card, which is also where the user can act. For `credit_held` that is ANOTHER book of the
account; for `run_in_flight` it is THIS book, and its own card is where the running job
can be watched or stopped.
CeilingBounds:
type: object