Land the contract minor: the canon stops promising the door the owner abolished, declares the correction door derived field-for-field from the landed verb, and the deployment announces the version it now serves
This commit is contained in:
parent
b5b2ab6eb2
commit
243a8c341c
10 changed files with 1030 additions and 130 deletions
372
docs/CONTRACT_MINOR_REPORT.md
Normal file
372
docs/CONTRACT_MINOR_REPORT.md
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
# Отчёт контрактной сессии: минор 0.4.0 → 0.5.0
|
||||
|
||||
> Сессия по промту `docs/CONTRACT_MINOR_SESSION_PROMPT.md` (оркестратор №19, 27.08.2026).
|
||||
> Файл ведётся ПО ХОДУ работы (§7): секция 1 написана ДО правок. Зона записи — два файла контракта
|
||||
> плюс этот отчёт; сессия НЕ коммитит.
|
||||
|
||||
## 1. Эхо-протокол (§9) и записка-план — ДО правок
|
||||
|
||||
**Скоуп:** снос отменённой пер-термной модели подписи (§3.1) · объявление двери правок банка,
|
||||
выведенной из словаря `tmctl bank-apply` (§3.2) · судьба счётчиков `pending_decisions`/`complete`
|
||||
(§3.2-бис) · предупреждение о конверте вне `/v0` (§3.3) · версия `0.5.0`.
|
||||
|
||||
**Инварианты:** словарь двери — из `membank/decisions.go` + `pipeline/bankdecisions.go`, не
|
||||
изобретаю; неизвестный кортеж — НЕ ошибка (форма добавления терма); `undecided` — только вместе с
|
||||
запретительным контрактом `SignatureState`, обе половины; коды 14/15 различимы на проводе; конверт
|
||||
ошибки — существующий `Problem`/`ErrorCode`; узость окна, отклонение сид-терма, необслуживаемость —
|
||||
явно в каноне; машиночитаемый признак «не построено» обязателен.
|
||||
|
||||
**Не-делать:** не коммичу; `platform/`/`frontend/`/`backend/`/`eval/` не трогаю; полоса прогресса
|
||||
(строка 200) не берётся (D39.160); семантика прогресса `openapi.yaml:1293` не меняется; гейт
|
||||
платформы не правится — его красный ожидаем; в `docs/PROGRESS.md` не пишу.
|
||||
|
||||
### Записка-план: что меняю, где, чем проверю
|
||||
|
||||
**Канон `openapi.yaml`:**
|
||||
1. `version: 0.4.0 → 0.5.0`; пример `contract_version` → `0.5.0`.
|
||||
2. §3.1 снос: путь `:469-505` · проза `:1830` (`TermStatus`) · схемы `:1939-2008`
|
||||
(`BankDecision`/`BankDecisionsRequest`/`BankDecisionsResult`). Мой греп подтвердил список промта
|
||||
и НЕ нашёл иных носителей четырёх имён (команда в §5 ниже).
|
||||
3. §3.2-бис: снос `pending_decisions`+`complete` из `BankPage` (`:1924-1934`) и — находка МОЕГО
|
||||
грепа, в списке промта её нет — из `EventBank` (`:2367-2372`). Ратифицированная проза «ONE act»
|
||||
в `listBankTerms` сохраняется, клауза счётчиков и фраза «marked unverified inside the service»
|
||||
(снята ещё ФБ-8 как обещание без носителя, но в `:445` уцелела) — уходят.
|
||||
4. §3.2 дверь: `POST /books/{bookId}/bank/corrections` + схемы (`BankCorrection`,
|
||||
`BankCorrectionsRequest`, `BankCorrectionsReceipt`, `BankSignatureCount`, `CorrectionRefusal`) +
|
||||
два корневых кода (`bank_corrections_refused` 409, `bank_corrections_incomplete` 503) + признак
|
||||
`bank_corrections_enabled` в `Capabilities` (прецедент `intake_enabled`; false ⇒ 404).
|
||||
5. §3.3: в `info.description` (после `:35-37`) — предупреждение о конверте вне `/v0` без
|
||||
обязательного `code`, ссылкой на компаньон §2.14 (один носитель на факт).
|
||||
6. Тег `:127` «Memory bank and term signing» — переформулировать без пер-термной подписи.
|
||||
|
||||
**Компаньон `README.md`:** шапка (статус 0.5.0, без самозаписи ратификации) · `:124` и `:724`
|
||||
(списки мест правила «схема + слова» — преемник) · `:674` (историческая строка сохраняется, статус
|
||||
дописывается) · `:685` (строка про недоезд `decline` — пере-снята: новая дверь пишет файлы движка) ·
|
||||
`:358` §2.9 (остаток пере-привязан к живым носителям) · новая секция §2.19 (вывод двери, провенанс) ·
|
||||
§3 таблица (строка двери: движок построен, платформа — пак 2в) · Приложение А-2 (две строки кодов).
|
||||
|
||||
**Чем проверю (§5):** (1) YAML-парс + линтер, если найдётся в окружении; (2) сверка `$ref` против
|
||||
`components.schemas` в обе стороны + loader-хук на дубли ключей; (3) гейт платформы на копии
|
||||
`cp -a --parents platform docs/architecture/14-api-contract` — красный ИМЕННО на версии и с
|
||||
ожидаемым текстом. Плюс опровергатели (§5 промта, заказ): 2–4 агента, мандат «дверь против словаря
|
||||
глагола» + оси §6.
|
||||
|
||||
## 2. Обоснование формы двери (§3.2)
|
||||
|
||||
Полная таблица «провод ← движковый источник, file:line» и решения ◆ — в компаньоне, новая секция
|
||||
§2.19 (`docs/architecture/14-api-contract/README.md`); здесь — сами решения с разобранными
|
||||
альтернативами. Всё выведенное (✓) взято из `membank/decisions.go` + `pipeline/bankdecisions.go` +
|
||||
`cmd/tmctl/main.go:69-75` и в канон попало без пересочинения.
|
||||
|
||||
**2.1. Форма поверхности — один `POST /books/{bookId}/bank/corrections` с документом целиком.**
|
||||
Альтернатива «ресурсная» (`PATCH /bank/terms/{id}`) отвергнута: глагол атомарен и
|
||||
всё-или-ничего (`ApplyDecisions`, `decisions.go:282-307`), а пер-строчные мутации обещали бы
|
||||
частичную применимость и правку строк банка, которых этот вызов не меняет (вид пересобирается на
|
||||
границе прогона — `18-bank-ontology.md`). Альтернатива «двух путей» (`…/preview` отдельным POST)
|
||||
отвергнута: два пути = два места, где растёт форма, а закон шва п.7 требует, чтобы проекция была
|
||||
той же операцией, что мутация («a report printed by the same call that already wrote is not a
|
||||
projection» — `bankdecisions.go:44-45`).
|
||||
|
||||
**2.2. Имя `corrections`.** «decisions» вернуло бы имена снесённых схем с другой семантикой
|
||||
(генерённый дифф читался бы как правка старой двери); «edits» — `edit` в грепе-гейте §5 компаньона
|
||||
неотличим от имени волны движка. Продуктовое слово владельца — «правка» (D39.144).
|
||||
|
||||
**2.3. `book_id` в теле, обязательный, при живом `{bookId}` в пути.** Движок держит ДВЕ разные
|
||||
проверки: документ обязан НАЗЫВАТЬ книгу (`decisions.go:141-143`) и отдельно сверяется с открытой
|
||||
книгой (`bankdecisions.go:267-269`) — причина названа в коде: решения несут слова пользователя в
|
||||
канон книги, и набор, посчитанный для другой книги, даунстрим не заметит. Если бы book_id вводила
|
||||
платформа из пути, сверка стала бы вакуумной — клиент, POST-нувший набор книги A в путь книги B,
|
||||
прошёл бы молча. Цена — одно поле.
|
||||
|
||||
**2.4. Кортеж требует все четыре члена** (`src`,`sense`,`since_chapter`,`until_chapter`; движок
|
||||
дефолтит опущенные — `decisions.go:469,479-482`). Провод строже движка сознательно: неизвестный
|
||||
кортеж здесь легально ДОБАВЛЯЕТ терм, поэтому цена опущенного `sense` — не отказ, а тихая
|
||||
параллельная строка (клиент хотел править (src, sense="X"), а назвал (src, "")). `null`-семантика
|
||||
окна — как у `BankTerm` (0.3.0 развёл сентинел `0` на `null`); проекция `null→0` — платформа,
|
||||
закон шва п.6.
|
||||
|
||||
**2.5. `preview` — обязательное булево тела.** Закон шва п.7 (проекция прежде мутации) +
|
||||
прецеденты тотальной обязательности (`stop_requested` — §6в J компаньона; в запросах —
|
||||
`RunRequest.stop_for_signing`). Query-параметр на POST отвергнут: в этом каноне тело — единственный
|
||||
носитель семантики небезопасных вызовов, и опциональность дала бы «мутацию по умолчанию».
|
||||
|
||||
**2.6. Раскладка полосы отказов.** Ратифицированная полоса 10–19 (`main.go:69-75`) ложится так:
|
||||
- **14 → `409` `bank_corrections_refused`** + member `refusals[]`. Отдельный корневой код, не общий
|
||||
`400`: это законный отказ, по которому ПОЛЬЗОВАТЕЛЬ пере-решает (совет промта принят; довод —
|
||||
разные адресаты ремеди). `409`, не `422`: канон весь класс state-зависимых отказов держит на 409,
|
||||
а отказ здесь — о книге как она стоит (сид, коллизии набора).
|
||||
- **15 → `503` `bank_corrections_incomplete`**: «документ ПРИНЯТ, слать ТОТ ЖЕ» — ремеди машинный и
|
||||
специфичный, потому код свой, а не общий `service_unavailable`; ретрай сходится по байтовому
|
||||
no-op (`bankdecisions.go:209-221`).
|
||||
- **12 → `409` `run_in_flight`**: словарь канона уже имеет точное слово («книга уже переводится»);
|
||||
движковая причина (флок живого прогона, `bankdecisions.go:159-163`) совпадает по смыслу.
|
||||
- **10/11/13 → без пер-операционных кодов**: это «деплой сломан, чинит человек» — `5xx` общего вида
|
||||
(500 канон сознательно не документирует — Zalando-правило из А-2).
|
||||
- **потолок 1 МиБ → `413`** (`payload_too_large` дообъяснён), **потолок 5000 → схема
|
||||
(`maxItems: 5000`) + слова**, нарушение = `400` `invalid_request` (нарушение объявленной формы;
|
||||
движковый класс 14 для него — за платформенной валидацией и на провод не выходит).
|
||||
|
||||
**2.7. Причины пер-решенческих отказов — `refusals[].detail`, developer-facing, не показываются.**
|
||||
У движка причины — свободный текст (`RejectedDecision.Reason`); машинного словаря причин нет, и
|
||||
таблица перевода (закон п.6) переводит только структуры, не прозу. Замораживать в каноне пересказ
|
||||
двадцати формулировок = вторая копия растущего словаря. Альтернатива «коды причин на платформе»
|
||||
отвергнута: платформа для этого парсила бы английские предложения движка. Названная щель — в §6.
|
||||
|
||||
**2.8. Что НЕ пошло на провод из отчёта глагола** (каждое — решение, не забывчивость): пути файлов
|
||||
и `written_delta`/`written_rejects` (серверная топология; ремеди клиента один — «слать тот же»);
|
||||
`canonical_rewrite*` (предупреждение оператору файлов на диске, у клиента продукта нет ни файла, ни
|
||||
комментариев в нём); ТЕКСТЫ `preexisting_problems` (свободный текст движка) — но их СЧЁТ
|
||||
(`preexisting_faults`) едет, потому что «книга уже больна, следующий прогон может встать» экрану
|
||||
нужен; `replaced[]` (свободный текст) — свёрнут в булев `displaced`, несущий ровно то свойство,
|
||||
ради которого поле существует («перекрыл чужое слово — видь это», `decisions.go:225-228`);
|
||||
`mode` — раскладывается на `preview`/`changed`/HTTP без остатка; `decisions_version`/
|
||||
`report_version` — версии документов ШВА, на проводе их роль играет `contract_version`.
|
||||
|
||||
**2.9. `depth`: `edit_wave` → `refinement`.** Движковая константа несёт имя волны — запрещённый на
|
||||
проводе словарь (гейт §5). Продуктовое слово с тем же смыслом; обе половины смысла («следующий
|
||||
прогон»; «черновик не пере-формируется» → «не пере-переводится с нуля») продублированы словами.
|
||||
Поле оставлено (не свёрнуто в прозу), потому что движок сделал его ПОЛЕМ с версией ровно затем,
|
||||
чтобы будущая смена глубины была наблюдаемой (`decisions.go:51-57`).
|
||||
|
||||
**2.10. `signature` — опубликован с ОБЕИМИ половинами контракта** (`bankdecisions.go:88-118`):
|
||||
положительная (счёт согласован с правилом сворачивания — полезен экрану) и запретительная
|
||||
(не гейт; не отвечает «погаснет ли стоп» ни в одну сторону; `undecided: 0` ничего не обещает;
|
||||
решать — ПРАВО; `unreadable` = числа не значат ничего). `map` (путь) не едет; «карты ещё нет»
|
||||
выражено `signature: null` — иначе `0/0` без карты был бы байтово неотличим от «всё решено», тот же
|
||||
класс дыры, который движок закрыл флагом `unreadable`.
|
||||
|
||||
**2.11. Признак «не построено» — `Capabilities.bank_corrections_enabled: boolean`.** `Capabilities`
|
||||
годится: плоский документ деплоя, уже дом `intake_enabled` (булев + объявленный `404`) и
|
||||
`export_formats` («пусто = не построено»). Булев, а не пустой список: у двери нет оси форматов —
|
||||
нечего перечислять. Поведение при `false` объявлено (`404`), зеркаля интейк.
|
||||
|
||||
**2.12. Квитанция — НЕ чтение:** без `revision`/`structure_version`, кадр `bank` не испускается,
|
||||
строки `GET /bank` не меняются до следующего прогона — сказано в каноне прямо (иначе первый же
|
||||
экран прочтёт «исправил, а банк не изменился» как баг). Это проекция однонаправленного потока
|
||||
онтологии (источники → вид → проекции, `18-bank-ontology.md`).
|
||||
|
||||
## 3. Решение по счётчикам (§3.2-бис): СНЕСТИ — со всех ТРЁХ носителей
|
||||
|
||||
Мой греп нашёл носителей больше, чем названо в промте: `pending_decisions`/`complete` жили в
|
||||
`BankPage` (`:1924-1934`), в квитанции `BankDecisionsResult` (`:1997-2008`, умерла со схемой) **и в
|
||||
кадре `EventBank` (`:2367-2372`, required)** — третий носитель в списке промта отсутствовал
|
||||
(команда: `grep -n "pending_decisions\|'complete'\|complete:" openapi.yaml` до правок — хиты
|
||||
442, 1924, 1929, 1997, 2000, 2004, 2367, 2371-2372).
|
||||
|
||||
Из трёх заказанных исходов выбран **снос**, довод в компаньоне §2.19-бис: пере-определить нельзя —
|
||||
честный счёт движковый (`SignatureState`), и read-модель его не вычислит, не пере-реализовав
|
||||
движковый закон (запрещено D39.156 п.6; «Чего эта форма НЕ несёт» онтологии); пометить нельзя —
|
||||
обязательное вечно-нулевое поле это PD-370 в поле вместо пути. Ратифицированная проза «Signing the
|
||||
bank is ONE act…» СОХРАНЕНА в `listBankTerms`; попутно из того же абзаца снята фраза «marked
|
||||
unverified inside the service» — обещание без носителя, снятое ещё ФБ-8 (компаньон §6б) и
|
||||
уцелевшее в одном месте. Цена сноса названа в каноне и компаньоне: чтение банка больше не отвечает
|
||||
«сколько осталось»; счёт едет квитанцией двери (`signature`); возврат в чтение — когда закажут
|
||||
экран подписи и движок опубликует нерешённость проекцией (носитель — `18-bank-ontology.md`).
|
||||
|
||||
## 4. Исполнения самопроверки (§5) — команды и вывод
|
||||
|
||||
**4.1. Канон парсится + дубли ключей** (loader-хук, § 5 п.1-2; скрипт `check_canon.py` в
|
||||
скратчпаде сессии):
|
||||
|
||||
```
|
||||
$ python3 .../check_canon.py
|
||||
PARSE OK, no duplicate keys. version = 0.5.0
|
||||
```
|
||||
|
||||
**4.2. Ссылки, обе стороны** (тот же скрипт: все `$ref` резолвятся; все компоненты на кого-то
|
||||
ссылаются):
|
||||
|
||||
```
|
||||
refs total: 272; dangling: 0
|
||||
components.schemas: 63 defined, unreferenced: none
|
||||
components.responses: 10 defined, unreferenced: none
|
||||
components.parameters: 11 defined, unreferenced: none
|
||||
components.headers: 2 defined, unreferenced: none
|
||||
```
|
||||
|
||||
Тем же скриптом пере-ран гейт §5 компаньона (утечка конвейерного словаря) по ФИНАЛЬНОЙ спеке:
|
||||
`stage` — 1 хит, сама формулировка запрета (легально); `edit` — 2 хита, оба прежние легальные
|
||||
английские глаголы (`:283` «not an edit», `:1683` «editing the source» — инвентарь 0.4.0, новых
|
||||
нет; команда: `grep -n "\bedit" openapi.yaml | grep -v credit`); `verdict` — 2 хита БЫЛИ моими в
|
||||
`CorrectionRefusal`, исправлено на «refusal» до сдачи, пере-ран чист.
|
||||
|
||||
**4.3. Настоящий линтер и генератор** (пины зоны фронта, поверх п.1):
|
||||
|
||||
```
|
||||
$ ./frontend/node_modules/.bin/spectral lint --ruleset frontend/.spectral.yaml docs/architecture/14-api-contract/openapi.yaml
|
||||
No results with a severity of 'error' found!
|
||||
$ ./frontend/node_modules/.bin/openapi-typescript <канон 0.4.0 из HEAD> → OK (exit 0)
|
||||
$ ./frontend/node_modules/.bin/openapi-typescript <канон 0.5.0> → OK (exit 0)
|
||||
```
|
||||
|
||||
Дифф генерённых типов 0.4.0 → 0.5.0 — 380 строк, ВСЕ в заявленной поверхности: путь и operationId ·
|
||||
минус три схемы и счётчики (`BankPage`, `EventBank`) · плюс шесть схем · `ErrorCode` +2 значения ·
|
||||
`Capabilities` +1 обязательное поле · `Problem.refusals`. Неожиданных сдвигов нет (команда: `diff
|
||||
types-040.ts types-050.ts`). Замечание генератора: `oneOf`-ветки идентичности `BankCorrection`
|
||||
сворачиваются в `& (unknown | unknown)` — тип не дискриминирует ветки; это известный класс К-11
|
||||
(генератор игнорирует и `if`/`then`), правило «схема + слова» применено, слова в описании стоят.
|
||||
|
||||
**4.4. Гейт платформы краснеет ПРЕДСКАЗУЕМО** (§5 п.3; копия ВМЕСТЕ с каноном, как велит промт):
|
||||
|
||||
```
|
||||
$ cp -a --parents platform docs/architecture/14-api-contract $SCRATCH/gatecopy/
|
||||
$ cd $SCRATCH/gatecopy/platform && go test ./internal/gates/ -run TestTheAnnouncedContractVersionIsTheOneTheCanonRatified
|
||||
--- FAIL: TestTheAnnouncedContractVersionIsTheOneTheCanonRatified (0.00s)
|
||||
contract_test.go:40: this build announces contract 0.4.0 and the ratified canon is 0.5.0; the
|
||||
constant is raised in the same change as the code that implements a minor, never afterwards
|
||||
FAIL
|
||||
```
|
||||
|
||||
Красный ИМЕННО на несовпадении версий и ИМЕННО с ожидаемым текстом; константу поднимает оркестратор
|
||||
при лендинге тем же коммитом (§4 промта, D39.160).
|
||||
|
||||
## 5. Находки опровергателей — все, включая отвергнутые с причиной
|
||||
|
||||
Панель: 3 агента (в пределах веера 2–4, §5 промта), по осям §6 — «словарь двери против словаря
|
||||
глагола» (заказ) · «холодный потребитель/генератор» · «что читатель узнает о том, чего нет».
|
||||
Workflow `wf_20c22060-e6e`, 349k токенов, 0 упавших. Каждый ключевой клейм пере-сверен МОИМ чтением
|
||||
кода до вправления (файлы и строки в диспозициях). **Итог: 21 находка; принято и вправлено 17,
|
||||
отвергнуто 4 (с причиной ниже).**
|
||||
|
||||
### Приняты и вправлены (после каждой — куда легла правка)
|
||||
|
||||
**Ось «словарь глагола»:**
|
||||
1. **MAJOR. Decline сид-поверхности уплощён до безусловного отказа.** Движок отказывает ТОЛЬКО
|
||||
инертному (`decisions.go:374`, условие `!deltaHoldsSurface`); когда правки книги держат свою
|
||||
строку той же поверхности, decline принимается и снимает её — ремонт ливлока, ради которого
|
||||
правило и переписывалось. Мой канон запрещал ремонт. → канон (`BankCorrection`, список
|
||||
«NOT correctable») + компаньон §2.19 (строка сид-терма).
|
||||
2. **MAJOR. Decline алиаса уплощён.** Движок судит РЕЗУЛЬТАТ и отказывает только алиасу
|
||||
ПОДПИСАННОГО терма (`aliasOwner` пропускает `status != approved`, `decisions.go:644-659`);
|
||||
decline терма и его алиаса одним документом — оба принимаются. → канон (условная формулировка
|
||||
«once this document is applied, would remain only an ALIAS an approved correction still fires
|
||||
for») + компаньон.
|
||||
3. Алиасы на проводе НЕ публикуются (`BankTerm` их не несёт, D39.136 п.4б), а вывод-таблица
|
||||
читалась как будто публикация есть; клиент не может увидеть, что поверхность — алиас. → канон
|
||||
(скобка «the alias set itself is not published on this surface») + компаньон (названная щель).
|
||||
4. Класс 12 → `run_in_flight` точен только при сериализации платформой СВОИХ вызовов двери (флок
|
||||
держит любой глагол, включая превью). → компаньон: обязательство монтажа (в), «точное слово»
|
||||
переквалифицировано.
|
||||
5. `400` `errors[/book_id]` не имеет движкового производителя (движковая сверка — класс 10):
|
||||
производит платформа до спавна. → компаньон: обязательство монтажа (б).
|
||||
6. Оба потолка у движка — класс 14, канон раскладывает в `400`/`413`: платформа мерит их на
|
||||
проводной форме до спавна; остаток (движковый документ после рендера > 1 МиБ) падает в `409`.
|
||||
→ компаньон: обязательство монтажа (а); канон: описание `bank_corrections_refused` несёт
|
||||
«a set larger than the service applies in one act».
|
||||
7. `preexisting_faults` «may stop at the bank» читался как благой signing-stop, и объект был сужен
|
||||
до файлов правок (движок считает и фолты сид-строк). → канон: «bank inputs», «would FAIL at
|
||||
the bank — a fault, not the signing stop».
|
||||
8. «`mode` раскладывается без остатка» — ложь: исход `stopped` не был разложен. → компаньон:
|
||||
назван (`5xx` рестарта, ничего не записано, ретрай сходится).
|
||||
9. Усечение отказа: на `409` движок печатает полный отчёт, провод несёт только `refusals[]` —
|
||||
не было названо. → компаньон: названо ценой v1.
|
||||
10. `kind` на проводе сужен до `TermKind`, движок принимает любую строку; источник пятёрки —
|
||||
классификатор чтения, не `decisions.go`. → компаньон: сужение задекларировано ◆ с доводом.
|
||||
|
||||
**Ось «холодный потребитель»:**
|
||||
11. **MAJOR. Счёт `signature` недостижим, пока нечего послать** (у `corrections` `minItems: 1`,
|
||||
пустой документ и движок отказывает классом 10). Решено НЕ ослаблением `minItems` (движок
|
||||
пустой документ не принимает — ослабление дало бы дверь, врущую о глаголе), а НАЗВАННОЙ
|
||||
узостью: канон (`signature` квитанции + `listBankTerms`) говорит прямо «only there; a client
|
||||
with nothing to correct or preview cannot ask for it yet»; носитель возврата счёта в чтение —
|
||||
§2.19-бис.
|
||||
12. **MAJOR. Семантика `sense`/окна при `decline` в кортежной форме не была определена.** → канон:
|
||||
«say which row the receipt reports and do NOT narrow the effect», для поверхности вне банка —
|
||||
`sense: ""` и `null`-окна (сверено: движковый decline ключуется нормализованным `src`,
|
||||
`addReject`).
|
||||
13. **MAJOR. Неизвестный `id` — двоякое прочтение (400 c `errors[] unknown` против 409).** → канон
|
||||
называет код явно: `409` `bank_corrections_refused`, вход в `refusals[]` (движок: класс 14).
|
||||
14. **MAJOR. Отказ за decline алиаса опирался на данные, невидимые клиенту** — слился с №2/№3.
|
||||
15. «Would change nothing» как причина 409 против гарантии `already_applied` — граница не была
|
||||
проведена. → канон: «a `decline` nothing recorded answers to; re-sending an already-recorded
|
||||
correction is NOT this — that answers `200` with `already_applied`».
|
||||
16. `AcceptedCorrection.dst` — третье незаявленное исключение из правила шапки
|
||||
«required+nullable». → канон: `dst` теперь required, `[string, 'null']`, «null on decline».
|
||||
17. Переиспользованные `TooLarge`/`ServiceUnavailable` несли чужую конкретику (интейк/прогоны) —
|
||||
докстроки врали бы на этой операции. → канон: описания компонентов обобщены, `503` отсылает к
|
||||
диспетчеризации по `code`.
|
||||
18. `BankTerm.id` «its surfaces» (мн.ч.) читалось как «id зависит от dst». → канон: «its SOURCE
|
||||
surface … not from `dst`, so a corrected rendering keeps the id».
|
||||
19. Протухший пример `EventHello.contract` `0.4.0`. → канон: `0.5.0`.
|
||||
20. `Problem.code` обязателен схемой, а вне `/v0` может отсутствовать — правило жило только в
|
||||
шапке, генератор его не видит. → канон: оговорка в описании `Problem`.
|
||||
|
||||
**Ось «чего нет»:**
|
||||
21. **MAJOR. «Канон и деплой при лендинге СОВПАДАЮТ» — переклейм.** Сверено кодом: `wireBankPage`
|
||||
(`platform/internal/httpapi/reading.go`) продолжает слать `pending_decisions`/`complete`, и
|
||||
они НЕ нули — `bankCountsTx` (`pgstore/readmodel.go`) считает «все proposed-строки» с
|
||||
комментарием «the wire fields are the canon's» (теперь ложным). Совпадение точно по ПУТЯМ, не
|
||||
по полям. → компаньон: шапка переписана честно, §2.19-бис несёт сверенную правду вместо
|
||||
«навсегда нули», строка `GET /bank` в §3 пере-снята, носитель снятия — пункт (2в). ⚠ Это
|
||||
расхождение с БУКВОЙ промта пака (§4 п.2 «СОВПАДУТ точно», §3.2-бис «поля навсегда нули») —
|
||||
**пинг оркестратору, см. §6.**
|
||||
22. Композиция «до монтажа 2в счёт недоступен НИГДЕ» была только выводимой. → компаньон
|
||||
§2.19-бис, названо тремя половинами цены.
|
||||
23. А-2: «гейт полноты ДЕМОНТИРУЕТСЯ в P7» — будущее время при снятом гейте (сверено:
|
||||
`reconcile.go`, ветка `awaiting_bank`). → компаньон: «ДЕМОНТИРОВАН, сверено с кодом».
|
||||
24. §2.9: баннер 0.5.0 стоял ПОСЛЕ перекрытой клаузы — против «баннер прежде содержимого». →
|
||||
компаньон: переставлен перед ней.
|
||||
25. Позитив: все семь обязательных «явных фактов» оси 3 подтверждены с картой line-номеров
|
||||
(вердикт панели, находка-нота) — в канон изменений не требует.
|
||||
|
||||
### Отвергнуты, с причиной
|
||||
|
||||
1. **`Capabilities.intake_max_bytes` обязателен и не-nullable у read-only деплоя** — реальный
|
||||
дефект, но 0.3.0/0.4.0-наследие ВНЕ состава этого пака (§3 промта); правка сейчас раздула бы
|
||||
ломающую поверхность минора без заказа. Оставлено оркестратору как кандидат следующего касания.
|
||||
2. **`TermKind`/`TermStatus`/`TermOrigin` без пер-схемных нот толерантности** — паттерн 0.3.0, вне
|
||||
состава пака; общее правило шапки формально покрывает. Кандидат следующего касания (вместе с
|
||||
№1).
|
||||
3. **Байт-зеркало фронта (0.2.3) несёт снесённую модель** — отставание РАТИФИЦИРОВАНО (D39.142
|
||||
п.5, D39.147, фриз фронта); шапка компаньона его уже несёт. Не дефект этого минора.
|
||||
4. **`unreadable`: «the call was refused before it could count» не имеет носителя на проводе** —
|
||||
принято НАПОЛОВИНУ: формулировка-триггер из описания снята (правда), но само поле оставлено —
|
||||
первый триггер («не читается состояние») жив и на 200-квитанции (карта/файлы нечитаемы, вызов
|
||||
при этом принят byte-no-op'ом… нет: нечитаемые файлы — отказ класса 10; нечитаемой может быть
|
||||
КАРТА подписи — вызов принят, счёт не измерен). Ровно этот случай поле и несёт.
|
||||
|
||||
## 6. Obstacle — что НЕ удалось и что НЕ проверено (§10)
|
||||
|
||||
1. **Расхождение промта с кодом — пинг оркестратору (§11 промта):** промт утверждал «платформа
|
||||
свою половину уже снесла 22.08, после твоего сноса они СОВПАДУТ точно» (§4 п.2) и «поля
|
||||
навсегда нули» (§3.2-бис). Сверка с кодом (`httpapi/reading.go` `wireBankPage`;
|
||||
`pgstore/readmodel.go` `bankCountsTx`) показала: путь снесён, а ПРОЕКЦИЯ ЧТЕНИЯ продолжает
|
||||
слать оба поля, и `pending_decisions` — живое число всех proposed-строк, не ноль. Я не
|
||||
интерпретировал в свою пользу и не обходил: канон 0.5.0 поля снимает (это и есть заказ), а
|
||||
расхождение записано в шапку компаньона с носителем (пункт 2в) и сюда. **Оркестратору решить:**
|
||||
достаточно ли носителя «пункт 2в» для снятия двух полей из `wireBankPage`, или нужна отдельная
|
||||
строка бэклога платформы (я её завести не могу — не моя зона).
|
||||
2. **Не проверено исполнением:** поведение НАСТОЯЩЕЙ платформы против 0.5.0 (маршрута нет — 404
|
||||
проверить не на чем до монтажа); рендер экранов фронта (зона заморожена ратифицированно);
|
||||
`spectral` гонялся ТОЛЬКО с рулсетом зоны фронта (`spectral:oas`) — иных рулсетов в репо нет.
|
||||
3. **Названные щели v1 (в канон/компаньон внесены, но не закрыты):** машинного словаря причин
|
||||
отказов нет (едут developer-facing `detail`); алиасы невидимы клиенту; счёт `signature`
|
||||
недостижим без правки/превью; на `409` не едут `preexisting`/`signature`; счётчики деплой шлёт
|
||||
до монтажа 2в.
|
||||
4. **Гипотетический остаток раскладки потолков:** случай «HTTP-тело < 1 МиБ, движковый документ
|
||||
после рендера платформой > 1 МиБ» назван и покрыт кодом 409, но живьём не воспроизводился
|
||||
(двери нет); замер — работа монтажа.
|
||||
5. Дифф генерённых типов пере-ран после вправлений панели (`types-050b.ts`, exit 0), но
|
||||
пост-фиксовый ПОЛНЫЙ дифф против 0.4.0 построчно не пере-инвентаризировался — сверены только
|
||||
изменённые правками места (`AcceptedCorrection.dst`, примеры, описания).
|
||||
|
||||
## 7. Таблица комплектности против §3 (§7)
|
||||
|
||||
| Пункт заказа | Что сделано | Исполнение-подтверждение |
|
||||
|---|---|---|
|
||||
| §3.1 снос: канон, 9 позиций | путь+операция (`:469-505` стар.) · проза `:1830` · три схемы `:1939-2008` — снесены; замена — дверь corrections | `grep -n "bank/decisions\|submitBankDecisions\|BankDecision\b\|BankDecisions\|pending_decisions\|term_id" openapi.yaml` → exit 1 (0 хитов) |
|
||||
| §3.1 снос: компаньон, 5 позиций | `:124` (список К-11-мест) · `:674` (историческая строка сохранена, статус дописан) · `:685` (пере-снята на живые носители) · `:724` (К-11) · `:1173` (⚠-баннер истории §6в K) | `grep -n "submitBankDecisions\|BankDecisionsRequest\|BankDecisionsResult" README.md` → 0 хитов вне ⚠-контекстов; пер-позиционные диффы в дереве |
|
||||
| §3.1 «свой греп обязателен, ещё и по `dst`/`UnknownTermError`/`BankPage`» | исполнен до правок | команда и хиты — секция 3 отчёта; `UnknownTermError` — 0 хитов в обоих файлах |
|
||||
| §3.2 дверь объявлена, тело выведено из глагола | `POST /books/{bookId}/bank/corrections` + 6 схем + 2 корневых кода + member `refusals` | вывод-таблица §2.19 компаньона (каждое поле — file:line движка); панель-опровергатель по оси словаря — секция 5 |
|
||||
| §3.2 машиночитаемый признак «не построено» | `Capabilities.bank_corrections_enabled` (required; `false` ⇒ `404`) | канон; прецеденты `intake_enabled`/`export_formats` — §2.19 |
|
||||
| §3.2 ⛔ signature только с запретительным контрактом | `BankSignatureCount` несёт ОБЕ половины D39.144 + `unreadable` + `null`-семантика | канон `:2217+`; проверка панелью (ось 3, факт 7 подтверждён с line-номерами) |
|
||||
| §3.2-бис счётчики: решить обязан | решено — СНОС с трёх носителей (третий найден моим грепом) | секция 3 отчёта: греп-команда; §2.19-бис компаньона |
|
||||
| §3.3 конверт вне `/v0`, строка 203(а) | предупреждение в `info.description` + оговорка в `Problem`; ссылка на компаньон §2.14 | буква (а) сверена: `docs/PROGRESS.md:146` (греп `строка 203`); канон `:39-43` |
|
||||
| §3.4 прогресс НЕ трогать | семантика `:1293` не тронута | `git diff openapi.yaml | grep -c "segment\|counter starts again"` → 0 правок в блоке `Progress` |
|
||||
| §4 версия | `0.5.0`; гейт платформы краснеет предсказуемо | секция 4.4: команда копии и вывод FAIL с ожидаемым текстом |
|
||||
| §5 п.1 парс | исполнено | секция 4.1 |
|
||||
| §5 п.2 ссылки + дубли | исполнено, обе стороны | секция 4.2 |
|
||||
| §5 п.3 гейт на копии | исполнено, `cp -a --parents` | секция 4.4 |
|
||||
| §5 опровергатель (заказ) | 3 агента, 21 находка, 17 вправлено, 4 отвергнуто с причиной | секция 5; workflow `wf_20c22060-e6e` |
|
||||
| §7 записка-план до правок | секция 1 написана до первой правки файлов контракта | git-история дерева не создавалась (не коммичу); порядок виден по этому файлу |
|
||||
| §9 эхо · §10 obstacle · §11 состав | секции 1, 6; состав сдачи — три файла | `git status` перед сдачей — см. финал сессии |
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -26,7 +26,7 @@
|
|||
| Платформа | активного НЕТ | читающий пак P8-REVIEW ОТРАБОТАН, ПРИНЯТ и ЗАЛЕНДЁН 27.08 (D39.159): четыре оси, 24 строки регистра, единственный vuln `PD-379`. Промт — `platform/docs/archive/`. Следующая работа зоны — КОДОВЫЙ пак по этим строкам, промт пишется по слову владельца |
|
||||
| Полигон | [POLYGON_EXP2223_REDO_SESSION_PROMPT.md](POLYGON_EXP2223_REDO_SESSION_PROMPT.md) (отложенный — [POLYGON_PACKAGE4_SESSION_PROMPT.md](POLYGON_PACKAGE4_SESSION_PROMPT.md), строка 85) | фаза Д ИДЁТ; ⚠ живой носитель курса — в `eval/dovodka/`, какой именно называет зона (⚠ [POLYGON_PHASE_D_HANDOFF.md](POLYGON_PHASE_D_HANDOFF.md) — перекрытый снимок, читать не как курс) |
|
||||
| Фронт | активного НЕТ | **ЗОНА ЗАМОРОЖЕНА** (D39.136 п.2 + D39.147: разморозка отдельным словом владельца, не привязана к P7); перечень первого касания — в зонном журнале |
|
||||
| Контракт | [CONTRACT_MINOR_SESSION_PROMPT.md](CONTRACT_MINOR_SESSION_PROMPT.md) | ВЫДАН 27.08, минор 0.4.0 → 0.5.0, пункт (2б) очереди D39.156. **Состав — §3 промта, здесь он НЕ пересказан** (один носитель на факт). Ратифицированное исключение: прогресс (строка 200) в минор не входит, едет с паком (2в) — D39.160 |
|
||||
| Контракт | активного НЕТ | минор **0.5.0** ПРИНЯТ и заленджен 27.08 (D39.161): отменённая пер-термная модель снесена, дверь `POST …/bank/corrections` объявлена и выведена из словаря глагола, признак «не построено» машиночитаем. Следующая работа по контракту — ПОСЛЕ монтажа двери паком (2в) |
|
||||
|
||||
Отработанные промты — `archive/prompts/`, отчёты с ревью-шапками — `archive/reports/`. Зонные журналы фронта и платформы — `frontend-PROGRESS.md` / `platform-PROGRESS.md` в их зонах (прогресс зон только там, D39.100).
|
||||
- Чужие зоны — фронт и платформа (читать при касании стыка; каждая ведёт СВОЙ зонный бэклог — единый бэклог их строк не принимает, D39.84): [../frontend/](../frontend/) — веб-интерфейс: промт фронт-сессий + [STACK_DECISIONS.md](../frontend/docs/STACK_DECISIONS.md) (пины версий точными числами и ловушки) + [BACKLOG.md](../frontend/docs/BACKLOG.md) · [../platform/](../platform/) — SaaS control plane: README + [BACKLOG.md](../platform/BACKLOG.md) + `docs/` (зонный журнал `platform-PROGRESS.md` · регистр дефектов · `STACK_DECISIONS.md` с рецептом стенда и инвентарём каналов шва · **[ENGINEERING_STANDARDS.md](../platform/docs/ENGINEERING_STANDARDS.md) — ратифицирован; КАЖДЫЙ промт платформенной сессии обязан на него ссылаться, отступление = пинг** · **[PLATFORM_DIRECTION.md](../platform/docs/PLATFORM_DIRECTION.md) — ратифицированное направление зоны: аутентификация, деньги, стандарты, скорость** · архив промтов).
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Реестр D-нот — карта актуальности v2 (D1–D39.160;
|
||||
# Реестр D-нот — карта актуальности v2 (D1–D39.161;
|
||||
|
||||
> ⚠ **СЛАБОЕ МЕСТО, КОТОРОЕ БЫЛО ЗДЕСЬ (вписано 22.08, ЗАКРЫТО 24.08 — D39.157 п.6).** Колонка ТЕЛА
|
||||
> у нот D39.107…D39.123 говорила «жив», хотя тела уехали в слайс подрезкой D39.139; семнадцать строк
|
||||
|
|
@ -221,4 +221,5 @@
|
|||
| D39.158 | 27.08 | Входная дверь шва закончена: стоп банка — ФЛАЖОК в движке (память предъявленного, схема v16); полоса — два яруса + гардрейл «разрушительное на классе»; класс `write_incomplete`/exit 15; отчёт двери v2; пути решений только конвенцией; эрраты 191(б)/203(з) | жив | живой файл | шов · банк · полоса отказов |
|
||||
| D39.159 | 27.08 | Пак P8-REVIEW принят: числа воспроизведены приёмкой на дереве С лендингом бэкенда (18 пакетов, EXIT=0, скипов 0; регистр 398/96). Ратифицированы норма копии-с-каноном (`ENGINEERING_STANDARDS` §3 п.3), эррата `STACK_DECISIONS` §13 и токен `ОСПОРЕНО(PD-N)` при двусторонней ссылке; статусы трёх спорных строк НЕ пере-открыты. `PD-379` подтверждён чтением, `PD-376` — своей посадкой. Правка гейта формы регистра отклонена замером (краснит 7 законных строк). | ✅ |
|
||||
| D39.160 | 27.08 | Контрактный минор 0.4.0 → 0.5.0 выдан промтом; **сквозная полоса прогресса (строка 200) ИЗЪЯТА из него словом владельца** и едет с платформенным паком (2в) — гейт версии требует совпадения канона и деплоя в момент лендинга, а сервер прогресс по-новому не считает. Цена «одним куском» обнулена заморозкой фронта. Промт прошёл оба рубежа: 9 находок опровергателя, все применены. | ✅ |
|
||||
| D39.161 | 27.08 | Контрактный минор **0.5.0** принят и заленджен: отменённая пер-термная модель снесена из канона и компаньона, дверь `POST …/bank/corrections` выведена из словаря `bank-apply` поле в поле, признак «не построено» машиночитаем, счётчики упразднённой модели сняты. Константа платформы поднята тем же коммитом — батарея зоны 18/EXIT=0. Ошибка промта про «поля навсегда нули» найдена исполнителем и вынесена `PD-399`. | ✅ |
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Журнал решений оркестратора — контракт D1–D39.160 (живой файл: карта · эрраты · живые тела · голова D39.124+ (подрезка D39.139); тела закрытых эр — в слайсах `docs/archive/architecture/`, указатель ниже; реестр всех нот — `05-decisions-index.md`)
|
||||
# Журнал решений оркестратора — контракт D1–D39.161 (живой файл: карта · эрраты · живые тела · голова 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 живёт ниже в этом файле (голова D39.106+).
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
> ⚠ **Эррата 27.08-г (D39.159 п.8):** отказ от правки `n != shape` СТОИТ, но обоснование было шире истины — «краснит семь ЗАКОННЫХ строк» неверно для двух из них. `PD-99` несла корректно markdown-экранированные черты (виноват был парсер, не строка), а `PD-197` — сырую черту в регексе, которая молча сдвигала ВЕС, потому что он читался с конца, а от конца далеко. Названный там остаточный риск ПОСТРОЕН 27.08 на эмпирике зоны (три молчаливых случая за двое суток): парсер уважает экранирование · вес регистра читается с НАЧАЛА · гейт `tail_vocab` судит словарь хвоста. Проверено подсадкой; числа не сдвинулись. Строка `PD-398` остаётся открытой на отсутствие автоматического пина у самого гейта.
|
||||
> ⚠ **Эррата 27.08-д (D39.159 п.7) — норма §3 п.8 применена к СОБСТВЕННОМУ бэклогу, три строки переsуждены.** Свип грепом по путям лендинга `d1eb8a9` нашёл, что лекарство трёх строк уже в дереве. **212 ЗАКРЫТА и снята с таблицы:** голый `yaml.Unmarshal` заменён строгим `seed.DecodeFile` (`internal/membank/memseed.go:51`, код называет строку по номеру), пин двойной — `internal/seed/decode_test.go:16` и `internal/membank/seedlint_test.go:39`. **218 ЗАКРЫТА и снята:** три тест-хелпера снесены, `miner_parity_test.go:35` говорит «the old $HOME/books address is dead». **199 СУЖЕНА:** движковая половина цепи доставки правок банка ПОСТРОЕНА, открыт только платформенный конец (пункт 2в очереди) — прежняя формулировка «канал НЕ построен» отправляла бы следующий пак строить построенное. Бэклог 177 → 175. ⚠ Норма окупилась на своём авторе в тот же день, что и на чужом.
|
||||
> ⚠ **Эррата 27.08-е (D39.159 п.8, финал): `PD-398` ЗАКРЫТА — у построенного гейта появился пин.** `selftest_tail_vocab()` гоняется на каждом `--check`, четыре утверждения, проверен ПОСАДКОЙ трёх мутаций самого гейта (все три пойманы, базовая линия молчит). ⚠ Первая редакция пина молчала на одной из трёх: утверждение про вес проверяло `cells()`, а мутация меняет то, чем пользуется `register()`. Пин, проверенный одним прогоном вместо посадки, — ровно тот класс, который эта строка описывает; поймано только потому, что посадку сделал. Регистр 96 → 95 открытых.
|
||||
> ⚠ **Эррата 27.08-ж (D39.160 п.2) — ошибка ОРКЕСТРАТОРА, найденная исполнителем пака.** Нота утверждала, что после сноса отменённой двери «канон и деплой СОВПАДУТ точно», а промт минора (§3.2-бис) — что счётчики `pending_decisions`/`complete` «навсегда нули». **Верно по ПУТЯМ, неверно по ПОЛЯМ:** проекция `GET /bank` продолжает их слать, и это не нули — `bankCountsTx` (`platform/internal/pgstore/readmodel.go:330`) считает `proposed`-строки, о чём говорит её собственный комментарий. Клиент 0.5.0 лишние поля игнорирует, но аллоулист-норма нарушена до монтажа (2в). Носитель — `PD-399`. ⚠ Контрактная сессия принесла это ПИНГОМ по §11 промта, вместо того чтобы тихо подогнать работу под неверную посылку; это и есть поведение, которого норма требует.
|
||||
|
||||
> ⚠ **Навигация (актуализация 07.08, эра D39.1xx):** append-only-дисциплина (D23.3) означает, что
|
||||
> НЕВЕРНЫЙ ФАКТ внутри старой ноты не переписывается, а получает эрратау — и тогда он опасен ровно
|
||||
|
|
@ -770,3 +771,38 @@
|
|||
воспитала бы у экрана подписи отменённую пер-термную модель ТЕМ ЖЕ минором, который её сносит.
|
||||
6. **Ошибка буквы, исправленная тем же рубежом:** пункт строки 203 про `/auth` — **(а)**, не (е);
|
||||
у строки открыт ещё (к), в пак не входящий.
|
||||
|
||||
## D39.161 — КОНТРАКТНЫЙ МИНОР 0.5.0 ПРИНЯТ И ЗАЛЕНДЖЕН: отменённая модель снесена, дверь правок объявлена и выведена из глагола, счётчики сняты (27.08, оркестратор №19). ✅
|
||||
|
||||
Пак исполнен по промту `CONTRACT_MINOR_SESSION_PROMPT.md` (D39.160) и принят. Механика — канон,
|
||||
компаньон и отчёт `docs/CONTRACT_MINOR_REPORT.md`; здесь решённое и то, что добавила приёмка.
|
||||
|
||||
1. **Канон 0.5.0 заленджен; константа `platform/internal/httpapi/capabilities.go:13` поднята мной ТЕМ
|
||||
ЖЕ коммитом**, как обязывала D39.160. Проверено исполнением на копии-с-каноном: до подъёма гейт
|
||||
краснеет предсказанным текстом («announces contract 0.4.0 and the ratified canon is 0.5.0»), после
|
||||
подъёма ПОЛНАЯ батарея зоны платформы — **18 пакетов, EXIT=0, линтер 0 issues**. Лендинг чужую
|
||||
зону красной не оставляет.
|
||||
2. **Снос полон, пере-проверен приёмкой:** в каноне ноль вхождений пути, операции и трёх схем
|
||||
отменённой модели; счётчики сняты. Канон парсится, **дублей ключей 0**, висячих `$ref` 0, сирот 0
|
||||
(две «сироты» — `securitySchemes`, подключаемые блоком `security`, а не ссылкой). В компаньоне три
|
||||
упоминания — все ИСТОРИЧЕСКИЕ (баннер ломающего минора · провенанс · зачёркнутая строка таблицы,
|
||||
которую промт велел сохранить как историю).
|
||||
3. **Дверь `POST /books/{bookId}/bank/corrections` выведена из словаря, а не сочинена** — сверено
|
||||
приёмкой поле за полем: `BankCorrection` совпадает с `membank.Decision` один в один. Раскладка
|
||||
отказов сделана по существу: **409 `bank_corrections_refused`** = класс 14 (всё-или-ничего, отказ
|
||||
всего документа), **503 `bank_corrections_incomplete`** = класс 15 с «слать ТОТ ЖЕ документ» и
|
||||
названной сходимостью, **409 `run_in_flight`** = класс 12, **413** = потолок 1 МиБ, 5000 — в схеме.
|
||||
`preview` = `--dry-run`. Отказанные решения едут `refusals[]` в конверте `Problem`, а не списком в
|
||||
квитанции 200, — следствие всё-или-ничего.
|
||||
4. **Машиночитаемый признак «не построено» — `Capabilities.bank_corrections_enabled`, false ⇒ 404.**
|
||||
Требование промта исполнено: дверь объявлена честно, а не голой.
|
||||
5. **Третий носитель счётчиков нашла САМА сессия** — `EventBank`, которого в списке промта не было.
|
||||
6. **Ошибка ОРКЕСТРАТОРА, найденная исполнителем** (эррата 27.08-ж): «после сноса канон и деплой
|
||||
совпадут точно» и «поля навсегда нули» верны по ПУТЯМ и неверны по ПОЛЯМ — проекция `GET /bank`
|
||||
продолжает слать `pending_decisions`/`complete`, и это живой счёт `proposed`-строк. Носитель —
|
||||
`PD-399`, лечение пунктом (2в). ⚠ Сессия принесла это ПИНГОМ, а не подогнала работу под неверную
|
||||
посылку промта; это ровно то поведение, которого требует CLAUDE.md.
|
||||
7. **Строка бэклога 206 ЗАКРЫТА и снята с таблицы** — она и была заказом этого минора («канон объявляет ручку, которой зона больше не отдаёт»); лекарство в дереве, пере-проверено приёмкой грепом: ноль вхождений. Её якорь `openapi.yaml:469` умер именно оттого, что путь снесён. Строка **203** пункт (а) исполнен, открытым по ней остаётся (к). Бэклог 176 → 175.
|
||||
8. **Щели v1 названы, не спрятаны** (в каноне и компаньоне): машинного словаря причин отказа нет ·
|
||||
алиасы клиенту невидимы · счёт `signature` недостижим без правки или превью · на 409 не едут
|
||||
`preexisting`/`signature`, хотя движок их печатает — названная цена, не забывчивость.
|
||||
|
|
|
|||
|
|
@ -51,11 +51,31 @@ cmp-сверка обязательна (D39.138 п.3).
|
|||
> сверку со стандартами, разобранные альтернативы). При расхождении по ФОРМЕ побеждает YAML;
|
||||
> при вопросе «почему так» — этот файл.
|
||||
>
|
||||
> **Статус: РАТИФИЦИРОВАН.** 0.2.0 (D39.115, 08.08) · 0.2.1 (D39.123, 09.08) · 0.2.2 (D39.129,
|
||||
> 10.08) · 0.2.3 (D39.135, 15.08) · **0.3.0 (D39.138, 16.08) — ломающий минор по целостному
|
||||
> ревью research/28** · **0.4.0 (D39.152, 20.08) — синк с платформой, §6в**. Дом канона — этот каталог;
|
||||
> **Статус: РАТИФИЦИРОВАН по 0.4.0 включительно.** 0.2.0 (D39.115, 08.08) · 0.2.1 (D39.123, 09.08) ·
|
||||
> 0.2.2 (D39.129, 10.08) · 0.2.3 (D39.135, 15.08) · **0.3.0 (D39.138, 16.08) — ломающий минор по
|
||||
> целостному ревью research/28** · **0.4.0 (D39.152, 20.08) — синк с платформой, §6в** ·
|
||||
> **0.5.0 (сдано контрактной сессией 27.08, ратификация — акт оркестратора при лендинге) — снос
|
||||
> отменённой пер-термной модели подписи (PD-370) + дверь правок банка, выведенная из построенного
|
||||
> глагола движка (D39.158), + предупреждение о конверте вне `/v0`; вывод и провенанс — §2.19,
|
||||
> отчёт сессии — `docs/CONTRACT_MINOR_REPORT.md`**. Дом канона — этот каталог;
|
||||
> `frontend/docs/api-contract/openapi.yaml` — байт-зеркало.
|
||||
>
|
||||
> ⚠ **0.5.0 ломающий по построению (мажор `0`): снесены путь `POST …/bank/decisions` и три его
|
||||
> схемы, из `BankPage` и `EventBank` сняты `pending_decisions`/`complete` (§2.19-бис), в
|
||||
> `Capabilities.required` добавлен `bank_corrections_enabled`.** Дверь
|
||||
> `POST …/bank/corrections` объявлена И НЕ ОБСЛУЖИВАЕТСЯ до платформенного пака (2в очереди
|
||||
> D39.156): деплой честно говорит это флагом `bank_corrections_enabled: false` и отвечает `404`.
|
||||
>
|
||||
> ⚠ **Совпадение канона и деплоя при лендинге — точное по ПУТЯМ, не по полям** (находка
|
||||
> опровергателя этой сессии, сверена с кодом): свою половину ПУТИ платформа снесла 22.08, а
|
||||
> проекция `GET /bank` ещё кладёт на провод снятые 0.5.0 счётчики (`wireBankPage`,
|
||||
> `platform/internal/httpapi/reading.go` — `PendingDecisions`/`Complete`; комментарий «the wire
|
||||
> fields are the canon's» с этого минора ложен). Клиент 0.5.0 лишние поля игнорирует по общему
|
||||
> правилу, но аллоулист-норма нарушена, пока пак (2в) не снимет их одной строкой проекции —
|
||||
> носитель: пункт (2в) очереди D39.156, §2.19-бис. Гейт версии
|
||||
> (`TestTheAnnouncedContractVersionIsTheOneTheCanonRatified`) этого класса не ловит — он сверяет
|
||||
> только номер.
|
||||
>
|
||||
> ⚠ **0.4.0 ломающий ровно по одному месту, и это ЗАМЕРЕНО, а не объявлено.** Дифф генерённых
|
||||
> типов 0.3.0 → 0.4.0 (`openapi-typescript@7`, комментарии отброшены) — **одна строка:**
|
||||
> `Run.stop_requested: boolean`. Всё остальное в этой редакции — проза канона, одна запись в
|
||||
|
|
@ -120,8 +140,9 @@ cmp-сверка обязательна (D39.138 п.3).
|
|||
получило «No results» — то есть **все 29 находок ревью были смысловыми, ни одной синтаксической**.
|
||||
А К-11 замерил, что `if`/`then` OpenAPI 3.1 генератор типов ИГНОРИРУЕТ. Отсюда правило,
|
||||
действующее с 0.3.0: **любое межполевое или условное правило обязано быть записано И схемой, И
|
||||
словами в описании поля** — схема защищает сервер, слова доезжают до клиента. Мест таких три:
|
||||
`BankDecision.dst`, `Unit.target`, агрегаты `BankPage`.
|
||||
словами в описании поля** — схема защищает сервер, слова доезжают до клиента. Мест таких три
|
||||
(0.5.0 сменил первое: схема `BankDecision` снесена вместе с моделью): `BankCorrection` (`dst` при
|
||||
`approve`/`decline` и взаимоисключение `id`/кортежа), `Unit.target`, агрегаты `BankPage`.
|
||||
|
||||
Инструменты и пины — `STACK_DECISIONS.md` §3 и бэклог зоны.
|
||||
|
||||
|
|
@ -349,18 +370,23 @@ every proposed term is promoted or rejected» ОПРОКИНУТА D39.144 —
|
|||
квитанции POST. Экран, перезагруженный посреди стопа, мог прочитать ВЕСЬ банк и не узнать, сколько
|
||||
решений осталось; единственная реализация выводила признак как `left === 0`
|
||||
(`frontend/src/mock/handlers.ts:203-204`) — инвариант, которого контракт не объявлял. Теперь
|
||||
`pending_decisions` и `complete` отвечает и `GET /bank`, и квитанция, и кадр `bank`.
|
||||
⚠ **(0.5.0: клауза счётчиков этого пункта ПЕРЕКРЫТА — прежде чем читать дальше.)** Сняты со
|
||||
всех трёх носителей; честный счёт едет квитанцией двери правок (`signature`), разбор —
|
||||
§2.19-бис. Исторический текст 0.3.0: `pending_decisions` и `complete` отвечает и `GET /bank`,
|
||||
и квитанция, и кадр `bank`.
|
||||
|
||||
`POST /runs/{id}/resume` — нормативная операция, а не резерв.
|
||||
|
||||
⚠ **Открытый остаток, НЕ закрытый батчем и вынесенный вопросом (см. отчёт батча §8).** Чтение банка
|
||||
отвечает СКОЛЬКО решений осталось (`pending_decisions`, `complete`), но не КАКИЕ строки уже решены:
|
||||
`TermStatus` — состояние строки банка, а решение живёт отдельной таблицей (`bank_decisions`,
|
||||
`00002_readmodel.sql`), и на провод оно не проецируется ни одним полем. Экран подписи, перезагруженный
|
||||
посреди стопа, поэтому знает «осталось 17 из 300» и не знает, какие семнадцать. Лечение — одно
|
||||
поле `BankTerm.decision` (`approve`/`decline`/`null`), и оно НЕ добавлено: слово владельца 15.08
|
||||
«добавочные поля `BankTerm` НЕ заводить» (D39.136 п.4б) прямо это запрещает, а промт батча повторяет
|
||||
запрет. Найдено холодным потребителем; решение — за владельцем.
|
||||
⚠ **Открытый остаток (заведён батчем 0.3.0, пере-привязан 0.5.0 к живым носителям).** Чтение банка
|
||||
не отвечает, КАКИЕ строки уже решены: `TermStatus` — состояние строки, решения живут файлами
|
||||
движка (дельта и список отказов — `18-bank-ontology.md`, роли ИСТОЧНИКОВ), и на провод пер-строчная
|
||||
решённость не проецируется ни одним полем; счётчик «сколько осталось» с 0.5.0 отвечает только
|
||||
квитанция двери правок (`signature`), не чтение. Экран подписи, перезагруженный посреди стопа,
|
||||
знает суммарный счёт лишь после первого своего вызова двери и не знает, какие строки решены.
|
||||
Лечение — одно поле `BankTerm.decision`, и оно НЕ добавлено: слово владельца 15.08 «добавочные поля
|
||||
`BankTerm` НЕ заводить» (D39.136 п.4б) прямо это запрещает; публикация решённости движком — заказ
|
||||
на день, когда экран подписи закажут (`18-bank-ontology.md`, «Чего эта форма НЕ несёт»). Решение —
|
||||
за владельцем.
|
||||
|
||||
### 2.10. Ревизия — ✓ у ре-синка, ◆ у чтений
|
||||
|
||||
|
|
@ -590,7 +616,7 @@ CORS-слоя в платформе нет вовсе: preflight `OPTIONS` с ч
|
|||
|
||||
**Форма, выбранная батчем.**
|
||||
|
||||
- **`code` — корневой, закрытый, 16 значений; `cause.code` — второй уровень, НЕ закрытый.** Это и
|
||||
- **`code` — корневой, закрытый, 16 значений (0.3.0; 0.5.0 добавил два банковских — §2.19); `cause.code` — второй уровень, НЕ закрытый.** Это и
|
||||
есть механизм расширяемости: новый частный случай добавляется в `cause`, не ломая клиентов, — то,
|
||||
чем Microsoft закрывает «новый код = ломающее изменение». Второй уровень сделан ВЛОЖЕННЫМ объектом,
|
||||
а не соседним полем: так граница «стабильное / расширяемое» видна структурно, и всё, что внутри
|
||||
|
|
@ -648,6 +674,153 @@ the original request was never applied». Каждый `POST /books` созда
|
|||
считается по объявленным частям», дофикс ФБ-8 16.08) этой правкой ПЕРЕКРЫТА и оставлена только как
|
||||
история — читать по канону.
|
||||
|
||||
### 2.19. Дверь правок банка (0.5.0) — ✓ выведено из `tmctl bank-apply`; форма HTTP — ◆ этой сессии
|
||||
|
||||
Движковая половина ПОСТРОЕНА и заленджена (D39.158, коммит `d1eb8a9`): `tmctl bank-apply` — $0-глагол
|
||||
со словарём (`backend/internal/membank/decisions.go`), отчётом (`pipeline/bankdecisions.go`,
|
||||
`BankDecisionsReport`) и полосой отказов 10–19 (`cmd/tmctl/main.go:69-75`). Канон 0.5.0 не сочиняет
|
||||
тело двери — он ПРОЕЦИРУЕТ этот словарь на провод; платформа при монтаже (пак 2в очереди D39.156)
|
||||
реализует объявленное: контракт-JSON → документ движка → спавн глагола → отчёт → ответ. Перевод
|
||||
словаря на шве — закон (17-seam-inbound-law п.6), поэтому ниже каждая форма провода названа с её
|
||||
движковым источником.
|
||||
|
||||
**Выведено (✓), с грунтом:**
|
||||
|
||||
| Провод | Источник в движке |
|
||||
|---|---|
|
||||
| `approve`/`decline`, третьего нет; «un-decide» не существует — решение ЗАМЕНЯЕТСЯ | `decisions.go:33-36,59-63` |
|
||||
| идентичность: `id` XOR полный кортеж; оба сразу — отказ («назвать два разных терма одним решением») | `decisions.go:88-95,469-472` |
|
||||
| неизвестный кортеж = ДОБАВЛЕНИЕ терма, не ошибка; неизвестный `id` — отказ с причиной «другая книга или пере-резка» | `decisions.go:94-96` («the form in which a term the bank does not have yet is added»), `:473-477` |
|
||||
| `dst` обязателен и непуст при `approve`, запрещён при `decline` (вместе с `kind`) | `decisions.go:100-103,459-468` |
|
||||
| `kind`: отсутствие = «не решено», сбросить в `null` нельзя. ⚠ Провод типизирует его `TermKind` (5 значений — словарь ЧТЕНИЯ, `terminology/classify.go`), движок в этой двери принимает любую непустую строку: сужение провода — ◆, см. список ниже | `decisions.go:104-106,632-634` |
|
||||
| `note`: принимается, НЕ публикуется; отсутствие = «не решено»; опустошить нельзя, только заменить | `decisions.go:107-115` |
|
||||
| `aliases` НЕ принимаются; promotion несёт кластер минера дальше; decline алиаса ПОДПИСАННОГО терма — инертен и отказан, судится по РЕЗУЛЬТАТУ документа (алиас неподписанной строки деклайнится законно — `aliasOwner` пропускает `status != approved`; decline терма и его алиаса одним документом — оба принимаются). ⚠ На ПРОВОДЕ `BankTerm` алиасов НЕ публикует (снос — слово владельца, D39.136 п.4б): клиент не видит, что поверхность — алиас; отказ приходит `refusals[].detail`. Названная щель | `decisions.go:78-80`, `termFromBank`, `refuseInertDeclines` (`:399-423`), `aliasOwner` (`:641-659`) |
|
||||
| `gender`/`speech`/`decl` — полей НЕТ: сид-онли, производителя нет | `decisions.go:81-83` (строка бэклога 210) |
|
||||
| узость окна v1, ОБЕ половины: approve нового кортежа добавляет строку и оставляет старую; decline снимает ВСЕ окна поверхности | `decisions.go:85-87` |
|
||||
| сид-терм не правится этой дверью: approve — клэш с подписанной базой, БЕЗУСЛОВНО; decline — отказ ТОЛЬКО ИНЕРТНОМУ (условие `!deltaHoldsSurface`: когда правки книги держат свою строку той же поверхности, decline принимается и снимает её — ремонт ливлока «сид-алиас и строка правок делят firing key») | `decisions.go:357-377` (условие — `:374`; перенос базы — строка 192) |
|
||||
| `book_id` в теле обязателен И отдельно сверяется с книгой — две разные проверки | `decisions.go:121-124,141-143` и `bankdecisions.go:267-269` |
|
||||
| неизвестное поле — громкий отказ | `DecodeDecisions`, `DisallowUnknownFields` (`decisions.go:133`) — зеркало 17-seam-inbound-law п.4 |
|
||||
| всё-или-ничего; один отказ отклоняет набор | `ApplyDecisions` (`decisions.go:282-307`) |
|
||||
| дубли: один вызов решает терм один раз; decline поверхности против второго решения той же поверхности — противоречие | `duplicateDecisions` (`decisions.go:520-554`) |
|
||||
| окно, кончающееся раньше начала, — отказ («терм записан и нигде не сработает») | `decisions.go:486-492` |
|
||||
| идемпотентность: `already_applied` + байтовый no-op (`changed: false`), ретрай безопасен | `decisions.go:221-224`, `bankdecisions.go:209-221` |
|
||||
| превью прежде мутации | `--dry-run`, `bankdecisions.go:206-208`; закон шва п.7 |
|
||||
| `depth` — поле отчёта: решение доезжает до редакторской волны и НЕ пере-формирует черновик | `DecisionDepth` (`decisions.go:51-57`), `bankdecisions.go:49-51` |
|
||||
| `signature{surfaces, undecided, unreadable}` и его запретительный контракт | `SignatureState` (`bankdecisions.go:88-118`), D39.144 |
|
||||
| потолки: 5000 решений на акт («split it») и 1 МиБ документа | `bankdecisions.go:640-647` (`maxDecisions`), `:637-639` (`maxDecisionsBytes`) |
|
||||
| `write_incomplete`: документ принят, запись не довершена — слать ТОТ ЖЕ документ | класс 15 (`main.go:74`), ретрай сходится через байтовый no-op |
|
||||
| занятый арбитр (живой прогон держит флок) — «подожди», не «сломано» | класс 12 (`main.go:71`), `bankdecisions.go:159-163` |
|
||||
|
||||
**Решения этой сессии (◆), каждое с доводом; подробный разбор и отвергнутые альтернативы — отчёт
|
||||
`docs/CONTRACT_MINOR_REPORT.md`:**
|
||||
|
||||
- **Один `POST /books/{bookId}/bank/corrections` с документом целиком.** Глагол принимает документ и
|
||||
отвечает отчётом атомарно; ресурсная модель (PATCH строк) врала бы про атомарность и про то, что
|
||||
строки банка этим вызовом не меняются.
|
||||
- **Имя `corrections`, не `decisions` и не `edits`.** Модель владельца: пер-термно существует
|
||||
ПРАВКА. `edit` — имя волны движка, запрещённое на проводе гейтом §5 (утечка была бы неотличима от
|
||||
волны грепом); `decisions` вернуло бы имена снесённых схем в сгенерированные типы с другой
|
||||
семантикой — генерённый диф читался бы как правка старой двери, а не как новая.
|
||||
- **`preview` — обязательное поле тела** (не query, не второй путь): закон шва п.7 требует проекцию
|
||||
прежде мутации; обязательность делает выбор акта явным в каждом вызове (прецедент тотальной
|
||||
обязательности — `stop_requested`, §6в J; в запросах — `RunRequest.stop_for_signing`).
|
||||
- **Кортеж в проводной форме требует ВСЕ четыре члена** (движок дефолтит опущенные): опущенный
|
||||
`sense` молча называет ДРУГОЙ ключ — а неизвестный ключ здесь легально ДОБАВЛЯЕТ терм, то есть
|
||||
цена умолчания — не отказ, а тихая параллельная строка. Проводная форма строже движковой ровно на
|
||||
ширину этой ловушки; `null`-семантика окна — как у `BankTerm` (проекция `null`→`0` — платформа,
|
||||
закон шва п.6).
|
||||
- **`kind` на проводе сужен до `TermKind`** (движок в этой двери принимает любую непустую строку,
|
||||
словарь из пяти значений живёт в классификаторе чтения — `terminology/classify.go`): дверь
|
||||
переиспользует ОПУБЛИКОВАННЫЙ словарь чтения, а не движковую свободу — kind, которого чтение не
|
||||
знает, нельзя и установить через провод. Цена: сид-авторский kind вне пятёрки через дверь не
|
||||
повторить (named); рост словаря = минор, тем же правилом, что у самого `TermKind`.
|
||||
- **Раскладка полосы отказов на провод:** 14 → `409` `bank_corrections_refused` (+`refusals[]`),
|
||||
15 → `503` `bank_corrections_incomplete` («слать тот же документ»), 12 → `409` `run_in_flight`,
|
||||
10/11/13 → не пер-операционные (деплой сломан — `5xx` общего вида); движковый исход `stopped`
|
||||
(SIGTERM до первого байта: ничего не записано) пер-операционного кода тоже не имеет — снаружи
|
||||
это рестарт деплоя, `5xx`, ретрай сходится; потолок 1 МиБ → `413`, потолок 5000 и вся валидация
|
||||
формы → `400` `invalid_request`. 14 и 15 получили СВОИ корневые коды: разные адресаты ремеди
|
||||
(пере-решить человек / повторить машина), совет промта принят.
|
||||
⚠ **Три обязательства монтажа (2в), которые раскладка создаёт — названы, чтобы не потерялись
|
||||
(находки опровергателя этой сессии, сверены с кодом):** (а) оба потолка движок классифицирует
|
||||
КЛАССОМ 14 («the document is well-formed and the deployment is fine», `bankdecisions.go:656-673`),
|
||||
а канон раскладывает их в `400`/`413` — платформа обязана мерить оба на ПРОВОДНОЙ форме до
|
||||
спавна; остаточный случай (HTTP-тело < 1 МиБ, отрендеренный документ шва — больше) падает в
|
||||
`409` `bank_corrections_refused`, чьё описание несёт «a set larger than the service applies in
|
||||
one act»; (б) канонный `400` с `errors[/book_id]` на несовпадение тела с путём производит
|
||||
ПЛАТФОРМА до спавна — движковая сверка того же факта (`bankdecisions.go:267-269`) есть класс 10,
|
||||
«конфиг вызывающего», и в `400` сама не раскладывается; (в) свои вызовы двери по одной книге
|
||||
монтаж СЕРИАЛИЗУЕТ сам: флок движка держит любой глагол, включая второй `bank-apply` и превью
|
||||
(класс 12 = «another tmctl owns this project», не «книга переводится»), и без сериализации
|
||||
транзиентный держатель отвечал бы `run_in_flight` — словом про прогон, которого нет. С
|
||||
сериализацией снаружи остаётся ровно живой прогон, и слово точное.
|
||||
- **Причины пер-решенческих отказов едут `refusals[].detail` developer-facing и НЕ показываются**
|
||||
(как `Problem.detail`): у движка причины — свободный текст (`RejectedDecision.Reason`), машинного
|
||||
словаря причин нет, а замораживать в каноне пересказ — вторая копия растущего словаря. Названная
|
||||
щель: продуктовые фразы отказов появятся, когда движок даст причинам машинные имена.
|
||||
- **Что НЕ пошло на провод из отчёта:** пути файлов (`files`) и пофайловая правда записи
|
||||
(`written_delta`/`written_rejects`) — серверная топология, ремеди клиента от неё не зависит ·
|
||||
`canonical_rewrite*` — предупреждение оператору файлов, а не пользователю продукта ·
|
||||
тексты `preexisting_problems` — свободный текст движка; на провод идёт СЧЁТ
|
||||
(`preexisting_faults`), потому что «книга уже больна, следующий прогон умрёт у банка»
|
||||
пользователю нужен, а формулировки — нет · `replaced[]` — свободный текст; на провод идёт булев
|
||||
`displaced` (перекрыл ли ты чьё-то раннее слово — свойство, ради видимости которого поле и
|
||||
существует) · `mode` — раскладывается на `preview`/`changed`/HTTP-коды; остаток `stopped`
|
||||
разобран в пункте раскладки выше · `decisions_version` / `report_version` — версии ДОКУМЕНТОВ
|
||||
ШВА, на проводе их место занимает `contract_version`. ⚠ Отдельно названное УСЕЧЕНИЕ: на отказе
|
||||
(`409`) движок печатает ПОЛНЫЙ отчёт (с `preexisting` и `signature` — «its report of reasons IS
|
||||
its product»), а провод несёт только `refusals[]`: конверт `Problem` расширяется членами про
|
||||
отказ, не квитанцией. Книга, каждый документ которой отказан, своё «уже больна» через дверь не
|
||||
покажет — названная цена v1, не забывчивость.
|
||||
- **`depth: edit_wave → refinement`:** движковая константа несёт имя волны — на провод идёт
|
||||
продуктовое слово с тем же смыслом, открытым словарём; обе половины смысла (следующий прогон;
|
||||
черновик не пере-формируется) продублированы словами в описании — это первое, о чём экран соврал
|
||||
бы.
|
||||
- **`signature` опубликован ВМЕСТЕ с запретительным контрактом** (обе половины D39.144): без запрета
|
||||
экран подписи при разморозке выучил бы из канона «доведи число до нуля» — отменённую модель через
|
||||
чёрный ход; без положительной половины поле выглядело бы бесполезным и его бы не строили. `map`
|
||||
(путь карты) на провод не идёт; «карты ещё нет» выражено `signature: null`.
|
||||
- **Признак «не построено» — `Capabilities.bank_corrections_enabled`** (прецеденты:
|
||||
`intake_enabled` — булев с объявленным `404`, `export_formats` — «пусто = не построено»).
|
||||
`Capabilities` годится: один плоский документ деплоя, одинаковый для всех аккаунтов, читается до
|
||||
предложения UI. Без признака минор воспроизвёл бы PD-370 тем же коммитом, которым закрывает.
|
||||
- **Квитанция — НЕ чтение:** ни `revision`, ни `structure_version` не едут — вызов не двигает
|
||||
read-модель (вид банка пересобирается на границе прогона, `18-bank-ontology.md`), кадр `bank` не
|
||||
испускается, и канон говорит это прямо, чтобы «исправил, а банк не изменился» читалось как
|
||||
корректность, а не как баг.
|
||||
|
||||
### 2.19-бис. Счётчики `pending_decisions`/`complete` — СНЕСЕНЫ (0.5.0)
|
||||
|
||||
Оба поля кормились платформенной таблицей `bank_decisions`, чей write-путь снесён 22.08 вместе с
|
||||
пер-термной моделью: новая дверь пишет файлы движка, не эту таблицу. ⚠ **«Поля навсегда нули»
|
||||
(буква промта пака) опровергнута кодом при вычитке опровергателем — на деле ХУЖЕ нулей:**
|
||||
`pending_decisions` считается как «proposed-строки, которых не коснулось ни одно решение», а
|
||||
касаться нечем — то есть это число ВСЕХ proposed-строк банка, живое на каждой пересборке, и
|
||||
`complete` вырождается в «предложений нет вовсе» (`platform/internal/pgstore/readmodel.go`,
|
||||
`bankCountsTx` — комментарий признаёт это прямо). Замороженно-правдоподобное число учит
|
||||
отменённой модели живым счётчиком. Из трёх исходов (снести · пере-определить на новую дверь ·
|
||||
пометить) выбран СНОС со всех трёх носителей (`BankPage`, `EventBank`, квитанция — вместе со
|
||||
схемой):
|
||||
|
||||
- **пере-определить нельзя**: честный счёт нерешённости — движковый (`SignatureState`: карта
|
||||
последнего стопа против файлов решений); read-модель платформы его НЕ вычислит, не пере-реализовав
|
||||
движковый закон у себя, что запрещено (17-seam-inbound-law п.6; `18-bank-ontology.md`, «Чего эта
|
||||
форма НЕ несёт»);
|
||||
- **пометить («пока нули») нельзя**: поле, обязательное в схеме и вечно лгущее нулём, — это ровно
|
||||
класс PD-370 («канон объявляет — деплой не обслуживает»), только в поле вместо пути;
|
||||
- ратифицированная проза «Signing the bank is ONE act over the whole of it» при сносе СОХРАНЕНА в
|
||||
описании `listBankTerms`; фраза «marked unverified inside the service» из того же абзаца снята —
|
||||
это обещание без носителя, снятое ещё ФБ-8 (§6б) и уцелевшее в одном месте.
|
||||
|
||||
Цена, названная честно — ТРЕМЯ половинами: (1) чтение банка больше не отвечает «сколько
|
||||
осталось» — экран узнаёт счёт из квитанции двери правок (`signature`), то есть только имея что
|
||||
послать или что превьюировать; (2) **до монтажа пака (2в) счёт недоступен НИГДЕ** — дверь не
|
||||
обслуживается, чтение не отвечает: это названная цена окна между минорами, а не пробел; (3) до
|
||||
того же монтажа проекция `GET /bank` деплоя ещё шлёт снятые поля (см. ⚠ шапки — лишние поля,
|
||||
клиент их игнорирует; снимает монтаж). Возврат счёта в чтение — день, когда экран подписи закажут
|
||||
и движок опубликует нерешённость проекцией (`18-bank-ontology.md`, «Чего эта форма НЕ несёт»);
|
||||
сегодняшние носители лгать не будут.
|
||||
|
||||
---
|
||||
|
||||
## 3. Зависимости: чтение → источник → строка бэклога
|
||||
|
|
@ -670,8 +843,9 @@ the original request was never applied». Каждый `POST /books` созда
|
|||
| `PATCH`/`DELETE /books/{id}`, `GET /runs/{id}` | колонки есть | НЕ ПОСТРОЕНО (заведено 0.3.0) | вход P7 |
|
||||
| `GET /books/{id}/chapters`, `/units` | материализация манифеста | **ПОСТРОЕНО P7** — `httpapi/reading.go`, материализатор `internal/readmodel` | закрыто D39.153 |
|
||||
| `GET /books/{id}/notes` | `unit_done` несёт флаг и причину (`runevents.go:126-135`), платформа хранит (`sink.go:227-233`), колонка `notes.reason` заведена под это | канал ЕСТЬ; не хватает карты «причина → код → фраза» (приложение А) и проекции | приложение А + вход P7 |
|
||||
| `GET /books/{id}/bank` | движок пишет сайдкар всего банка (`pipeline/bankexport.go:16-33,72`, D39.122) | движковая половина ПОСТРОЕНА; не хватает проекции платформы | строка 169 · вход P7 |
|
||||
| ~~`POST /bank/decisions`~~ | стоп-механика майнера | ⚠ **НЕ «не построено», а ОТМЕНЕНО**: было построено P7 и СНЯТО 22.08 вместе с пер-термной моделью подписи (D39.144, слово владельца). Канон путь ещё держит — это `PD-370` и строка бэклога **206**, ломающий минор 0.5.0 | отменено D39.144 |
|
||||
| `GET /books/{id}/bank` | движок пишет сайдкар всего банка (`pipeline/bankexport.go:16-33,72`, D39.122) | ⚠ пере-снято 0.5.0: проекция платформы ПОСТРОЕНА (P7 — маршрут в `contractSurface`, `wireBankPage` в `httpapi/reading.go`, `SaveBank` в `pgstore`; прежняя запись «не хватает проекции» устарела при израсходованном носителе «вход P7», D39.153); живой дефект другой — проекция несёт форму 0.4.0 со снесёнными счётчиками, см. ⚠ шапки | пункт (2в) очереди D39.156 — снять счётчики при монтаже |
|
||||
| ~~`POST /bank/decisions`~~ | стоп-механика майнера | ⚠ **НЕ «не построено», а ОТМЕНЕНО**: было построено P7 и СНЯТО 22.08 вместе с пер-термной моделью подписи (D39.144, слово владельца). **0.5.0 снёс и канон-половину — `PD-370` закрыт этим минором**; преемник — строка `POST …/bank/corrections` ниже | отменено D39.144; снесено 0.5.0 |
|
||||
| `POST /books/{bookId}/bank/corrections` | `tmctl bank-apply` — движковая половина ПОСТРОЕНА (D39.158, лендинг `d1eb8a9`) | **объявлено 0.5.0, платформой НЕ обслуживается**: деплой говорит это `Capabilities.bank_corrections_enabled: false` и отвечает `404`; монтаж (перевод словаря, спавн глагола, раскладка отказов) — платформенный пак | **пункт (2в) очереди D39.156** |
|
||||
| `GET /books/{id}/events` (SSE) | эмиттер шва построен (D39.131) | **ПОСТРОЕНО P7** — `httpapi/stream.go`, поток регистрируется ВНЕ слоя сжатия (сжатие буферизует поток — единственное, что канон запрещает этому маршруту) | закрыто D39.153 |
|
||||
| `POST`/`GET /exports` | у движка только stdout-JSON и `--plaintext` (`cmd/tmctl/invocation.go:107`) | НЕ ПОСТРОЕНО с обеих сторон | строка 49 / D29.1 «tmctl export-контракт» |
|
||||
| Условные чтения (`ETag`/304), сжатие | — | **ПОСТРОЕНО P7** — `httpapi/conditional.go`; валидатор считается от БАЙТ ответа. Остаток строки 186 — шаги 3–5 (скоуп кадра, дельта-чтение, `staleTime`) | шаги 1–2 закрыты D39.153 |
|
||||
|
|
@ -682,7 +856,7 @@ the original request was never applied». Каждый `POST /books` созда
|
|||
| Настоящие названия глав (`Chapter.heading` ≠ null) | парсер структуры | НЕ ПОСТРОЕНО | строка 160 (Этап 0) |
|
||||
| `title_raw` / `kind` (глава ↔ фрагмент) | дизайн-пак структуры глав | передано паку, аддитивно | строка 161 |
|
||||
| `ErrorCode.content_refused` (400) **и** `RejectReason.content_refused` | прескрин злоупотреблений | НЕ ПОСТРОЕН ни на одной стороне: в платформе только объявление константы (`httpapi/problem.go:42,94`), `ContractRejectReason` (`ingest/vocabulary.go:59-69`) его не отображает; в движке отказ провайдера живёт как ПРИЧИНА ЗАМЕЧАНИЯ (`disposition.go:60-63` → `Note.code: content_withheld`) и в exit-контракт не выходит — мостá между двумя словарями нет | **строка 94 (ПТ-16)**; там же ограничение числа попыток аккаунта — обязанность падает ВМЕСТЕ с производителем, не раньше |
|
||||
| `decline` в подписи банка доезжает до работы | движок читает файл `mined_rejects` (`pipeline/mining.go`), платформа его не пишет | решение ЗАПИСЫВАЕТСЯ (таблица `bank_decisions`) и на следующем прогоне НЕ применяется — отклонённый термин уезжает авто-строкой | **строка 192** (отложена владельцем); канон предупреждает на `BankDecision.action` |
|
||||
| `decline` в подписи банка доезжает до работы | ⚠ пере-снято 0.5.0: старый носитель («таблица `bank_decisions`, движок её не читает») умер вместе с моделью — таблица снесена 22.08, предупреждение на `BankDecision.action` снесено вместе со схемой | новая дверь пишет ФАЙЛЫ ДВИЖКА (`tmctl bank-apply`), decline доезжает до следующего прогона ПО ПОСТРОЕНИЮ — как только платформа смонтирует дверь; до монтажа двери нет вовсе (флаг `bank_corrections_enabled`) | **пункт (2в) очереди D39.156** |
|
||||
| Снятие замечания (переход «флаг снят») | движок | **НЕДОСТИЖИМО сегодня, проверено чтением движка** — п. H §6в | **PD-298** регистра платформы (`platform/docs/DEFECT_REGISTER.md`) — там строка и живёт; механизма не строим |
|
||||
| Счёт «сделанного» на деплое без второго прохода | платформа | канон 0.4.0 определил «сделано» = последний проход ЭТОГО деплоя; проекция платформы считает жёстко второй проход | **вход P7** (PD-202) |
|
||||
|
||||
|
|
@ -721,7 +895,7 @@ the original request was never applied». Каждый `POST /books` созда
|
|||
| К-8 | Стоп по потолку — каким статусом | **✅ ЗАКРЫТ D39.100** (`paused` + оповещение) |
|
||||
| К-9 | Отказ прескрина не выразим статусами | **✅ ПРИНЯТО ВЛАДЕЛЬЦЕМ 16.08:** прескрин — ещё одна ПРИЧИНА, а не двенадцатый статус: `rejected` + ОДИН грубый код (`content_refused`), максимально абстрактно, без вариации между попытками (§8а). Заведено 0.3.0 |
|
||||
| К-10 | Пофазность у главы | **✅ ЗАКРЫТ — вердикт «НЕ строить»** (D39.138, поправка приёмки research/28 №1). Пофазных счётчиков на главу не будет: фаз на проводе нет. Исходная жалоба («дерево читает ноль всю первую волну») лечится СЕГМЕНТНОЙ логикой `Chapter.units_done` — §2.5 |
|
||||
| К-11 | Условная обязательность полей | **○ ОСТАТОК ИНСТРУМЕНТАЛЬНЫЙ.** 0.3.0 применил правило «схема + слова» к трём местам (`BankDecision.dst`, `Unit.target`, агрегаты `BankPage`) и сделал адресацию `Note` обязательной. Остаток — генератор игнорирует `if`/`then`; лечится сужением на шве клиента (S5) |
|
||||
| К-11 | Условная обязательность полей | **○ ОСТАТОК ИНСТРУМЕНТАЛЬНЫЙ.** 0.3.0 применил правило «схема + слова» к трём местам (`BankDecision.dst`, `Unit.target`, агрегаты `BankPage`; 0.5.0: место `BankDecision.dst` унаследовала `BankCorrection` — там теперь `if`/`then`/`else` и `oneOf` идентичности, все продублированы словами) и сделал адресацию `Note` обязательной. Остаток — генератор игнорирует `if`/`then`; лечится сужением на шве клиента (S5) |
|
||||
| К-12 | Завершение выгрузки: опрос или событие | **✅ ОТВЕЧЕН P0 (опрос)**, подтверждён AIP-151 («The response must not be a streaming response»). 0.3.0 добавил то, без чего опрос не завершался: `state` вместо булева `ready`, `failure_code`, `expires_at` |
|
||||
| К-13 | `paused_reason` не различает две беды | **✅ ЗАКРЫТ D39.132 п.2а** (`null` на проводе ратифицирован). 0.3.0 добил остаток: описание требовало «`null` in every other state», что противоречило ратифицированному «`paused` + `null`» |
|
||||
|
||||
|
|
@ -819,7 +993,8 @@ the original request was never applied». Каждый `POST /books` созда
|
|||
— то есть дефект «замечание, не адресующее ничего» закрыт.
|
||||
- **`Bank.signed`** («доезжает до клиента и не рисуется»). Тот же довод «нет экрана» — экран подписи
|
||||
это S5. `total`, `signed` и `pending_decisions` — три НЕЗАВИСИМЫХ факта: строку можно решить и не
|
||||
подписать (отклонить), поэтому `signed` не выводится из двух других.
|
||||
подписать (отклонить), поэтому `signed` не выводится из двух других. *(0.5.0: `pending_decisions`
|
||||
снесён — §2.19-бис; довод о независимости `signed` стоит и без него.)*
|
||||
- **нагрузка кадров `note` и `bank`** («передаётся и игнорируется»). Игнорировалась она по причине,
|
||||
которую батч устранил: у замечания не было id, поэтому кадр нельзя было сопоставить со списком.
|
||||
С `Note.id` кадр `note` несёт ПРИМЕНИМУЮ ДЕЛЬТУ — это ровно первая ветка правила Б-11а, и снятие
|
||||
|
|
@ -1173,6 +1348,10 @@ announce-once-леджер: ключ `unit:<book>:<wave>:<chapter>:<unit>` не
|
|||
предупреждением на `BankDecision.action` + строкой §3 с носителем — строка 192, отложенная
|
||||
владельцем. Форму не меняю: обещание верное, не выполнена реализация.
|
||||
|
||||
⚠ *(0.5.0: предупреждение умерло вместе со своим носителем — схема `BankDecision` снесена, а дверь-
|
||||
преемник пишет файлы движка сама, так что «не доезжает» перестало быть свойством формы; остаток —
|
||||
монтаж, пункт (2в) очереди D39.156. Абзац сохранён как история синка 20.08.)*
|
||||
|
||||
### L. Мелкое
|
||||
|
||||
- **Ссылка приложения А протухла** — ИСПРАВЛЕНО, и не новым номером, а именем функции
|
||||
|
|
@ -1399,9 +1578,11 @@ heartbeat · форма `id` кадра. Сжатие как ТРЕБОВАНИ
|
|||
| `run_in_flight` | 409 | `pgstore.ErrRunInFlight` (`v0.go:548`) |
|
||||
| `book_not_ready` | 409 | `runs.ErrBookNotReady` (`v0.go:550`) |
|
||||
| `run_not_stoppable` | 409 | `runs.ErrNotStoppable` (`v0.go:554`) |
|
||||
| `run_not_resumable` | 409 | `runs.ErrNotResumable`; `cause`: `ceiling_reached` — **с 0.4.0 это же ответ на ИСЧЕРПАННЫЙ прогон в `stopped`/`awaiting_bank`, где платформа сегодня молча отвечает `202`** (A, PD-282: `runs/reconcile.go`, ветка `exhausted` функции `reopen` и её чтение в `Resume`). ⚠ `bank_decisions_incomplete` удалён D39.144 — гейта полноты нет; построенный гейт полноты (`reconcile.go`, ветка `case "awaiting_bank"`) ДЕМОНТИРУЕТСЯ в P7 |
|
||||
| `run_not_resumable` | 409 | `runs.ErrNotResumable`; `cause`: `ceiling_reached` — **с 0.4.0 это же ответ на ИСЧЕРПАННЫЙ прогон в `stopped`/`awaiting_bank`, где платформа сегодня молча отвечает `202`** (A, PD-282: `runs/reconcile.go`, ветка `exhausted` функции `reopen` и её чтение в `Resume`). ⚠ `bank_decisions_incomplete` удалён D39.144 — гейта полноты нет; построенный гейт полноты ДЕМОНТИРОВАН (0.5.0, сверено с кодом: `reconcile.go`, ветка `case "awaiting_bank"` снимает стоп при ЛЮБОМ состоянии решений, комментарий «this is the built half being dismantled with it») |
|
||||
| `ceiling_unavailable` | 409 | `runs.ErrCeilingOutOfBounds` + `pgstore.ErrInsufficientCredit` (`v0.go:558`); `cause`: `bounds_moved` · `credit_held`; несёт `blocked` |
|
||||
| `idempotency_conflict` | 409 | форма заведена батчем; реализация — P7 |
|
||||
| `bank_corrections_refused` | 409 | объявлен 0.5.0 ДО производителя; производитель — монтаж двери правок (пак (2в) очереди D39.156): раскладка отказа класса 14 движка (`tmctl bank-apply`, документ прочитан и отклонён целиком — пользователь пере-решает) |
|
||||
| `bank_corrections_incomplete` | 503 | объявлен 0.5.0 ДО производителя; тот же монтаж: раскладка класса 15 (документ принят, запись не довершена — слать ТОТ ЖЕ документ, ретрай сходится) |
|
||||
| `content_refused` | 400 | К-9; прескрин не построен (ПТ-16, строка 94). Отказ целой КНИГИ приходит не сюда, а состоянием `rejected` + `reject_reason` |
|
||||
| `service_unavailable` | 503 | `runner.ErrCeilingNotWired` + `runs.ErrRunnerIncomplete` (`v0.go:562`) |
|
||||
| `internal_error` | 500 | `v0.go:518,572` · `middleware.go:68` |
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ openapi: 3.1.0
|
|||
|
||||
info:
|
||||
title: TextMachine API
|
||||
version: 0.4.0
|
||||
version: 0.5.0
|
||||
summary: Ratified contract between the frontend and the TextMachine platform.
|
||||
description: |
|
||||
**RATIFIED contract.** Canonical copy: `docs/architecture/14-api-contract/`;
|
||||
|
|
@ -36,6 +36,12 @@ info:
|
|||
**Signing in is not part of this surface**: session mechanics live outside the version prefix and
|
||||
the flow starts at `GET /auth/login` (companion). A client that meets `401` sends the user there.
|
||||
|
||||
⚠ **Outside the version prefix the error envelope is thinner.** A refusal from `/auth/*` (or any
|
||||
path not under `/v0`) is `problem+json` of the same family but MAY arrive without the mandatory
|
||||
`code` — there a client dispatches on the HTTP status and shows one neutral phrase, and a client
|
||||
generated from this document MUST tolerate the absence rather than fail parsing. The surface and
|
||||
the reasoning live in the companion, §2.14.
|
||||
|
||||
## `Location`
|
||||
|
||||
Every `Location` here is a URI reference resolved against the request's URL (RFC 9110 §10.2.2);
|
||||
|
|
@ -124,7 +130,7 @@ tags:
|
|||
- name: reading
|
||||
description: Chapters, source/translation pairs, notes.
|
||||
- name: bank
|
||||
description: Memory bank and term signing.
|
||||
description: Memory bank — reading it, correcting terms, signing it as one act.
|
||||
- name: runs
|
||||
description: Translation runs, live events, control.
|
||||
- name: export
|
||||
|
|
@ -438,14 +444,17 @@ paths:
|
|||
The bank ordered by source surface then by the term's window, so the several rows of one
|
||||
surface stand together.
|
||||
|
||||
**This read is also the STATE of a signing stop** — informationally, never as a gate:
|
||||
`pending_decisions` and `complete` say how much of the bank a person has touched, and a
|
||||
screen reloaded mid-stop can show it. **Signing the bank is ONE act over the whole of it**,
|
||||
not a march through every row: the stop is lifted by `resumeRun` with the decisions as they
|
||||
stand, and a term nobody touched rides on as the service proposed it, marked unverified
|
||||
inside the service. Per-term decisions are the OPTIONAL correction path, not the unit of
|
||||
**Signing the bank is ONE act over the whole of it**, not a march through every row: the
|
||||
stop is lifted by `resumeRun` with the corrections as they stand, and a term nobody touched
|
||||
rides on as the service proposed it. Per-term corrections
|
||||
(`POST /books/{bookId}/bank/corrections`) are the OPTIONAL correction path, not the unit of
|
||||
signing.
|
||||
|
||||
**How many surfaces still await a word is NOT this read's answer.** This deployment cannot
|
||||
count it honestly from the rows it serves; the honest count rides the correction receipt
|
||||
(`signature` on `BankCorrectionsReceipt`), with the warning attached there — and only
|
||||
there: a client with nothing to correct or preview cannot ask for it yet.
|
||||
|
||||
**Delta read** with `after_version` — a full book's bank is too large to re-read on every
|
||||
change. Page size default: `GET /capabilities`.
|
||||
parameters:
|
||||
|
|
@ -466,43 +475,69 @@ paths:
|
|||
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||
'404': { $ref: '#/components/responses/NotFound' }
|
||||
|
||||
/books/{bookId}/bank/decisions:
|
||||
/books/{bookId}/bank/corrections:
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/BookId'
|
||||
post:
|
||||
tags: [bank]
|
||||
operationId: submitBankDecisions
|
||||
summary: Submit term decisions.
|
||||
operationId: applyBankCorrections
|
||||
summary: Apply term corrections to the memory bank.
|
||||
description: |
|
||||
**Signing is not a row edit**: the bank is rebuilt from its inputs on every run, so a direct
|
||||
write would be erased. A decision is `approve` (with a translation) or `decline`, keyed by
|
||||
term, so re-sending one is harmless.
|
||||
The per-term half of the bank model: signing is ONE act over the whole bank (`resumeRun`),
|
||||
and what exists per term is a CORRECTION — `approve` a rendering, or `decline` a surface so
|
||||
it stops being proposed. The whole document is applied as one act, **all or nothing**: one
|
||||
refused correction refuses the set (`409`, `code: bank_corrections_refused`), because the
|
||||
refused one is the one the user has to see, and a partial save would hide it behind work
|
||||
that appears saved.
|
||||
|
||||
Submission is PARTIAL and accumulates on the server — a closed tab must not cost an hour of
|
||||
work.
|
||||
**Preview first.** Every request says whether it is a preview: `"preview": true` answers
|
||||
the same receipt without changing anything. A client offers the preview before the save —
|
||||
this surface says what it will do before doing it.
|
||||
|
||||
**A `term_id` this book's bank does not hold** — a stale row from before a rebuild, or one
|
||||
belonging to another book — refuses the WHOLE call: `400`, `code: invalid_request`, with an
|
||||
`errors[]` entry pointing at the item (`/decisions/2/term_id`, item code `unknown`). Refusing
|
||||
the batch rather than skipping the row is deliberate: a silently dropped decision reads on
|
||||
the screen as a decision that was saved.
|
||||
**The receipt is not a read.** A correction takes effect on the NEXT run; the bank rows
|
||||
this surface serves (`GET /books/{bookId}/bank`) do not change until a run next rebuilds
|
||||
the bank, no `bank` frame fires, and a client re-reading the bank right after a correction
|
||||
sees it unchanged — correct behaviour, not staleness. What was recorded is in the receipt.
|
||||
|
||||
**While the book is being translated the bank cannot be corrected**: `409`,
|
||||
`code: run_in_flight`. The service reads its own inputs mid-run, and a correction landing
|
||||
under a live run would enter it unpredictably. Wait for the stop or the end.
|
||||
|
||||
**Two ceilings, both hard:** at most **5000** corrections in one act (bound in the schema;
|
||||
a larger set is refused whole — split the document), and at most **1 MiB** of request
|
||||
document (`413`, `code: payload_too_large`).
|
||||
|
||||
**A failed write answers `503`, `code: bank_corrections_incomplete`: the document was
|
||||
ACCEPTED and did not land whole. The remedy is to re-send the SAME document** — the retry
|
||||
converges: what already landed is recognised, not duplicated, and a fully-landed document
|
||||
answers `200` with `changed: false`.
|
||||
|
||||
⚠ **Declared ahead of its serving half.** A deployment that has not mounted this door says
|
||||
so — `Capabilities.bank_corrections_enabled: false` — and answers `404` here. A client
|
||||
checks the flag before offering the correction UI rather than discovering the absence by
|
||||
failing a user's save.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/ClientHeader'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/BankDecisionsRequest' }
|
||||
schema: { $ref: '#/components/schemas/BankCorrectionsRequest' }
|
||||
responses:
|
||||
'200':
|
||||
description: Decisions accepted; the response carries what is left.
|
||||
description: |
|
||||
The receipt — for a preview, what WOULD happen; otherwise what happened. `200` means
|
||||
the whole document was accepted and, unless a preview, landed durably.
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/BankDecisionsResult' }
|
||||
schema: { $ref: '#/components/schemas/BankCorrectionsReceipt' }
|
||||
'400': { $ref: '#/components/responses/BadRequest' }
|
||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||
'403': { $ref: '#/components/responses/Forbidden' }
|
||||
'404': { $ref: '#/components/responses/NotFound' }
|
||||
'409': { $ref: '#/components/responses/Conflict' }
|
||||
'413': { $ref: '#/components/responses/TooLarge' }
|
||||
'503': { $ref: '#/components/responses/ServiceUnavailable' }
|
||||
|
||||
/books/{bookId}/run-options:
|
||||
parameters:
|
||||
|
|
@ -1097,7 +1132,8 @@ components:
|
|||
schema: { $ref: '#/components/schemas/Problem' }
|
||||
TooLarge:
|
||||
description: |
|
||||
File over the intake cap (`intake_max_bytes` of `GET /capabilities`).
|
||||
Body over the bound the operation declares: the intake cap (`intake_max_bytes` of
|
||||
`GET /capabilities`) on the intake, 1 MiB on bank corrections.
|
||||
headers:
|
||||
X-Request-Id: { $ref: '#/components/headers/RequestId' }
|
||||
content:
|
||||
|
|
@ -1115,7 +1151,9 @@ components:
|
|||
ServiceUnavailable:
|
||||
description: |
|
||||
The deployment cannot do this right now: starting or continuing a run needs its machinery
|
||||
fully configured. Temporary — retry later; no `Retry-After` is promised.
|
||||
fully configured. Temporary — retry later; no `Retry-After` is promised. On bank
|
||||
corrections it can instead carry `bank_corrections_incomplete`, whose remedy is specific:
|
||||
re-send the SAME document. Dispatch on `code`, as everywhere.
|
||||
headers:
|
||||
X-Request-Id: { $ref: '#/components/headers/RequestId' }
|
||||
content:
|
||||
|
|
@ -1220,6 +1258,7 @@ components:
|
|||
- intake_enabled
|
||||
- intake_max_bytes
|
||||
- export_formats
|
||||
- bank_corrections_enabled
|
||||
- page_size_default
|
||||
properties:
|
||||
contract_version:
|
||||
|
|
@ -1228,7 +1267,7 @@ components:
|
|||
The version this deployment serves — the only place a non-streaming client learns it. A
|
||||
client generated against a different one REFUSES to work and says so: while the major is
|
||||
`0` a differing minor carries breaking changes by design.
|
||||
examples: ['0.4.0']
|
||||
examples: ['0.5.0']
|
||||
language_pairs:
|
||||
type: array
|
||||
description: |
|
||||
|
|
@ -1252,6 +1291,13 @@ components:
|
|||
description: |
|
||||
Formats `POST /books/{bookId}/exports` accepts. Empty means none are built here.
|
||||
items: { type: string, minLength: 1 }
|
||||
bank_corrections_enabled:
|
||||
type: boolean
|
||||
description: |
|
||||
Whether this deployment serves `POST /books/{bookId}/bank/corrections`. `false` means
|
||||
the door is declared by this contract and not mounted here: the path answers `404`,
|
||||
and a client does not offer the correction UI. Machine-readable so a client learns it
|
||||
here — not by failing a user's save against a promised door.
|
||||
page_size_default:
|
||||
type: integer
|
||||
minimum: 1
|
||||
|
|
@ -1827,8 +1873,8 @@ components:
|
|||
boolean would merge "proposed, nobody has looked" with "a person started and did not finish"
|
||||
— on a screen of hundreds of rows that is the main filter of work.
|
||||
|
||||
This is the state of the ROW. `POST /books/{bookId}/bank/decisions` does not set it: a
|
||||
decision is recorded against the row and its status follows on the next rebuild.
|
||||
This is the state of the ROW. `POST /books/{bookId}/bank/corrections` does not set it
|
||||
directly: a correction is recorded and the status follows on the next rebuild.
|
||||
enum: [proposed, in_progress, approved]
|
||||
|
||||
TermOrigin:
|
||||
|
|
@ -1852,8 +1898,9 @@ components:
|
|||
id:
|
||||
$ref: '#/components/schemas/Id'
|
||||
description: |
|
||||
Identity of the row, derived from the term itself — its surfaces, sense and window — so
|
||||
a decision against it survives the bank being rebuilt.
|
||||
Identity of the row, derived from the term itself — its SOURCE surface, sense and
|
||||
window; not from `dst`, so a corrected rendering keeps the id — and a decision
|
||||
against it survives the bank being rebuilt.
|
||||
|
||||
⚠ It does NOT survive the book being cut differently: the window is in chapter numbers,
|
||||
those move with a re-cut, and an identity derived from them moves too. A client that sees
|
||||
|
|
@ -1870,8 +1917,9 @@ components:
|
|||
- $ref: '#/components/schemas/TermKind'
|
||||
- type: 'null'
|
||||
description: |
|
||||
`null` when the kind could not be decided — legal, and the row still needs signing. A
|
||||
client MUST show it as "kind not decided" and MUST NOT drop it or invent a kind.
|
||||
`null` when the kind could not be decided — legal; the row stays and stays
|
||||
correctable. A client MUST show it as "kind not decided" and MUST NOT drop it or
|
||||
invent a kind.
|
||||
status: { $ref: '#/components/schemas/TermStatus' }
|
||||
origin: { $ref: '#/components/schemas/TermOrigin' }
|
||||
sense:
|
||||
|
|
@ -1919,29 +1967,70 @@ components:
|
|||
type: integer
|
||||
minimum: 0
|
||||
description: |
|
||||
Rows in status `approved` in the whole bank. Distinct from the counters below: a row
|
||||
can be decided and NOT signed, because declining is also a decision.
|
||||
pending_decisions:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: |
|
||||
How many proposed terms still have no decision.
|
||||
complete:
|
||||
type: boolean
|
||||
description: |
|
||||
The set is complete. INFORMATIONAL, never a gate: signing is one act over the whole
|
||||
bank, and `resume` lifts the stop with the decisions as they stand. A client may
|
||||
show "N of M decided"; it never disables the continue action on this.
|
||||
Rows in status `approved` in the whole bank. Not a count of decisions: a row can be
|
||||
corrected and NOT signed, because declining is also a correction. How many surfaces
|
||||
still await a word is not derivable from these rows at all — see the note on the
|
||||
operation.
|
||||
terms:
|
||||
type: array
|
||||
items: { $ref: '#/components/schemas/BankTerm' }
|
||||
|
||||
BankDecision:
|
||||
BankCorrection:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
description: |
|
||||
A decision on one proposed term. `dst` is mandatory and non-empty for `approve`: a signed
|
||||
term with an empty translation matches nothing yet reads as an intended rendering.
|
||||
required: [term_id, action]
|
||||
One correction of one term: `approve` a rendering, or `decline` a surface. It names its
|
||||
term EITHER by `id` — a row of the bank read as it stands — OR by the full
|
||||
(`src`, `sense`, `since_chapter`, `until_chapter`) tuple that id is derived from; never by
|
||||
both, because both could name two different terms and the caller would never learn which
|
||||
one was taken. Stated in the schema AND in words, because a generator ignores the
|
||||
constraint forms.
|
||||
|
||||
**A tuple the bank does not hold is NOT an error — it is how a term is ADDED.** Approving
|
||||
an unknown tuple writes a new term. Only an unknown `id` refuses — the whole set, `409`
|
||||
`code: bank_corrections_refused`, the entry named in `refusals[]`: an id comes from a
|
||||
read, so an unknown one means the read was of another book or predates a re-cut of this
|
||||
one — the id is derived from the chapter window, and windows move with a re-cut.
|
||||
|
||||
**A member this schema does not declare refuses the request** (`400`,
|
||||
`code: invalid_request`): an unknown member is a caller believing it set something, and the
|
||||
quiet version of that is a correction half-applied.
|
||||
|
||||
**What is deliberately NOT correctable here:**
|
||||
- a term that came WITH the book (`origin: given`): APPROVING its key is refused —
|
||||
changing what the book was given re-buys the translation from the start, a different
|
||||
operation this contract does not offer. DECLINING such a surface is refused only when
|
||||
it would change nothing; when a recorded approval of this book holds that same surface,
|
||||
the decline is accepted and removes it — that is the repair for exactly that clash;
|
||||
- a term's chapter WINDOW in one act: approving a new tuple ADDS a second row and leaves
|
||||
the old one standing, and declining a surface removes EVERY window of that surface.
|
||||
Moving or widening a window is a two-call operation — decline, then approve;
|
||||
- a surface that, once this document is applied, would remain only an ALIAS an approved
|
||||
correction still fires for: declining it alone is refused as changing nothing — decline
|
||||
the owning term instead. (The alias set itself is not published on this surface; the
|
||||
refusal names the owner in its developer-facing `detail`.);
|
||||
- a `note` cannot be emptied, and a `kind` cannot be cleared back to `null` (see the two
|
||||
fields).
|
||||
|
||||
One call decides a term once. A `decline` names a whole SURFACE: the `sense` and window
|
||||
of its tuple say which row the receipt reports and do NOT narrow the effect — every
|
||||
window of the surface goes (for a surface the bank does not list, send `sense: ""` and
|
||||
`null` windows). So a decline and any second correction of the same surface in one
|
||||
document contradict each other and are refused; several `approve`s of one surface in
|
||||
different senses or windows are legitimately several terms.
|
||||
required: [action]
|
||||
oneOf:
|
||||
- title: an existing row, by id
|
||||
required: [id]
|
||||
not:
|
||||
anyOf:
|
||||
- required: [src]
|
||||
- required: [sense]
|
||||
- required: [since_chapter]
|
||||
- required: [until_chapter]
|
||||
- title: a term by its full key — the form that can also add one
|
||||
required: [src, sense, since_chapter, until_chapter]
|
||||
not: { required: [id] }
|
||||
if:
|
||||
properties:
|
||||
action: { const: approve }
|
||||
|
|
@ -1950,62 +2039,241 @@ components:
|
|||
required: [dst]
|
||||
properties:
|
||||
dst: { minLength: 1 }
|
||||
else:
|
||||
not:
|
||||
anyOf:
|
||||
- required: [dst]
|
||||
- required: [kind]
|
||||
properties:
|
||||
term_id: { $ref: '#/components/schemas/Id' }
|
||||
action:
|
||||
type: string
|
||||
enum: [approve, decline]
|
||||
description: |
|
||||
`approve` — take the term into the book (with a translation in `dst`); `decline` — leave
|
||||
it out.
|
||||
|
||||
⚠ **NOTHING recorded here reaches the work itself yet** — not `decline`, not `approve`,
|
||||
not the translation typed into `dst`. A deployment stores the decisions and answers the
|
||||
receipt; the producer of the bank reads its own files and this call does not write them.
|
||||
So a term left out here can come back proposed on the next run, and a corrected
|
||||
translation does not change the text. Both halves are read from `BankPage` and neither
|
||||
is guessed; the companion carries who owes the missing half.
|
||||
|
||||
This is a TEMPORARY property of the deployment and not a promise of the contract: the
|
||||
call exists so the correction has somewhere to go the day the delivery is built, and a
|
||||
client may show what it recorded. What DOES take effect today is the signature itself —
|
||||
one `resume` over the whole bank, whatever state the decisions are in.
|
||||
`approve` — take the term into the book with the rendering in `dst`; it also lifts a
|
||||
previous decline of the surface. `decline` — leave the surface out and stop it being
|
||||
proposed again; it also removes every previously approved row of that surface. There is
|
||||
no "un-decide": a correction is REPLACED by the opposite one, never withdrawn, and the
|
||||
receipt says when one displaced an earlier word.
|
||||
id:
|
||||
$ref: '#/components/schemas/Id'
|
||||
description: |
|
||||
The bank row's `id`, exactly as the bank read published it. Mutually exclusive with
|
||||
the tuple.
|
||||
src:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Source surface of the term, as `BankTerm.src`.
|
||||
sense:
|
||||
type: string
|
||||
description: |
|
||||
Polysemy disambiguator, as `BankTerm.sense`: required in the tuple form, and the EMPTY
|
||||
STRING means "no disambiguator". Required precisely so a caller cannot omit it and
|
||||
silently name a DIFFERENT term than it meant: the tuple is the term's whole key, and a
|
||||
partial key is another key.
|
||||
since_chapter:
|
||||
type: [integer, 'null']
|
||||
minimum: 1
|
||||
description: |
|
||||
First chapter of the window, `null` for "from the beginning" — the same coordinates
|
||||
and the same `null` as `BankTerm.since_chapter`; required in the tuple form for the
|
||||
same reason as `sense`. A window that ends before it begins is refused: such a term
|
||||
would be recorded and then apply nowhere.
|
||||
until_chapter:
|
||||
type: [integer, 'null']
|
||||
minimum: 1
|
||||
description: Last chapter of the window, `null` for "to the end". As `since_chapter`.
|
||||
dst:
|
||||
type: string
|
||||
description: |
|
||||
Translation. **Required and non-empty when `action` is `approve`** — in words as well as
|
||||
in the constraint above, because a generator ignores `if`/`then`. ⚠ See the caveat on
|
||||
`action`: today this text is recorded and not applied.
|
||||
|
||||
BankDecisionsRequest:
|
||||
type: object
|
||||
required: [decisions]
|
||||
properties:
|
||||
decisions:
|
||||
type: array
|
||||
minItems: 1
|
||||
maxItems: 1000
|
||||
The rendering. **Required and non-empty when `action` is `approve`; forbidden on
|
||||
`decline`** — in words as well as in the constraints above, because a generator
|
||||
ignores them. An approved term with no rendering is not a weak approval — it would
|
||||
fail the next run; and a rendering on a decline says the caller meant to approve, and
|
||||
half of that is not something to guess at.
|
||||
kind:
|
||||
$ref: '#/components/schemas/TermKind'
|
||||
description: |
|
||||
Decisions to record, bounded like every other collection here.
|
||||
items: { $ref: '#/components/schemas/BankDecision' }
|
||||
Reclassify the term. ABSENT means "not decided" and keeps whatever the term already
|
||||
carries — including a `kind` of `null`: there is no way to clear a kind back to `null`
|
||||
through this surface. Forbidden on `decline`, like `dst`.
|
||||
note:
|
||||
type: string
|
||||
description: |
|
||||
The user's reason, recorded with the correction and NOT published: it never appears in
|
||||
`BankTerm` or anywhere else on this surface. ABSENT means "not decided" and keeps the
|
||||
note recorded earlier — so a note cannot be EMPTIED here, only replaced with other
|
||||
words. Named rather than hidden, because dropping the earlier reasoning by omission is
|
||||
the mistake a caller makes by accident. Legal on both actions.
|
||||
|
||||
BankDecisionsResult:
|
||||
BankCorrectionsRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
description: |
|
||||
Receipt of a submission: the same facts the bank read answers, so a client updates its
|
||||
screen without a second call.
|
||||
required: [revision, pending_decisions, complete]
|
||||
The correction document, applied as ONE act, all or nothing. An undeclared member refuses
|
||||
the request — see `BankCorrection`.
|
||||
required: [book_id, preview, corrections]
|
||||
properties:
|
||||
revision: { $ref: '#/components/schemas/Revision' }
|
||||
pending_decisions:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: How many proposed terms still have no decision.
|
||||
complete:
|
||||
book_id:
|
||||
$ref: '#/components/schemas/Id'
|
||||
description: |
|
||||
The book these corrections were computed FOR, checked against the book in the path; a
|
||||
mismatch is `400` `invalid_request` with an `errors[]` entry at `/book_id`.
|
||||
Deliberately a second carrier of the same fact: corrections carry a user's own words
|
||||
into a book's canon, and a set computed for one book landing in another is not a
|
||||
mistake anything downstream could notice.
|
||||
preview:
|
||||
type: boolean
|
||||
description: |
|
||||
The set is complete. Informational, never a gate — `resume` lifts the stop with the
|
||||
decisions as they stand.
|
||||
`true` — answer the receipt and change NOTHING; `false` — apply. Required, not
|
||||
defaulted: which of the two acts this call is must be said, not implied.
|
||||
corrections:
|
||||
type: array
|
||||
minItems: 1
|
||||
maxItems: 5000
|
||||
description: |
|
||||
At most 5000 in one act — a hard ceiling, not advice: a larger set is refused whole.
|
||||
Split a larger document and send the parts in turn.
|
||||
items: { $ref: '#/components/schemas/BankCorrection' }
|
||||
|
||||
BankCorrectionsReceipt:
|
||||
type: object
|
||||
description: |
|
||||
The answer of a correction call — the whole of it, for a preview and for an apply alike;
|
||||
`preview` says which of the two this receipt is. It is NOT a read of the bank: the rows
|
||||
`GET /books/{bookId}/bank` serves change only when a run next rebuilds the bank.
|
||||
required: [preview, changed, depth, accepted, preexisting_faults, signature]
|
||||
properties:
|
||||
preview:
|
||||
type: boolean
|
||||
description: Echo of the request. A preview's receipt promises; an apply's reports.
|
||||
changed:
|
||||
type: boolean
|
||||
description: |
|
||||
Whether this call changed the recorded corrections (for a preview: whether applying
|
||||
would). `false` on an apply means the document was ALREADY fully applied — the normal
|
||||
answer to a retry, and nothing was written again.
|
||||
depth:
|
||||
type: string
|
||||
description: |
|
||||
How far an accepted correction reaches. `refinement` — it is applied when a run next
|
||||
refines the text: the translation already produced is corrected in later passes rather
|
||||
than re-translated from scratch, and nothing changes until a run happens. A client
|
||||
renders its own words from this value, MUST NOT promise a fresh re-translation, and
|
||||
treats an unknown value neutrally — this vocabulary can grow.
|
||||
examples: ['refinement']
|
||||
accepted:
|
||||
type: array
|
||||
description: One entry per correction of the document, in the document's order.
|
||||
items: { $ref: '#/components/schemas/AcceptedCorrection' }
|
||||
preexisting_faults:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: |
|
||||
Faults the book's bank inputs ALREADY carried — not caused by this call, and not
|
||||
refusing it: this door must stay usable exactly when the book needs repair. Non-zero
|
||||
warns that the next run would FAIL at the bank — a fault, not the signing stop —
|
||||
regardless of this call. The detail is a server-side matter, correlated by
|
||||
`X-Request-Id`; it is not on this surface.
|
||||
signature:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/BankSignatureCount'
|
||||
- type: 'null'
|
||||
description: |
|
||||
The count against the surfaces the LAST signing stop offered — `null` when no run has
|
||||
reached a signing stop yet, so there is nothing to count against. Carries its own
|
||||
warning: see the schema. ⚠ A named narrowness of this version: the count lives ONLY
|
||||
on this receipt, so a client with no correction to send — or preview — has no way to
|
||||
ask for it yet; the companion carries when a read would take it over.
|
||||
|
||||
AcceptedCorrection:
|
||||
type: object
|
||||
description: What one correction did — for a preview, would do.
|
||||
required: [index, action, id, src, dst, state, displaced]
|
||||
properties:
|
||||
index:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: Position of the correction in the request document.
|
||||
action:
|
||||
type: string
|
||||
enum: [approve, decline]
|
||||
id:
|
||||
$ref: '#/components/schemas/Id'
|
||||
description: |
|
||||
Id of the term the correction named — for a term this call ADDS, the id its tuple
|
||||
derives to: the same id the bank read will publish for it.
|
||||
src:
|
||||
type: string
|
||||
description: Source surface of the term.
|
||||
dst:
|
||||
type: [string, 'null']
|
||||
description: The rendering; `null` on `decline`.
|
||||
state:
|
||||
type: string
|
||||
enum: [applied, already_applied]
|
||||
description: |
|
||||
`already_applied` — the recorded corrections already say this; nothing was (or would
|
||||
be) written for it. That is the whole of idempotency as a caller sees it: re-sending a
|
||||
document is safe.
|
||||
displaced:
|
||||
type: boolean
|
||||
description: |
|
||||
Whether this correction displaced an earlier word — a previous rendering, a previous
|
||||
decline of the surface, previously approved rows removed by a decline. `true` is not
|
||||
an error: a correction REPLACES. It is surfaced because overwriting without seeing
|
||||
that you overwrote is the mistake this door must not enable. The itemization of WHAT
|
||||
was displaced is not on this surface: its vocabulary is the service's own free text,
|
||||
which does not cross this boundary.
|
||||
|
||||
BankSignatureCount:
|
||||
type: object
|
||||
description: |
|
||||
Of the surfaces the LAST signing stop offered, how many the user has not yet spoken about —
|
||||
counted by the same rule a run uses to fold corrections into the bank, so the count agrees
|
||||
with what the next run treats as decided.
|
||||
|
||||
⚠ **INFORMATIONAL, never a gate — it counts, it does not decide.** It does NOT answer
|
||||
whether the stop will lift, in either direction: `undecided: 0` promises NOTHING about the
|
||||
next run's behaviour, and an undecided remainder forbids nothing — deciding surfaces is
|
||||
the user's RIGHT, not the stop's demand. A client may show "N of M still open"; it MUST
|
||||
NOT disable or gate the continue action on these numbers or imply they must reach zero:
|
||||
`resumeRun` lifts the stop with the corrections as they stand.
|
||||
required: [surfaces, undecided, unreadable]
|
||||
properties:
|
||||
surfaces:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: How many surfaces the last stop asked about.
|
||||
undecided:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: How many of them are still undecided AFTER this call.
|
||||
unreadable:
|
||||
type: boolean
|
||||
description: |
|
||||
`true` — the two numbers mean NOTHING for this call: the state they are counted from
|
||||
could not be read. Without this flag, "could not count" would be byte-identical to
|
||||
"nothing left undecided" — the one thing this schema must never say by accident.
|
||||
|
||||
CorrectionRefusal:
|
||||
type: object
|
||||
description: One refused correction, or one refusal about the would-be result as a whole.
|
||||
required: [pointer, detail]
|
||||
properties:
|
||||
pointer:
|
||||
type: string
|
||||
description: |
|
||||
JSON Pointer to the refused correction (`/corrections/3`), or the EMPTY STRING for a
|
||||
refusal about the result as a whole — a contradiction the SET introduces rather than
|
||||
any one entry.
|
||||
detail:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: |
|
||||
Developer-facing sentence naming the reason, like `Problem.detail`: a client MUST NOT
|
||||
show it to a user — it marks the row and draws its own neutral phrase. The reasons
|
||||
carry no machine vocabulary on this surface yet: that vocabulary is the service's own
|
||||
and still growing, and freezing a copy here would be a second source of truth.
|
||||
|
||||
RunRequest:
|
||||
type: object
|
||||
|
|
@ -2304,8 +2572,8 @@ components:
|
|||
properties:
|
||||
contract:
|
||||
type: string
|
||||
description: Contract version this deployment serves, e.g. `0.4.0`.
|
||||
examples: ['0.4.0']
|
||||
description: Contract version this deployment serves, e.g. `0.5.0`.
|
||||
examples: ['0.5.0']
|
||||
|
||||
EventStatus:
|
||||
allOf:
|
||||
|
|
@ -2361,15 +2629,14 @@ components:
|
|||
- $ref: '#/components/schemas/EventBase'
|
||||
- type: object
|
||||
description: |
|
||||
The bank changed, or a signing stop happened. The counters are the delta a screen header
|
||||
needs; the ROWS are read with `after_version` set to the revision the client last
|
||||
applied.
|
||||
required: [total, signed, pending_decisions, complete]
|
||||
The bank changed, or a signing stop happened. The counters are the delta a screen
|
||||
header needs; the ROWS are read with `after_version` set to the revision the client
|
||||
last applied. A correction (`POST …/bank/corrections`) does not produce this frame:
|
||||
it changes no row until the next run.
|
||||
required: [total, signed]
|
||||
properties:
|
||||
total: { type: integer, minimum: 0 }
|
||||
signed: { type: integer, minimum: 0 }
|
||||
pending_decisions: { type: integer, minimum: 0 }
|
||||
complete: { type: boolean }
|
||||
|
||||
EventResyncRequired:
|
||||
description: |
|
||||
|
|
@ -2398,7 +2665,9 @@ components:
|
|||
Error, per RFC 9457 with the extension members below.
|
||||
|
||||
**The machine identifier is `code`.** `type` is `about:blank` on every response and carries
|
||||
no information: this deployment serves no problem-type documents.
|
||||
no information: this deployment serves no problem-type documents. On THIS surface `code`
|
||||
is always present; outside the version prefix it may be absent (header, `/auth/*`) — a
|
||||
parser applied there must tolerate that rather than fail.
|
||||
|
||||
⚠ **`title` and `detail` are written for a DEVELOPER and a log, and a client MUST NOT show
|
||||
either to a user.** They are English and will not be translated. The sentence the user reads
|
||||
|
|
@ -2413,6 +2682,7 @@ components:
|
|||
| `errors` | `invalid_request` |
|
||||
| `cause` | any code with a narrower cause to give |
|
||||
| `blocked` | `ceiling_unavailable` |
|
||||
| `refusals` | `bank_corrections_refused` |
|
||||
| `localized` | codes whose cause cannot be enumerated |
|
||||
required: [type, title, status, code, request_id]
|
||||
properties:
|
||||
|
|
@ -2452,6 +2722,13 @@ components:
|
|||
description: |
|
||||
Carried by `ceiling_unavailable` when another book of the account holds the credit — the
|
||||
same shape `RunOptions` answers.
|
||||
refusals:
|
||||
type: array
|
||||
minItems: 1
|
||||
description: |
|
||||
Which corrections were refused and why. Carried by `bank_corrections_refused`; the
|
||||
whole set was refused and NOTHING was applied.
|
||||
items: { $ref: '#/components/schemas/CorrectionRefusal' }
|
||||
localized:
|
||||
$ref: '#/components/schemas/LocalizedMessage'
|
||||
description: |
|
||||
|
|
@ -2484,7 +2761,8 @@ components:
|
|||
rather than check the address. The operation says where it is also the answer to an
|
||||
identifier that never existed;
|
||||
- `request_timeout` (408) — the body did not arrive whole in time. Retrying is the remedy;
|
||||
- `payload_too_large` (413) — over `intake_max_bytes`;
|
||||
- `payload_too_large` (413) — the body is over the bound the operation declares:
|
||||
`intake_max_bytes` on the intake, 1 MiB on bank corrections;
|
||||
- `run_in_flight` (409) — this book is already being translated;
|
||||
- `book_not_ready` (409) — the book cannot be translated yet: it is still arriving, still
|
||||
being cut, or was rejected;
|
||||
|
|
@ -2503,6 +2781,18 @@ components:
|
|||
- `idempotency_conflict` (409) — an `Idempotency-Key` was re-used. `cause.code`:
|
||||
`key_reused` for a different request under the same key, `key_in_flight` for one that is
|
||||
still running, and then `Retry-After` says how long to wait;
|
||||
- `bank_corrections_refused` (409) — the correction document was read, understood and
|
||||
declined WHOLE; nothing was applied. A legal refusal, not a fault: what the document
|
||||
asks contradicts the book as it stands — a term that came with the book, a
|
||||
contradiction within the set, an `id` no current row carries, a `decline` nothing
|
||||
recorded answers to, or a set larger than the service applies in one act. Re-sending an
|
||||
already-recorded correction is NOT this — that answers `200` with `already_applied`.
|
||||
The USER re-decides and sends a new document; `refusals` names every refused entry.
|
||||
Distinct from `invalid_request`, which is about the request's FORM;
|
||||
- `bank_corrections_incomplete` (503) — the correction document was ACCEPTED and the write
|
||||
did not land whole. The remedy is to re-send the SAME document: the retry converges —
|
||||
what landed is recognised, not duplicated. Distinct from `service_unavailable` because
|
||||
the remedy is this specific;
|
||||
- `content_refused` (400) — the service will not do this work. **One coarse code for a whole
|
||||
class** and deliberately so: it does not say which check refused, does not vary between
|
||||
attempts, and carries neither `cause` nor `errors`. A client shows one neutral phrase and
|
||||
|
|
@ -2535,6 +2825,8 @@ components:
|
|||
- run_not_resumable
|
||||
- ceiling_unavailable
|
||||
- idempotency_conflict
|
||||
- bank_corrections_refused
|
||||
- bank_corrections_incomplete
|
||||
- content_refused
|
||||
- service_unavailable
|
||||
- internal_error
|
||||
|
|
|
|||
|
|
@ -1,5 +1,22 @@
|
|||
# Промт: контрактная сессия, минор 0.4.0 → 0.5.0 — снос отменённой модели и дверь, выведенная из глагола
|
||||
|
||||
> ⚠⚠ **ОТРАБОТАН И ЗАКРЫТ. Инструкции отсюда НЕ исполнять** — файл сохранён как заказ, по которому
|
||||
> судить исполнение.
|
||||
>
|
||||
> **Исход:** пак исполнен и ПРИНЯТ 27.08, ратификация **D39.161**. Канон 0.5.0 и компаньон заленджены,
|
||||
> отчёт — `docs/CONTRACT_MINOR_REPORT.md`.
|
||||
>
|
||||
> ⚠ **ДВА МЕСТА, ГДЕ ЭТОТ ПРОМТ БЫЛ НЕВЕРЕН, и это нашёл исполнитель, а не автор.** §4 п.2 обещал, что
|
||||
> после сноса «канон и деплой СОВПАДУТ точно», а §3.2-бис — что счётчики `pending_decisions`/`complete`
|
||||
> «навсегда нули». Верно по ПУТЯМ, неверно по ПОЛЯМ: проекция `GET /bank` продолжает их слать, и это
|
||||
> живой счёт `proposed`-строк (`platform/internal/pgstore/readmodel.go:330`). Носитель — `PD-399`,
|
||||
> эррата — 27.08-ж в шапке журнала решений. Сессия принесла расхождение ПИНГОМ, а не подогнала работу
|
||||
> под неверную посылку.
|
||||
>
|
||||
> ⚠ Список носителей §3.1 был НЕПОЛОН: третий носитель счётчиков (`EventBank`) сессия нашла своим
|
||||
> грепом. Промт предупреждал, что его шаблон мог что-то не поймать, — предупреждение оправдалось.
|
||||
|
||||
|
||||
> **Выдан оркестратором №19, 27.08.2026.** Порядок работ по шву ратифицирован **D39.156**; этот пак —
|
||||
> пункт (2б) очереди, и он идёт ПОСЛЕ движкового пака, который закончен и заленджен 27.08 (**D39.158**,
|
||||
> коммит `d1eb8a9`). Это существенно: тело новой двери больше не сочиняется — оно ВЫВОДИТСЯ из словаря
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -10,7 +10,7 @@ import "net/http"
|
|||
// client generated against another one refuses to work and says so — which is why this must be
|
||||
// raised in the same commit as the code that implements a new minor, and never as a courtesy
|
||||
// afterwards.
|
||||
const ContractVersion = "0.4.0"
|
||||
const ContractVersion = "0.5.0"
|
||||
|
||||
// Capabilities is what this deployment can do: one flat document, the same for every account.
|
||||
type Capabilities struct {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue