Land batch B5: the contract companion taught a compression gap the platform closed in Go, and its provenance column had rotted by 240 lines

This commit is contained in:
heaven 2026-09-02 00:38:10 +03:00
parent 9aa7106603
commit ac729bf544
2 changed files with 24 additions and 24 deletions

View file

@ -57,7 +57,7 @@ cmp-сверка обязательна (D39.138 п.3).
> целостному ревью research/28** · **0.4.0 (D39.152, 20.08) — синк с платформой, §6в** ·
> **0.5.0 (D39.161, 27.08) — снос отменённой пер-термной модели подписи (PD-370) + дверь правок
> банка, выведенная из построенного глагола движка (D39.158), + предупреждение о конверте вне
> `/v0`; вывод и провенанс — §2.19, отчёт сессии — `docs/CONTRACT_MINOR_REPORT.md`** ·
> `/v0`; вывод и провенанс — §2.19, отчёт сессии — `../../archive/reports/CONTRACT_MINOR_REPORT_2026-08-27.md`** ·
> **0.6.0 (D39.162, 28.08) — сквозная полоса прогресса и подпись стадии, приехавшие ОДНИМ лендингом
> с платформенным паком P9; вывод и провенанс — §2.20** ·
> **0.7.0 (D39.166, 28.08) — покупка ПЕРЕ-ПРОХОДА: правка банка доезжает в уже переведённый текст;
@ -735,7 +735,7 @@ the original request was never applied». Каждый `POST /books` созда
| занятый арбитр (живой прогон держит флок) — «подожди», не «сломано» | класс 12 (`main.go:71`), `bankdecisions.go:159-163` |
**Решения этой сессии (◆), каждое с доводом; подробный разбор и отвергнутые альтернативы — отчёт
`docs/CONTRACT_MINOR_REPORT.md`:**
`../../archive/reports/CONTRACT_MINOR_REPORT_2026-08-27.md`:**
- **Один `POST /books/{bookId}/bank/corrections` с документом целиком.** Глагол принимает документ и
отвечает отчётом атомарно; ресурсная модель (PATCH строк) врала бы про атомарность и про то, что
@ -880,7 +880,7 @@ the original request was never applied». Каждый `POST /books` созда
(`runStage` — SQL над теми же колонками, а не проброс движковой строки), и словарь ОТКРЫТ — клиент
обязан рисовать незнакомое значение нейтрально. Следствие, ради которого условия и поставлены:
**пара или конвейер с ИНОЙ формой работы не требуют нового клиента и не поднимают версию.**
Сегодняшние значения — `drafting` и `editing`.
Сегодняшние значения — `drafting`, `editing` и `re_pass` (⚠ испр. 02.09: третье значение выдаёт `runStage` для всего пере-прохода — `platform/internal/pgstore/readmodel.go`, греп `re_pass` — и оно доезжает на провод).
**Ложная перекрёстная ссылка, снятая этим же минором.** `ChapterProgress.units_done` обещал «то же
счетоводство, что у `Progress`, уровнем ниже». После сквозной полосы это неправда: глава по-прежнему
@ -978,12 +978,12 @@ into the generated client's source**» (§Boundaries). Поймано линзо
| `GET /books`, `GET /books/{id}` | read-модель платформы | ПОСТРОЕНО | — |
| `GET /books/{id}/run-options`, `POST /runs` | шкала + холды | ПОСТРОЕНО | — |
| `POST /runs/{id}/stop` | реконсилятор | ПОСТРОЕНО (кнопки на экране нет) | зона фронта |
| `POST /runs/{id}/resume` | реконсилятор | ПОСТРОЕНО, но **0.3.0 сменил поведение на паузе по потолку, а 0.4.0 — на ИСЧЕРПАННОМ прогоне**. Пауза: платформа уже отвечает `409` `ceiling_reached` (`runs/reconcile.go`, ветка `case "paused"` в `Resume`) — совпало. Исчерпанность: платформа отвечает `202` и возвращает прогон в то же состояние (`reconcile.go`, оба выхода `exhausted` из `reopen` → ветка `exhausted` в `Resume``httpapi/v0.go`, хендлер `resumeRun`), а контракт теперь требует `409` `run_not_resumable` · `cause.code: ceiling_reached`. Молчаливый `202` на действие, которое ничего не сделало, — ровно то, от чего предупреждает собственный комментарий платформы; клиенту нечем отличить успех от no-op, а на `awaiting_bank` это ещё и не вызывает `ReleaseBankStop`. Полная таблица по статусам — в описании `resumeRun` | **вход P7** (правка построенного пути, не только читающей поверхности) |
| `POST /runs/{id}/resume` | реконсилятор | ПОСТРОЕНО, но **0.3.0 сменил поведение на паузе по потолку, а 0.4.0 — на ИСЧЕРПАННОМ прогоне**. Пауза: платформа уже отвечает `409` `ceiling_reached` (`runs/reconcile.go`, ветка `case "paused"` в `Resume`) — совпало. Исчерпанность: **РАСХОЖДЕНИЕ ЗАКРЫТО (испр. 02.09)**`Resume` больше не отвечает `202` на исчерпанный прогон. Реконсилятор различает ДВА вердикта, и оба доезжают до клиента кодом 409 `run_not_resumable`: `ceilingSpent``cause.code: ceiling_reached` и `creditUnavailable``cause.code: credit_unavailable` (`platform/internal/runs/reconcile.go`, греп `ceilingSpent`; `platform/internal/httpapi/v0.go`, греп `run_not_resumable`). Молчаливый `202` на действие, которое ничего не сделало, — ровно то, от чего предупреждает собственный комментарий платформы; клиенту нечем отличить успех от no-op, а на `awaiting_bank` это ещё и не вызывает `ReleaseBankStop`. Полная таблица по статусам — в описании `resumeRun` | **вход P7** (правка построенного пути, не только читающей поверхности) |
| `GET /usage` | кредиты | ПОСТРОЕНО, **не читается ни одним экраном** | зона фронта |
> ⚠ **ТАБЛИЦА ПЕРЕ-СНЯТА ПРОТИВ КОДА 22.08 (оркестратор №18, аудит доков): шесть строк объявляли НЕ ПОСТРОЕННЫМ то, что пак P7 построил, а P8 сохранил.** Правило §Б-21 требует вести здесь статус построенного — оно нарушалось пятый раз подряд, потому что носитель «вход P7» никто не гасил при лендинге. Носитель израсходован: пак P7 принят D39.153, пак P8-FIX — D39.154.
| `GET /capabilities` | конфигурация деплоя | **ПОСТРОЕНО P7** — маршрут смонтирован безусловно (`platform/internal/httpapi/v0.go`, таблица `contractSurface`), хендлер `capabilities.go`; версия контракта запинена ГЕЙТОМ против ЭТОГО канона (`platform/internal/gates/contract_test.go`) | закрыто D39.153 |
| `PATCH`/`DELETE /books/{id}`, `GET /runs/{id}` | колонки есть | НЕ ПОСТРОЕНО (заведено 0.3.0) | вход P7 |
| `PATCH`/`DELETE /books/{id}`, `GET /runs/{id}` | колонки есть | НЕ ПОСТРОЕНО (заведено 0.3.0) — факт верен: роутер монтирует 15 операций из 20 | ⚠ носитель «вход P7» ИЗРАСХОДОВАН (пак P7 принят D39.153); живой носитель — `platform/BACKLOG.md` П-17 и очередь CURRENT-STATE |
| `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) | ⚠ пере-снято 0.5.0: проекция платформы ПОСТРОЕНА (P7 — маршрут в `contractSurface`, `wireBankPage` в `httpapi/reading.go`, `SaveBank` в `pgstore`; прежняя запись «не хватает проекции» устарела при израсходованном носителе «вход P7», D39.153); форма синхронна канону с лендингом P9: счётчики `pending_decisions`/`complete` сняты с проекции (`PD-399`) | закрыто D39.162 |
@ -995,11 +995,11 @@ into the generated client's source**» (§Boundaries). Поймано линзо
| `Idempotency-Key` | — | **ПОСТРОЕНО P7**`httpapi/idempotency.go` + `pgstore/idempotency.go`, на двух создающих вызовах; ⚠ живой дефект под конкуренцией — `PD-369` | закрыто D39.153 |
| `bearerToken` — чем ВЫДАЁТСЯ токен | вход только ставит HttpOnly-куку (`login.go:338`) | сервер токен ПРИНИМАЕТ, выдать его нечем | носитель: research/28 §2 (Б-15); строки нет |
| `Problem.localized` | — | объявлено, ни одним кодом не используется | носитель: research/28 §8 п.4 |
| `Note.code` как enum спеки | карта приложения А | не enum, пока не написаны фразы | строка 148 (фразы владельца) |
| `Note.code` как enum спеки | карта приложения А | не enum, пока не написаны фразы | **прежний носитель «строка 148» МЁРТВ** — строку удалил `a023e39` решением владельца 21.08 (тот же факт этот док уже несёт ниже, в §6в); живой носитель — строка бэклога **204** (словарь фраз) |
| Настоящие названия глав (`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` в подписи банка доезжает до работы | ⚠ пере-снято 0.5.0: старый носитель («таблица `bank_decisions`, движок её не читает») умер вместе с моделью — таблица снесена 22.08, предупреждение на `BankDecision.action` снесено вместе со схемой | дверь смонтирована (D39.162), и decline доезжает до следующего прогона ПО ПОСТРОЕНИЮ. ⚠ **Остаток ДВИЖКОВЫЙ, две штуки, обе найдены воркфлоу-ревью P9:** decline не энтити-широк на обратном пути (отклонённая сущность возвращается через свой АЛИАС, `mining.go:548`), и повтор ТОГО ЖЕ документа на одном пути не сходится (`membank/decisions.go:374`) — второе ломает обещание сходимости, на котором стоит синхронная дверь | заказано бэкенд-паку «тихая порча», `docs/BACKEND_SILENT_HARM_SESSION_PROMPT.md` §3.3 | ⚠ пере-снято 30.08: из двух движковых остатков ВТОРОЙ ЗАКРЫТ — порядок записи документов решений сменён на rejects-first, повтор принятого документа СХОДИТСЯ (D39.164 §3.3). Живым остаётся ОДИН: `decline` не энтити-широк на обратном пути — отклонённая сущность возвращается через свой АЛИАС, тогда как канон обещает «declining a surface removes EVERY window of that surface». Носитель — строка 228 единого бэклога.
| `decline` в подписи банка доезжает до работы | ⚠ пере-снято 0.5.0: старый носитель («таблица `bank_decisions`, движок её не читает») умер вместе с моделью — таблица снесена 22.08, предупреждение на `BankDecision.action` снесено вместе со схемой | дверь смонтирована (D39.162), и decline доезжает до следующего прогона ПО ПОСТРОЕНИЮ. ⚠ **Остаток ДВИЖКОВЫЙ, две штуки, обе найдены воркфлоу-ревью P9:** decline не энтити-широк на обратном пути (отклонённая сущность возвращается через свой АЛИАС, `mining.go:548`), и повтор ТОГО ЖЕ документа на одном пути не сходится (`membank/decisions.go:374`) — второе ломает обещание сходимости, на котором стоит синхронная дверь | заказано бэкенд-паку «тихая порча», `../../archive/prompts/BACKEND_SILENT_HARM_SESSION_PROMPT_2026-08-28.md` §3.3 | ⚠ пере-снято 30.08: из двух движковых остатков ВТОРОЙ ЗАКРЫТ — порядок записи документов решений сменён на rejects-first, повтор принятого документа СХОДИТСЯ (D39.164 §3.3). Живым остаётся ОДИН: `decline` не энтити-широк на обратном пути — отклонённая сущность возвращается через свой АЛИАС, тогда как канон обещает «declining a surface removes EVERY window of that surface». Носитель — строка 228 единого бэклога.
| Снятие замечания (переход «флаг снят») | движок | **НЕДОСТИЖИМО сегодня, проверено чтением движка** — п. H §6в | **PD-298** регистра платформы (`platform/docs/DEFECT_REGISTER.md`) — там строка и живёт; механизма не строим |
| Счёт «сделанного» на деплое без второго прохода | платформа | канон 0.4.0 определил «сделано» = последний проход ЭТОГО деплоя; проекция платформы считает жёстко второй проход | **вход P7** (PD-202) |
@ -1634,8 +1634,8 @@ heartbeat · форма `id` кадра. Сжатие как ТРЕБОВАНИ
Ниже — то, что клиент не наблюдает и что деплой обязан себе устроить сам:
- **Чем и на каком слое сжимать** (middleware, обратный прокси, CDN). Go stdlib не сжимает,
edge-конфига в репозитории нет, `grep -rn "gzip|Content-Encoding" platform --include=*.go` → ноль.
- **Чем и на каком слое сжимать** (middleware, обратный прокси, CDN). ⚠ **ПРОТУХЛО (испр. 02.09):**
платформа сжимает САМА, в Go — `platform/internal/httpapi/conditional.go`, греп `gzipFloor` (порог 1<<10 байт) и `acceptsGzip`; edge-слой для этого не нужен.
- **HTTP/2 на edge.** Клиент держит одно SSE-соединение на книгу; на HTTP/1.1 шесть соединений на
origin — потолок вкладок. Сервис на HTTP/1.1 обязан работать ХУЖЕ, а не не работать.
- **`X-Accel-Buffering: no`** (или эквивалент прокси) — механизм для нормы «поток не буферизуется».
@ -1707,28 +1707,28 @@ heartbeat · форма `id` кадра. Сжатие как ТРЕБОВАНИ
### Приложение А-2. Карта кодов ОШИБОК — заполнена (0.3.0)
В отличие от карты выше, эта заполнена целиком: словарь выведен из реальных ветвей платформы, а фраз
она не содержит по замыслу — их рисует клиент.
она не содержит по замыслу — их рисует клиент.**Колонка «Откуда взят» переведена 02.09 с НОМЕРОВ СТРОК на СИМВОЛЫ:** вся она съехала на 240270 строк (производители уехали, на прежних номерах стояли комментарии и скобки), и это её второе протухание. Адресация символом (`pgstore.ErrNoBook`, `readField`, имя файла) переживает любой сдвиг; номеров сюда больше не ставить.
| Корневой `code` | HTTP | Откуда взят (платформа) |
|---|---|---|
| `invalid_request` | 400 | `v0.go:238,250,333,339,531` · `pgstore.ErrBadCursor` · `books.ErrBadIntake` (`books.go:118-121`, `readField` `v0.go:394-396`) · `v0.go:242` (неполное тело) · `v0.go:345,356,419,423` (интейк) |
| `unauthenticated` | 401 | гард сессии (`server.go:109-110`) |
| `forbidden` | 403 | `server.go:99` + `auth/csrf.go` (отсутствие `X-TM-Client` / чужой origin) |
| `not_found` | 404 | `pgstore.ErrNoBook`/`ErrNoAccount`/`ErrNoRun` · охраняемый catch-all `server.go:134` |
| `gone` | 410 | `pgstore.ErrNoChapter``v0.go:761` — глава, которой нет в ТЕКУЩЕЙ структуре книги (была она когда-то или нет — не различается по замыслу, `openapi.yaml` §`gone`; лечение клиента одно: перечитать дерево). ⚠ Строка дописана оркестратором №18 при ратификации 0.4.0: карта объявляла себя «заполненной», а несла 15 значений из 16 корневых — производитель у `gone` живой с P7 |
| `request_timeout` | 408 | `os.ErrDeadlineExceeded``v0.go:417` |
| `payload_too_large` | 413 | `*http.MaxBytesError``v0.go:409` |
| `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`) |
| `invalid_request` | 400 | `v0.go` · `pgstore.ErrBadCursor` · `books.ErrBadIntake` (`books.go`, `readField` `v0.go`) · `v0.go` (неполное тело) · `v0.go` (интейк) |
| `unauthenticated` | 401 | гард сессии (`server.go`) |
| `forbidden` | 403 | `server.go` + `auth/csrf.go` (отсутствие `X-TM-Client` / чужой origin) |
| `not_found` | 404 | `pgstore.ErrNoBook`/`ErrNoAccount`/`ErrNoRun` · охраняемый catch-all `server.go` |
| `gone` | 410 | `pgstore.ErrNoChapter``v0.go` — глава, которой нет в ТЕКУЩЕЙ структуре книги (была она когда-то или нет — не различается по замыслу, `openapi.yaml` §`gone`; лечение клиента одно: перечитать дерево). ⚠ Строка дописана оркестратором №18 при ратификации 0.4.0: карта объявляла себя «заполненной», а несла 15 значений из 16 корневых — производитель у `gone` живой с P7 |
| `request_timeout` | 408 | `os.ErrDeadlineExceeded``v0.go` |
| `payload_too_large` | 413 | `*http.MaxBytesError``v0.go` |
| `run_in_flight` | 409 | `pgstore.ErrRunInFlight` (`v0.go`) |
| `book_not_ready` | 409 | `runs.ErrBookNotReady` (`v0.go`) |
| `run_not_stoppable` | 409 | `runs.ErrNotStoppable` (`v0.go`) |
| `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` |
| `ceiling_unavailable` | 409 | `runs.ErrCeilingOutOfBounds` + `pgstore.ErrInsufficientCredit` (`v0.go`); `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` |
| `service_unavailable` | 503 | `runner.ErrCeilingNotWired` + `runs.ErrRunnerIncomplete` (`v0.go`) |
| `internal_error` | 500 | `v0.go` · `middleware.go` |
**Коды, которых платформа сегодня достигает, а контракт до 0.3.0 не объявлял:** 403 (`server.go:99`),
500 (`v0.go:518,572`), 431 от `net/http` (`serve.go:79`), 429 на `/auth` (`login.go:173,234`). Из них

View file

@ -1411,7 +1411,7 @@ components:
**The vocabulary is OPEN, and a client MUST render a value it does not know neutrally**
— the bar alone, or a generic phrase — rather than failing, hiding the run, or guessing.
Today's deployments answer `drafting` or `editing`; other values are not a breaking
Today's deployments answer `drafting`, `editing` or `re_pass`; other values are not a breaking
change and do not raise this version.
⚠ Derived by the platform from the same counters as `done` and `total`, so the caption