Accept and ratify platform P0 with corrections: defect and vulnerability register, zone engineering standards, backlog rows for OAuth and credits, stale doc pointers fixed
This commit is contained in:
parent
eeeef897b9
commit
954c034f53
6 changed files with 180 additions and 4 deletions
|
|
@ -12,7 +12,8 @@ openapi.yaml РЯДОМ (байт-копия зонной frontend/docs/api-cont
|
|||
Провенанс-классы ✓/◆/○ ниже выборочно перепроверены кодом движка — легенда честная.
|
||||
|
||||
ОТКРЫТЫ (см. §4): продуктовые К-1/К-3/К-5/К-6/К-8 — владелец; К-4/К-7/К-12 —
|
||||
платформа (П-1, кода ещё нет); К-2/К-9/К-10/К-11 — автор контракта/бэкенд.
|
||||
платформа — ОТВЕЧЕНЫ P0 и ратифицированы приёмкой 04.08 с поправками (носитель — зонный журнал
|
||||
`platform/docs/platform-PROGRESS.md`, раздел «Ратификация приёмкой»); К-2/К-9/К-10/К-11 — автор контракта/бэкенд.
|
||||
Зависимости §3 покрыты строками единого бэклога: 99/100/101/103 (перевешены в
|
||||
БЛОКЕРЫ контракта) + новые 125 (банк-экспорт) · 126 (поднятие потолка) · 127
|
||||
(DDL-коммент). Приложение А — заготовка: фразы и ступени заполняет владелец (В-3).
|
||||
|
|
@ -32,7 +33,7 @@ openapi.yaml РЯДОМ (байт-копия зонной frontend/docs/api-cont
|
|||
|
||||
# Контракт API v0 — спутник спеки: провенанс, обоснования, вопросы
|
||||
|
||||
> **Нормативная поверхность контракта — [`api-contract/openapi.yaml`](api-contract/openapi.yaml)**
|
||||
> **Нормативная поверхность контракта — [`openapi.yaml`](openapi.yaml)** (файл рядом, в этой же папке)
|
||||
> (OpenAPI 3.1). Этот файл её НЕ дублирует: он несёт то, чего YAML не выражает — откуда взято
|
||||
> каждое решение, чем оно обосновано, что осталось открытым. При расхождении по ФОРМЕ
|
||||
> побеждает YAML; при вопросе «почему так» — этот файл.
|
||||
|
|
|
|||
|
|
@ -12,4 +12,6 @@
|
|||
| П-2 | **Глобальный брокер рейт-лимитов провайдеров** (экс-строка 97): гарды движка per-процесс (`pipeline/ratelimit.go:11`, mistral ~48% отказов под параллелизмом), а лимит провайдера — на ВЕСЬ аккаунт: N прогонов = N независимых гардов против общего лимита; воркер отпрашивается у платформы перед вызовом | ДО второго параллельного пользователя | D39.81, D39.84 |
|
||||
| П-3 | **Очередь и конкурентность по книге**: River на том же Postgres, сериализация по `book_id` + пиннинг книги к одному хосту на MVP, лизы `book_leases` с heartbeat — вежливое ожидание вместо аварии | Ф3-пак платформы | STACK_DECISIONS §5, D39.84 |
|
||||
| П-4 | **Учёт токенов/денег per-user + бюджет-гейт ДО старта задачи** (сырьё уже считает движок: `request_log` + `internal/ledger`; помнить: леджер = нижняя граница — строка 78 единого) | Ф3-пак платформы | platform/README, D39.84 |
|
||||
| П-6 | **OAuth-вход популярных провайдеров (Google первым) + модель аккаунта** (направление владельца 04.08): authorization code + PKCE, валидация ID-токена, связывание аккаунта по ПОДТВЕРЖДЁННОЙ почте; собственная серверная сессия сохраняется (D39.84) — OAuth даёт только СОБЫТИЕ входа. Форма и библиотеки — по ресёрч-пакету направления (ратифицирует оркестратор); писатель куки с `__Host`-атрибутами и dev-профиль — здесь же (PD-8) | P1, вместе с П-1 | владелец 04.08 |
|
||||
| П-7 | **Фри-тир + покупка токенов «как у OpenRouter»** (направление владельца 04.08): кредитный леджер в целых микро-долларах, идемпотентные списания, резервация ДО старта прогона, окна фри-тира; продажа кредитов — через провайдера с минимальной комплаенс-поверхностью (merchant-of-record vs Stripe — решение по ресёрчу). ⚠ Гейт: пер-пользовательский учёт отстаёт на попытку, пока не решён открытый канон-вопрос об источнике денег во время попытки (журнал зоны, «Ратификация приёмкой») | P1+, после П-6 | владелец 04.08 |
|
||||
| П-5 | **API лимитов/использования + оповещение стопа по потолку** (решение владельца 04.08, D39.100/ПТ-35, механизм «как Claude Code»): страница лимитов в настройках читает СТАТУС использования (не суммы — деньги на провод не идут, D39.84); стоп по потолку → статус `paused` + оповещение «перевод остановлен: лимиты исчерпаны»; сырьё у движка есть (`request_log`/`ledger`, потолки конфига), форму API предлагает P0 | Ф3, вместе с П-1 | D39.100, ПТ-35, контракт К-8 |
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
## ⚠ Git и зона (читать ДО первой строки кода)
|
||||
|
||||
**Платформенная сессия не коммитит — лендит оркестратор.** Канон — `../CLAUDE.md`, §Гардрейлы; сюда вынесено, потому что отдельного промта платформы ещё нет.
|
||||
**Платформенная сессия не коммитит — лендит оркестратор.** Канон — `../CLAUDE.md`, §Гардрейлы; продублировано здесь, потому что зона живёт своим онбордингом. Стандарты и критерии приёмки зоны — `docs/ENGINEERING_STANDARDS.md`; дефекты и уязвимости — `docs/DEFECT_REGISTER.md` (каждая находка получает строку ДО закрытия).
|
||||
|
||||
1. **Писать только внутрь `platform/`.** Ничего за её пределами — ни `docs/`, ни `backend/`, ни `frontend/`, ни корневых файлов. Нужна правка вне зоны — пинг владельцу, её сделает оркестратор.
|
||||
2. **Чужие незакоммиченные файлы в дереве не трогать**: параллельные сессии — норма.
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
- учёт токенов и денег **на пользователя** (сырьё уже считает движок: `request_log` +
|
||||
`internal/ledger`), потолки и гейт бюджета ДО старта задачи;
|
||||
- очередь задач и запуск воркеров, статусы прогонов, ретраи;
|
||||
- SSE-поток прогресса и живой стоимости во фронт.
|
||||
- SSE-поток прогресса во фронт (⚠ денежные суммы на провод и на экран НЕ идут — D39.84; пользователь видит СТАТУС использования: процент и время сброса, П-5).
|
||||
|
||||
## Чего здесь НЕ будет
|
||||
|
||||
|
|
|
|||
29
platform/docs/DEFECT_REGISTER.md
Normal file
29
platform/docs/DEFECT_REGISTER.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Регистр дефектов и уязвимостей платформы
|
||||
|
||||
> Заведён по решению владельца 04.08 («отдельно ведётся колонка багов и уязвимостей — тут опасно всё»).
|
||||
> Правила: каждая находка любой сессии/приёмки/аудита — строкой сюда ДО закрытия; ID стабилен навсегда;
|
||||
> закрытие — только с коммитом фикса и тестом, пинящим свойство (урок PD-1: свойство без пинящего теста
|
||||
> считается НЕ закрытым). Класс: `vuln` — эксплуатируемо или ослабляет защиту · `bug` — неверное поведение ·
|
||||
> `hardening` — защита в глубину / латентное. Статус: `open` · `fixed(<commit>)` · `accepted-risk(<кем, когда>)`.
|
||||
|
||||
| ID | Класс | Серьёзность | Где | Суть | Статус | Источник |
|
||||
|---|---|---|---|---|---|---|
|
||||
| PD-1 | hardening | minor | `internal/pgstore/pg_test.go:89` | Свойство «в БД только SHA-256, не токен» НЕ запинено тестом: посадка «`Digest` возвращает плейнтекст» выживает — тест сверяет хранимое через тот же `auth.Digest` (self-consistent). Нужен тест с НЕЗАВИСИМО вычисленным хешом либо ассерт «плейнтекст в БД не находится» | open | приёмка P0 (посадка №1) |
|
||||
| PD-2 | vuln | **major, ЖИВАЯ (не латентная)** | `cmd/tmplatformd/main.go:73-82` | Нет `ReadTimeout` ⇒ соединения пиннятся уже СЕГОДНЯ, без единой body-принимающей ручки: `net/http` дренирует непрочитанное тело <256 КБ ВНУТРИ `chunkWriter.writeHeader` до отправки заголовка ответа (`net/http/server.go:1389-1435`), и этот чтение-шаг наследует отсутствующий дедлайн. **Репродуцировано оркестратором на собранном бинаре:** 50 полу-кормленных POST на охраняемый `/v0/*` → сервер отработал и залогировал 50×401 `ms:0`, клиенты получили НОЛЬ байт, fd 7→57 и держались, пока не закрыл КЛИЕНТ (агент-скептик независимо пинил 500 соединений). Ограничителя соединений и документированного edge-прокси в зоне нет. Фикс — одна строка (`ReadTimeout`; для будущего SSE — per-conn дедлайны через `ResponseController`). Вторая половина (`MaxBytesReader`) сегодня не эксплуатируема (ни один хендлер не читает body) — гейт P1: закрыть ДО первого POST-хендлера | open | приёмка P0 (security-линза + скептик + собственная репродукция) |
|
||||
| PD-3 | bug | minor | `internal/httpapi/middleware.go:57` | `Recover` логирует сырой `r.URL.Path` на ERROR — id книг/прогонов утекают в лог, против собственной дисциплины AccessLog (route-pattern, не путь) | open | приёмка P0 (security-линза) |
|
||||
| PD-4 | hardening | minor | `internal/pgstore/sessions.go:41` | WHERE у `Touch` слабее, чем у `Lookup` (нет `idle_expires_at > now`): прямой вызов воскресил бы idle-истёкшую сессию. Через `Require` недостижимо (Touch только после успешного Lookup) — одна строка защиты в глубину | open | приёмка P0 (security-линза) |
|
||||
| PD-5 | bug | minor | `internal/auth/middleware.go:36,45` | Ошибки стора невидимы: сбойный `Lookup` → 401 без единой строки лога (аутентификационный DB-outage выглядит как шторм 401), `Touch` глотается `_ =`. На проводе различать нельзя (оракул) — но лог обязан различать | open | приёмка P0 (security+blind линзы) |
|
||||
| PD-6 | hardening | info | `internal/auth/csrf.go:51` | GET освобождён от CSRF (верно), но SSE-хендшейк — GET с амбиентной кукой: origin-чек хендшейка потока (STACK §5) не покрыт ничем. Закрыть при постройке SSE (P1) | open | приёмка P0 (security-линза) |
|
||||
| PD-7 | bug | info | `internal/pgstore/sessions.go:78` | `DeleteExpiredSessions` никем не вызывается — свип запланировать в P1 (периодическая джоба воркера) | open | приёмка P0 |
|
||||
| PD-8 | hardening | info | `internal/auth/session.go:18` | Писателя куки ещё нет; `__Host-` требует Secure ⇒ локальный dev по HTTP куку не поставит. Решить формой в P1 (dev-профиль), префикс не ослаблять в проде | open | приёмка P0 |
|
||||
| PD-9 | bug | minor | `cmd/tmplatformd/main.go:81` | `BaseContext` возвращает signal-контекст ⇒ SIGTERM мгновенно рубит контексты ВСЕХ in-flight запросов, и 15-секундный дренаж `Shutdown` мёртв для ctx-aware хендлеров. Fix: BaseContext без signal-ctx; сигнал ведёт только Shutdown | open | приёмка P0 (faults-линза) |
|
||||
| PD-10 | bug | minor | `internal/ingest/decoder.go:42,61,67` | Три ужесточения декодера: (а) `hello` с пустым `engine_run_id` принимается — а это половина ключа идемпотентности; (б) seq самого hello не пинится к 1 — потеря пре-хендшейковых строк недетектируема; (в) mid-stream `hello` (любой версии, вкл. мажор 9.9) уходит в Sink как обычное событие — version-гейт держит только строку 1 | open | приёмка P0 (faults-линза) |
|
||||
| PD-11 | bug | minor | `internal/pgstore/migrations/00002_readmodel.sql:137,177` | Неиндексированные FK-каскады: `notes.chapter_id` и `bank_decisions.term_id` — каскадное удаление сканирует таблицы | open | приёмка P0 (faults-линза) |
|
||||
| PD-12 | bug | info | `internal/ingest/supervisor.go:82-84` | Сбой Sink в начале прогона ⇒ платформа дренирует ВЕСЬ оставшийся поток в `io.Discard` часами: ceiling/bank_stop-события выбрасываются, никто не оповещён. Нужна политика «БД платформы упала посреди прогона» (ретраи синка / деградация с алармом) — дизайн-вопрос P1 | open | приёмка P0 (faults-линза) |
|
||||
| PD-13 | bug | info | `internal/ingest/supervisor.go:64` | Краш платформы осиротляет процесс движка: ни process-group, ни pidfile, ни пути реаттача (поток невосстановим, повторный спавн упрётся в EXCLUSIVE-лок). Дизайн супервизии P1 | open | приёмка P0 (faults-линза) |
|
||||
| PD-14 | hardening | info | `internal/httpapi/server.go:79` | `readyz`: ping без собственного таймаута (WriteTimeout нет намеренно — SSE), эндпоинт неаутентифицирован и без rate-limit — задушить дешёво; таймаут на ping + прикрыть на ops-слое | open | приёмка P0 |
|
||||
| PD-15 | bug | info | `internal/ingest/resync.go:32` | Деньги в ре-синке — float64, а `usage_windows` хранит micro-USD именно против дрейфа: дрейф входит шагом раньше (JSON-парс + суммирование дельт). Принять осознанно или считать в целых | open | приёмка P0 (faults-линза) |
|
||||
| PD-16 | bug | minor | `internal/httpapi/server.go:81` | `readyz` глотает ошибку ping вопреки собственному комменту «the reason stays in the log» — лога нет | open | приёмка P0 (blind-линза) |
|
||||
| PD-17 | bug | minor | `Makefile:41-42` | Баннер «did NOT run (no database)» печатается и при ПРОГНАННЫХ БД-тестах (безусловный); батарея гоняет сьют дважды ради имён скипов (второй прогон без -race) | open | приёмка P0 (blind-линза) |
|
||||
| PD-18 | bug | info | `internal/pgstore/migrations/00002_readmodel.sql:9,139` | Коммент шапки «engine vocabulary never crosses this seam» противоречит `notes.reason` (движковая причина хранится, не проецируется); коммент переписать честно | open | приёмка P0 (canon-линза) |
|
||||
| PD-19 | bug | info | `internal/ingest/resync.go:44` | `WorstFlagReason` задокументирован «stored», а колонки в `chapters` нет — доккоммент или схема, одно из двух | open | приёмка P0 (canon-линза) |
|
||||
60
platform/docs/ENGINEERING_STANDARDS.md
Normal file
60
platform/docs/ENGINEERING_STANDARDS.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Стандарты разработки и критерии приёмки платформы
|
||||
|
||||
> Ратифицировано оркестратором №14 по решению владельца 04.08: платформа считает токены пользователей
|
||||
> и держит их логины — «ошибки недопустимы»; качество и отказоустойчивость выше скорости фич.
|
||||
> Каждый промт платформенной сессии обязан ссылаться на этот документ; отступление = пинг, не тихая девиация.
|
||||
|
||||
## 1. Рамка
|
||||
|
||||
- **Несущие пути** — auth/сессии/CSRF, всё денежное (usage, лимиты, биллинг), ингест/супервизия движка,
|
||||
миграции. Изменение несущего пути = «тяжёлый» класс: полный цикл приёмки §3, без исключений.
|
||||
- **Сторонние тулы прежде самописного** (решение владельца 04.08): проверенные библиотеки предпочитаются
|
||||
своему коду везде, КРОМЕ похода в ядро переводов (tmctl зовётся процессом, обвязка своя) и мест, где
|
||||
stdlib и есть индустриальный стандарт (net/http, database/sql-слой через pgx). Новая зависимость —
|
||||
строка в `STACK_DECISIONS.md` с датой релиза и «зачем», ратифицирует оркестратор.
|
||||
- Канон-инварианты зоны без изменений: D39.84 (никаких денежных сумм на проводе/UI/INFO-логах) ·
|
||||
D39.85 (шов: NDJSON → апсерт `(engine_run_id, seq)` → SSE из Postgres; живой SQLite движка не читать) ·
|
||||
ПТ-34 (noindex/no-store на всём) · языко-агностичность.
|
||||
|
||||
## 2. Базовые стандарты (проверяются каждой приёмкой)
|
||||
|
||||
**Безопасность** (направление — OWASP ASVS, без карго-культа):
|
||||
- Сессии: одна серверная, opaque-токен ≥256 бит из crypto/rand, в БД только хеш, немедленный отзыв,
|
||||
два срока (idle скользит, absolute нет). Плейнтекст-токен не персистится, не логируется и живёт
|
||||
в памяти только окно обработки запроса (решение владельца 04.08).
|
||||
- Секреты — только из окружения/секрет-менеджера; в репо и в логи не попадают НИ на каком уровне.
|
||||
- Ввод: только параметризованные запросы; каждый body-принимающий хендлер несёт `http.MaxBytesReader`
|
||||
и дедлайн (гейт PD-2 регистра); закрытые словари валидируются на входе И констрейнтами БД.
|
||||
- Ответы: problem+json; внутренности и оракулы (unknown-vs-expired токен) на провод не текут;
|
||||
заголовки ПТ-34+nosniff+no-referrer — мидлварью на всё, не дисциплиной хендлера.
|
||||
- `govulncheck` (`make vuln`) — перед каждым лендингом; красный = блокер лендинга.
|
||||
|
||||
**Отказоустойчивость:**
|
||||
- Ингест идемпотентен (`(engine_run_id, seq)`, high-water mark в одной транзакции с эффектом);
|
||||
at-least-once — норма, дубль — не ошибка.
|
||||
- Graceful shutdown с реальным дренажем; пул с лимитами; транзиентные ошибки БД — ретраи с джиттером;
|
||||
миграции идемпотентны, down-путь существует и гоняется тестом.
|
||||
- Отказ зависимости — деградация с диагностикой, не тишина: проглоченная ошибка без лога и без
|
||||
комментария-обоснования = дефект (класс PD-5/PD-16).
|
||||
|
||||
**Наблюдаемость:** структурные логи (route-pattern, не сырой путь — id пользователя/книги в логи не
|
||||
текут), `X-Request-Id` на каждом ответе, ошибки видимы на своём уровне. Money-числа — только в
|
||||
приватных таблицах, ни в логах, ни в ответах (кроме P-5-статуса процентами).
|
||||
|
||||
**Контракт-первичность:** поверхность = `docs/architecture/14-api-contract/openapi.yaml`; расхождение
|
||||
кода со спекой = дефект чей-то один: либо спека правится через ратификацию, либо код. Генерация
|
||||
типов/стабов из спеки (oapi-codegen — кандидат, ратификация при P1) предпочтительнее ручного дрифта.
|
||||
|
||||
## 3. Критерии приёмки сессии (Definition of Done)
|
||||
|
||||
1. `make check` зелёный офлайн; с `TM_PLATFORM_TEST_DSN` — та же батарея плюс живая схема
|
||||
(порядок подъёма PG без root — `STACK_DECISIONS.md`); скипы названы вслух и их ноль при DSN.
|
||||
2. Каждый деливерабл проверен ИСПОЛНЕНИЕМ (сервер поднят и опрошен, миграции применены дважды,
|
||||
констрейнты сработали поимённо, супервизор гонял настоящий процесс) — не чтением.
|
||||
3. Заявленное в отчёте свойство несущего пути ОБЯЗАНО быть запинено тестом, который ловит мутацию
|
||||
этого свойства; сессия сама называет в отчёте, какой тест что пинит. Приёмка оркестратора сажает
|
||||
СВОИ мутации (урок P0: «в БД только хеш» было истинно, но не запинено — посадка выжила).
|
||||
4. Адверсариальная самопроверка (author≠reviewer) до сдачи; найденное — в отчёт, не молча.
|
||||
5. Новые находки — строками в `DEFECT_REGISTER.md` тем же деревом; закрытие дефекта — коммит + пинящий тест.
|
||||
6. Дерево не коммитится сессией — лендит оркестратор после адверсариальной приёмки
|
||||
(для несущих путей — с воркфлоу-верификаторами и посадками).
|
||||
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
## Текущее состояние
|
||||
|
||||
- **P0 ПРИНЯТ и ЗАЛЕНДЕН** (`eeeef89`, приёмка оркестратора №14 04.08 — раздел «Ратификация приёмкой»
|
||||
ниже). Дизайн-ответы К-4/К-7/К-12/П-5 ратифицированы С ПОПРАВКАМИ. Найдено 19 дефектов, все
|
||||
строками в `DEFECT_REGISTER.md`; один — ЖИВАЯ уязвимость (PD-2, пиннинг соединений), гейт P1.
|
||||
- **Стандарты зоны заведены** (решение владельца 04.08): `ENGINEERING_STANDARDS.md` (критерии приёмки,
|
||||
индустриальные базовые линии) + `DEFECT_REGISTER.md` (отдельная колонка багов и уязвимостей).
|
||||
- **P0 собран** (сессия 04.08): модуль компилируется, батарея зоны `make check` зелёная,
|
||||
`/healthz` и `/readyz` проверены живым запуском против живого Postgres 18.4.
|
||||
- Стек запинен и live-сверен — `STACK_DECISIONS.md` (зонный).
|
||||
|
|
@ -25,6 +30,85 @@
|
|||
заголовок `X-TM-Client` на небезопасных запросах cookie-пути. Это требование к ФРОНТУ, и его
|
||||
место — в описании `sessionCookie` в спеке. Реализовано и проверено тестами.
|
||||
|
||||
## Ратификация приёмкой (оркестратор №14, 04.08)
|
||||
|
||||
**Вердикт: P0 ПРИНЯТ, заленден `eeeef89`.** Метод: батарея пере-прогнана мной (офлайн зелёная; с живым
|
||||
PostgreSQL 18.4, поднятым без root, все три БД-гейченных теста зелёные — 6/6 констрейнтов сработали
|
||||
поимённо) · живые пробы бинаря (healthz 200 без БД · readyz 503 честно · Bearer без стора → 401, не
|
||||
паника · неизвестный путь под `/v0` → 401 раньше 404 · CSRF: cookie-POST без `X-TM-Client` 403,
|
||||
cross-site 403, Bearer-POST не требует заголовка · SIGTERM → «shutting down» и чистый выход) ·
|
||||
**три СВОИ мутации** в несущие свойства (см. ниже) · адверсариальный воркфлоу из пяти линз
|
||||
(слепое ревью до отчёта · security-охота вне карты · корректность/отказоустойчивость · канон-сверка ·
|
||||
опровержение дизайн-ответов) со скептик-пассом.
|
||||
|
||||
**Что подтверждено исполнением:** зонная дисциплина (в коммите только `platform/*`, чужого нет) ·
|
||||
модуль-sibling, `backend/internal` не импортируется, `go.work` не заведён · живой SQLite движка нигде
|
||||
не открывается · event-sourcing не построен (high-water mark) · деньги отсутствуют на проводе и в
|
||||
INFO-логах (stderr движка уходит в ФАЙЛ попытки — верно) · ПТ-34-заголовки мидлварью на всём, не
|
||||
дисциплиной хендлера · имена полей `status --json` сверены 1:1 с `pipeline/status.go:37-130` ·
|
||||
экзит-коды сверены с `cmd/tmctl/main.go:30-52` · языко-агностичность (пар-литералов нет) ·
|
||||
пин линтера идентичен движковому.
|
||||
|
||||
**Мои мутации (author≠reviewer):** (1) `Digest` возвращает плейнтекст вместо SHA-256 — **тесты
|
||||
ВЫЖИЛИ** ⇒ свойство «в БД только хеш» истинно, но НЕ запинено (тест сверяет через ту же функцию);
|
||||
строка PD-1. (2) Снятие требования `X-TM-Client` — тест упал поимённо ✓. (3) Ослабление констрейнта
|
||||
`units_translated_has_text` до `check (true)` — тест упал поимённо ✓. Дерево после мутаций
|
||||
восстановлено байт-в-байт (sha256-сверка).
|
||||
|
||||
**Главная находка приёмки — PD-2, ЖИВАЯ уязвимость, а не латентная.** Отсутствие `ReadTimeout`
|
||||
позволяет пиннить соединения СЕГОДНЯ, без единой body-принимающей ручки: `net/http` дренирует
|
||||
непрочитанное тело <256 КБ внутри `chunkWriter.writeHeader` ДО отправки заголовка ответа, и это
|
||||
чтение наследует отсутствующий дедлайн. Репродуцировано мной на собранном бинаре (50 полу-кормленных
|
||||
POST: сервер залогировал 50×401 `ms:0`, клиенты получили ноль байт, fd 7→57 до закрытия КЛИЕНТОМ);
|
||||
скептик независимо пинил 500. Фикс — одна строка; **гейт: закрыть первым шагом P1**.
|
||||
|
||||
**Ратификация дизайн-ответов — все четыре ПРИНЯТЫ, каждый с обязательной поправкой:**
|
||||
|
||||
- **К-4 (ревизия пер-книжная, кадр SSE = `books.revision`) — ПРИНЯТ + три поправки.** (а) Посылка
|
||||
«единственный писатель» неверна: HTTP-хендлеры тоже пишут книго-скоупное состояние (подпись банка
|
||||
обязана вернуть бампнутую ревизию). Нормативный механизм — не «единственность писателя», а
|
||||
**блокировка строки книги, удерживаемая до коммита** (`update books set revision = revision + 1`
|
||||
сериализует и бамп, и порядок коммитов); материализатор и хендлеры обязаны ходить через неё.
|
||||
(б) Одна транзакция = одна ревизия, но НЕСКОЛЬКО кадров: докачка по `Last-Event-ID` обязана читать
|
||||
`revision >= R` (дельта-чтения идемпотентны), иначе теряются кадры-братья транзакции R. (в) Дельта-
|
||||
чтение не выражает УДАЛЕНИЯ (банк переписывается целиком, пере-чанковка заменяет главы/юниты) ⇒
|
||||
после любой транзакции-замены сервер обязан выдать `resync_required` (событие в контракте есть).
|
||||
- **К-7 (keyset-курсор на всех списках, `next_cursor` всегда) — ПРИНЯТ + две поправки.** (а) Курсор
|
||||
НЕ привязывать к `books.revision`: тот бампается на каждой материализации, и на книге 5000+ глав
|
||||
правило «сменилась ревизия — начни цикл заново» даёт вечный рестарт пагинации во время прогона.
|
||||
Привязка — к СТРУКТУРНОЙ эпохе (поколение манифеста / `books.chunker_version`), которая меняется
|
||||
только при (пере-)разборе. (б) Отклонение протухшего курсора — обязанность СЕРВЕРА (MUST), не
|
||||
клиента: курсор непрозрачен, клиент не может её исполнить. Ключи сортировки для банка и замечаний
|
||||
назвать при правке спеки (для глав — `(book_id, number)`).
|
||||
- **К-12 (опрос с `Retry-After`, 202+`Location`) — ПРИНЯТ.** Аргумент структурный и верен: поток
|
||||
привязан к прогону, экспорт делают с законченной книги. Поправка формы: `Retry-After` на 200
|
||||
стандартом не определён (RFC 9110 — 503 и 3xx), поэтому в спеке объявить его ЯВНЫМ заголовком
|
||||
этого ответа, а не полагаться на общую семантику.
|
||||
- **П-5 (`GET /v0/usage` статусом, `Run.paused_reason`, потолок поднимает платформа) — ПРИНЯТ +
|
||||
три поправки.** Несущий клейм ПЕРЕПРОВЕРЕН мной по коду и подтверждён: `Ceilings` объявлены
|
||||
(`backend/internal/config/book.go:106`), в канон `BriefHash` НЕ входят (`:280-297`, доккоммент
|
||||
`:264` «wiring fields … deliberately excluded»), и ни один другой хеш их не сворачивает (снапшот
|
||||
волны `pipeline/snapshot.go`, кортеж чекпойнта `stagerun.go:406-412`) ⇒ поднятие потолка не двигает
|
||||
снапшот и не вызывает ре-билл. Поправки: (а) `Run.paused_reason` не имеет колонки — завести в схеме
|
||||
(собственный мандат сессии: у каждого поля ответа есть источник); (б) `revision` в ответе usage не
|
||||
имеет скоупа — назвать счётчик пользователя либо убрать поле; (в) формулировка «поток денег не
|
||||
несёт и НЕ ДОЛЖЕН» подана как следствие D39.84 — это не так: D39.84 запрещает суммы на
|
||||
ПОЛЬЗОВАТЕЛЬСКОМ проводе, экране и в INFO-логах, а внутренний поток движок→платформа в приватную
|
||||
таблицу — другая поверхность. Вопрос «нести ли версионированное денежное событие в словаре строки
|
||||
103» ОТКРЫТ (см. ниже), а не закрыт.
|
||||
- **Три находки сессии подтверждены и маршрутизированы:** движковый `run_id` в кадре `hello`
|
||||
(иначе resume отбрасывается high-water mark'ом) — в строку 103 единого бэклога; статусы до прогона
|
||||
и библиотека без живого канала — правка спеки (S3); `X-TM-Client` в спеку — туда же, с уточнением
|
||||
«значение любое, несущей является ПРИСУТСТВИЕ заголовка».
|
||||
|
||||
**Открытый канон-вопрос, поднятый приёмкой (нужно слово владельца/решение оркестратора):
|
||||
у платформы нет санкционированного источника денег ВО ВРЕМЯ попытки.** Движок держит эксклюзивный
|
||||
лок (`status --json` физически недоступен), его stderr-INFO с ценами парсить запрещено (анти-паттерн
|
||||
research/23 §2 + запрет INFO-денег), а словарь строки 103 денег не несёт. Следствие: пер-пользовательские
|
||||
окна отстают на целую попытку (часы), и единственный он-лайн-гард — пер-книжные потолки, которые
|
||||
платформа обязана ставить консервативно. Варианты — версионированное денежное событие в словаре 103 ·
|
||||
консервативная политика потолков · нарезка попыток — в ресёрч-пакете направления «биллинг».
|
||||
|
||||
## Дизайн-ответы на ратификацию
|
||||
|
||||
### К-4 — ревизия: пер-ресурсная со скоупом КНИГА; чтения её несут
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue