491 lines
69 KiB
Markdown
491 lines
69 KiB
Markdown
# Журнал зоны «Платформа»
|
||
|
||
> **Что это.** Состояние зоны и её живые остатки. Обратно-хронологический: свежее выше.
|
||
> Отработавшие эры вынесены срезами в [`archive/`](archive/) — читать только по конкретной ссылке.
|
||
|
||
## Состояние зоны на 07.09.2026
|
||
|
||
| Вопрос | Ответ |
|
||
|---|---|
|
||
| последний заленджённый пак | «форма заказа перевода» (05–06.09, `628cc56`+`36ea8b8`, акты **D39.208**, **D39.211**–**D39.214**), канон контракта `0.12.0` |
|
||
| пак в дереве, не закоммиченный | «деньги и правда на экране» — отчёт ниже |
|
||
| открытые дефекты | `DEFECT_REGISTER.md` (счёт — `python3 docs/scripts/counts.py` от корня) |
|
||
| нормы и приёмка | `ENGINEERING_STANDARDS.md` · направление — `PLATFORM_DIRECTION.md` · стек и стенд — `STACK_DECISIONS.md` |
|
||
| незакрытые куски работы | `../BACKLOG.md` (`П-N`) |
|
||
| как разворачивается | `../deploy/README.md` |
|
||
|
||
## ПАК «ДЕНЬГИ И ПРАВДА НА ЭКРАНЕ» — ОТЧЁТ (06–07.09, `textmachine-bf`)
|
||
|
||
> Промт `docs/PLATFORM_MONEY_TRUTH_SESSION_PROMPT.md`, вход HEAD `e4097cb`, дерево на входе чисто.
|
||
> Зона НЕ коммитит — дерево передано оркестратору №23 (`textmachine-a8`).
|
||
|
||
### Что построено
|
||
|
||
| Заказ | Что стало с деревом | Чем предъявлено |
|
||
|---|---|---|
|
||
| §4.0 триаж журнала | журнал 5029 → **491** строки, из которых 385 — этот отчёт; две эры вынесены срезами в `archive/` | `carriers.py` по живому файлу — **0** находок при 491 осмотренных строках; вынос разобран построчно (ниже) |
|
||
| `PD-441` объявленная маржа | каждая строка `settlement` несёт БАЗИС; `runs` печатает `≥` перед суммой и легенду | два независимых пути по деньгам (ниже) · пины `TestEverySettlementSaysThatItsFigureIsAFloor`, `TestOnlyAnEndingTheEngineChoseIsSettledAsComplete`, `TestTheOperatorsTableSaysSpentIsAFloor…` |
|
||
| `PD-424` окно гонки | арбитр В ХРАНИЛИЩЕ: `AbandonOrder.ProofAttemptID` + `ProofSpawns`, сверка под книжной блокировкой | пины `TestAProofAboutTheAttemptBeforeThisOneIsRefused`, `TestAnAttemptClaimedWhileSystemdWasBeingAskedIsRefused` |
|
||
| `PD-438` видимость парковки | колонка `run_attempts.parked_at`, гейдж `tm_platform_parked_attempts`, ячейка PARKED в `runs` | пины `TestAParkedAttemptIsVisibleInTheRowTheGaugeAndTheListing`, `TestTheOperatorsTableSaysSpentIsAFloor…` |
|
||
| строки 285 + 325 | ⛔ **ПОСТРОЕНО, НО НЕ ГОТОВО — сработало правило остановки, см. секцию ниже.** Синхронный $0 `tmctl manifest` на приёме: fail fast и вход сужен по ЧИСЛУ ГЛАВ | 4 пина `failfast_test.go` + `TestOnlyTheHolderOfTheParseClaimCanDeleteARefusedIntake` + пин медленного разреза (круг 3) и три пина круга 4 (деплойные ветви · материализация · очередь) |
|
||
| строка 305 | `make check` при таймауте называет пакет, тест и причину | воспроизведено и предъявлено сквозным `make check` на копии |
|
||
| §4.4б | комментарий `THE FIX IS NOT IN THIS PACKAGE` приведён к поведению | греп по отозванной формулировке — 0 при 197 осмотренных `.go` |
|
||
|
||
### Заказ по пунктам — исход каждого
|
||
|
||
| Пункт промта | Исход |
|
||
|---|---|
|
||
| §4.0 триаж журнала | **сделано**; независимо пере-проверено оркестратором №23 мультимножеством и `carriers.py` по самим срезам |
|
||
| §4.1 `PD-441` | **сделано в достижимой половине**; вторая половина — движковая, названа поимённо и ушла строкой бэклога |
|
||
| §4.1 `PD-424` | **сделано**, и предмет оказался шире ряда: окон два |
|
||
| §4.1 `PD-438` | **сделано** |
|
||
| §4.2 строка 282 | **сознательно не делаю** — снято до выдачи, платформенная половина уже построена |
|
||
| §4.3 строки 285 и 325 | ⛔ **построено и ОСТАНОВЛЕНО**: пять кругов самопроверки, девять major из одиннадцати — в этом одном механизме, третий подряд промах бюджета хвоста. Предмет уезжает отдельным паком по правилу остановки оркестратора №23; семь незакрытых пунктов перечислены поимённо |
|
||
| §4.4 два малых дока | **не трогаю** — зона оркестратора; расхождения ушли пингами (ниже) |
|
||
| §4.4а строка 305 | **сделано**, предъявлено сквозным `make check` на копии |
|
||
| §4.4б комментарий | **сделано** |
|
||
| §4.5 запреты | **соблюдены**, проверено исполнением: контракт, `ENGINEERING_STANDARDS`, `PLATFORM_DIRECTION`, `backend/` — 0 изменённых файлов; коммитов 0; индекс пуст |
|
||
| пинги оркестратора: `PD-458`, `П-10` | **пере-сняты по дереву, оба подтвердились**; `PD-458` → `fixed`, `П-10` закрыта актом зоны |
|
||
|
||
### Два круга самопроверки — что нашёл каждый
|
||
|
||
⚠ **Круги сошлись на ТРЕТЬЕМ, а не на втором.** Второй я вела сама и объявила сходимость
|
||
преждевременно: адверсариальный субагент нашёл четыре major, три из них — дефекты, которые ввёл ЭТОТ
|
||
пак и которых не видел ни один мой пин.
|
||
|
||
| круг | находка | что сделано | чем предъявлено |
|
||
|---|---|---|---|
|
||
| 1 (мой, по готовой работе) | посадка «снят сравнитель попытки» ВЫЖИЛА — случай ловился чужим охранником | новой попытке дан тот же счёт заявок + сверка ТЕКСТА отказа | пере-посадка красная, текст называет именно сравнитель попытки |
|
||
| 1 | посадка «снят охранник `is null`» ВЫЖИЛА — два охранника прикрывали друг друга | добавлено обращение к записи напрямую | пере-посадка красная: `a second mark moved the stamp` |
|
||
| 1 | синхронный разрез добавил хвост, о котором бутовый гейт не знал | `UploadSettle = CutBudget + writeBudget + 30s`, `CutBudget` стал константой | пин + мутация, текст называет оба числа |
|
||
| 2 (по заказу и по первому кругу) | «≥» и PARKED были предъявлены ЧТЕНИЕМ КОДА, а не исполнением | заведён пин, читающий настоящий вывод команды | он же поймал мою ошибку в ожидании: `money.USD()` не печатает `$` |
|
||
| 2 | охранники `DeleteRefusedIntake` (стадия, claim, отсутствие прогона) не запинены | заведён пин | мутация «охранник claim'а всегда истинен» → `gave <nil>, want ErrNoBook` |
|
||
| 2 | дельта контракта врала про `character_count_exact` и `structure` | ⚠ **починка второго круга оказалась НЕВЕРНОЙ и пере-сделана четвёртым:** материализация с интейка снята, поэтому в `201` оба поля отсутствуют ВСЕГДА, а не «когда материализация удалась» | дельта переписана; пин `TestTheIntakeNeitherMaterializesNorEnqueuesWhenItsCutSucceeds` |
|
||
| 2 | числа отчёта устаревали четырежды | сняты заново после последней правки | команды и выводы — в разделах ниже |
|
||
| **3 (опровергатель, `fable`)** | ⛔ **пере-чтение строки для `201` шло на контексте, открытом ДО тела** (бюджет 30 с), а разрез длится до 90 с ⇒ после медленного разреза `201` нёс `parsing`/0 глав над строкой `not_started`/500 | свежий `writeCtx` для пере-чтения | пин `TestASlowCutStillAnswersWithTheRowAsItStandsAfterIt`; бюджет укорочен сеемым полем, а не ожиданием |
|
||
| 3 | ⛔ **три ветви деплойного класса НЕ отдавали claim и тратили попытку**, а одна писала `rejected`, который `201` выносил пользователю | все три при `atIntake` возвращают `errNotConclusive`, не трогая `defer_`/`reject` | пин `TestADeploymentFaultLeavesNoVerdictAboutTheFile`: `parse_attempts = 0`, `parse_started_at is null` |
|
||
| 3 | ⛔ **синхронная материализация читательской поверхности** (до 5 мин) держала запрос и не входила в `UploadSettle` | на интейке не материализуем — долг записан, забирает свип; `UploadSettle = CutBudget + 3×writeBudget` с перечнем шагов | гейт `internal/config` читает саму константу |
|
||
| 3 | ⛔ **маппинг обоих отказов на HTTP не был запинен** — обмен кодов местами оставлял пакет зелёным | пин по КОДУ, а не по факту 400 | `TestTheIntakesOwnRefusalsReachTheWireWithTheirOwnCodes` |
|
||
| 3 | ⚠ **сужение входа действует только на синхронной ветви** | закрыть сегодня нечем: словарь `RejectReason` закрыт, ближайшее значение УДАЛЯЕТ исходник | заведён `PD-463`, пинг ниже |
|
||
| 3 | `PD-458` помечен `fixed(в дереве пака…)` — ложная атрибуция | `fixed(0632a30)` | `git merge-base --is-ancestor 0632a30 e4097cb` → да |
|
||
| 3 | ряды `PD-441`/`PD-424`/`PD-438` не несли диспозиции пака | дописаны, форма ряда сохранена | `awk -F'|' '{print NF-2}'` по трём рядам → 7, 7, 7 при шапке 7 |
|
||
| 3 | `TestTheUploadSettleBudgetCoversTheSynchronousCut` — ТАВТОЛОГИЯ | тест удалён, вместо него перечень шагов в комментарии константы | `CutBudget = 10h` оставлял его зелёным, а `internal/config` давал 12 красных |
|
||
| 3 | замер денег не воспроизводился: скратч-БД удалена, команда не названа | стал ПИНОМ, печатающим обе стороны | `TestTheRawLedgerAndTheReadModelAgreeOnWhatWasSpent` |
|
||
| 3 | живой `STACK_DECISIONS` отсылал в архив, чей баннер запрещает исполнять инструкции | сказано, что правило целиком стоит на месте, а архив — археология | — |
|
||
| 3 | док-комментарий `cutNow` склеен с `cutResult` | разделены | `gofmt` и `go vet` чисты |
|
||
| 3 | числа «потеряна 1 строка» и «195 осмотренных `.go`» | пере-сняты: **10** строк (все названы) и **197** | раздел «Команды и их вывод» |
|
||
| **4 (опровергатель, `fable`)** | ⛔ **задание очереди ставилось в транзакции `StartParsing` и гонялось с синхронным разрезом за один claim** — выигрывает очередь ⇒ fail-fast молча нет; выигрывает разрез ⇒ job съеден впустую, и после возврата claim'а книгу подбирает только свип через 20 мин | job ставится ТОЛЬКО там, где разрез не идёт; на пути «вердикта нет» — вместе с возвратом claim'а, одной транзакцией (`ReleaseParseClaim`) | пины `TestTheIntakeNeitherMaterializes…` и `TestACutWithNoVerdictGivesTheClaimBackAndEnqueuesTheJob`, обе мутации красные |
|
||
| 4 | ⛔ **`UploadSettle` снова не покрывал хвост:** `StartParsing` (30 с) выпал из перечня, а квитанция идемпотентности 10 с, не 30 ⇒ худший путь 190 с при константе 180 | `CutBudget + 4*writeBudget`, перечень шагов пере-написан ПО КОДУ и по бюджету каждого | гейт `internal/config` читает саму константу |
|
||
| 4 | ⛔ **из «трёх ветвей деплойного класса» запинена была одна**; мутации на `ErrStorageGone` и `ErrDirectoryGone` выживали | обе ветви решаются ДО вызова движка, поэтому запинены на своём уровне | `TestTheBranchesDecidedBeforeTheEngineAlsoLeaveNoVerdict` |
|
||
| 4 | ⛔ **починка «не материализуем на интейке» не имела пина** | заведён | мутация «снят `!atIntake`» → `the intake materialized the reading surface (1 claims, 1 refreshes)` |
|
||
| 4 | ⛔ **дельта контракта противоречила починке третьего круга** — обещала `character_count_exact: true` там, где его теперь не бывает | дельта переписана: оба поля отсутствуют в `201` ВСЕГДА | пин выше |
|
||
| 4 | `Settle` потерял док-комментарий: мой тип встал между ним и функцией | комментарий возвращён | `go doc ./internal/pgstore Store.Settle` печатает его |
|
||
| 4 | `PD-461` сам нёс дефект, который описывает (9 полей), и занижал счёт | ряд переписан: полей 7, строк ЧЕТЫРЕ, а не две | `awk` по разделителю: `PD-461` → 7 |
|
||
| 4 | в таблице «Что построено» стоял носитель-призрак — удалённый тест | заменён на настоящие | `grep` по имени → 0 |
|
||
| 4 | ⚠ **опровергатель ошибся** в одном числе: `character_count_exact` «в 3 файлах» | пере-снято: **4** (`v0.go` 2 · `project.go` 1 · `v0_test.go` 2 · `books.go` 2); он грепал только строковый вид имени | команда в разделе ниже |
|
||
| 4 | ⚠ мутация «снят охранник стадии у `DeleteRefusedIntake`» выживает | **по построению, а не из-за дыры:** каждый терминальный переход (`FinishParse`, `reject`) обнуляет `parse_started_at`, поэтому непустой claim влечёт `status = 'parsing'` — охранник избыточен | `grep -n parse_started_at internal/pgstore/books.go` — все четыре писателя |
|
||
|
||
### `PD-424`: окон оказалось ДВА, а не одно
|
||
|
||
Ряд называл одно — заявку на спавн между пробой systemd и коммитом. По коду их два, и второе опаснее:
|
||
`AbandonRun` под книжной блокировкой читает **любую живую попытку** (`a.ended_at is null`), поэтому
|
||
разблокировавшаяся расплата + `restart` подставляют под доказательство о попытке N **попытку N+1** с
|
||
живым процессом. Лечение — доказательство приходит парой (`ProofAttemptID`, `ProofSpawns`) и
|
||
сверяется в той же транзакции; расхождение — `ErrProofOvertaken`, отказ, а не применение.
|
||
`unit_name` в свидетели не годится: `ReleaseSpawnClaim` возвращает его в NULL. Отсюда монотонный
|
||
счётчик `spawns`, инкремент — внутри самого CAS `RecordSpawn`.
|
||
|
||
### Миграция `00034` (санкционирована оркестратором №23)
|
||
|
||
`run_attempts`: `spawns integer not null default 0` · `parked_at timestamptz`. Down-путь ГОНЯЕТСЯ
|
||
(`pgstore.TestMigrationsRollBackAndReapply`, зелёный на живом Postgres). Существующие строки поведения
|
||
не меняют: сверка идёт с ПРОЧИТАННЫМ числом, не с абсолютным. План гейджа снят исполнением: обе
|
||
подвыборки (`parked_at`/`quarantine_reason`) идут `Index Scan using run_attempts_live_idx` — новый
|
||
индекс не нужен. Запись парковки — только на ПЕРЕХОДЕ, в установившемся состоянии ноль операторов.
|
||
|
||
### `PD-441`: чем ограничена платформенная половина
|
||
|
||
Маржу платформа **измерить не может** — биллинга провайдера у зоны нет, а движковый леджер её не
|
||
несёт. Что сделано: цифра перестала читаться как цена. Что НЕ сделано и почему: показывать маржу
|
||
конечному пользователю нечего — недо-счёт бьёт по ДЕПЛОЮ, баланс пользователя завышен в его же
|
||
пользу (решение оркестратора №23, 06.09; формулировка «показать пользователю» из промта снята).
|
||
|
||
**Движковая половина — заказ следующему паку, поимённо:**
|
||
1. `backend/internal/pipeline/stagerun.go`, ветвь `No 2xx ever arrived: nothing was billed` — сеттлить
|
||
ОЦЕНКУ резервации, а не ноль, когда отменённый вызов уже ушёл; различитель — факт ухода, порог
|
||
обязан быть ЗАМЕРЕН, а не назначен (ряд называет латентность кандидатом: 20 мс против 156 000 мс).
|
||
2. `tmctl status --json` — публиковать рядом с `committed_usd` число и сумму строк, чья цена
|
||
ОЦЕНОЧНАЯ или неизвестна (движок уже печатает `estimated-cost rows: N`, строка бэклога **78**).
|
||
Без этого платформа умеет говорить «≥ X», но никогда «≥ X, до Y».
|
||
|
||
### Строки 285 и 325: одна правка, предикат — по данным
|
||
|
||
`tmctl manifest` зовётся синхронно после последнего байта. Четыре исхода:
|
||
`глав ≥2` → `FinishParse` инлайн, `201` несёт `not_started` и число глав · `глав 0` → `400`
|
||
`invalid_request`, `errors[{file, no_book}]`, не принято ничего · `глав 1` → `400`,
|
||
`errors[{file, no_chapter_structure}]` · **деплойный класс или таймаут** → `201 parsing`, очередь
|
||
доделывает, claim ВОЗВРАЩАЕТСЯ (иначе задание очереди нашло бы книгу занятой и ничего не сделало).
|
||
|
||
⭐ Отказ **по числу глав, а не по расширению**: выдача кладёт по одному XHTML на главу ДВИЖКА, значит
|
||
«книга одним полотном» ⟺ движок нарезал <2 глав. `.epub` движок режет по nav/NCX и он проходит —
|
||
блокировка по расширению отказала бы тому, что мы умеем доставлять. Go по паре и формату не ветвится.
|
||
|
||
#### Дельта контракта, которую ратифицирует оркестратор (минор `0.12.0` → `0.13.0`)
|
||
|
||
⚠ **Канон я НЕ трогаю** — это зона оркестратора. Здесь лежит ТЕКСТ дельты, чтобы её не выводили заново.
|
||
|
||
**Что становится ложным:** `docs/architecture/14-api-contract/openapi.yaml`, описание `201` у
|
||
`createBook` — фраза «**The `201` carries `parsing`, not `uploading`**». После синхронного разбора
|
||
`201` несёт `parsing` только на одном из четырёх исходов.
|
||
|
||
⛔ **ПРЕЖНЯЯ РЕДАКЦИЯ ЭТОЙ ДЕЛЬТЫ ОТОЗВАНА (строка 285), и вот что она говорила неверно.** Она обещала, что
|
||
`character_count_exact` и `structure` зависят от того, «удалась ли материализация читательской
|
||
поверхности», и что при удаче приезжают в том же `201`. Это было верно ровно до починки четвёртого
|
||
круга: интейк БОЛЬШЕ НЕ МАТЕРИАЛИЗУЕТ читательскую поверхность (она стоит два движковых прогона и
|
||
держала бы запрос загрузившего), поэтому оба поля в `201` отсутствуют **ВСЕГДА** и приезжают
|
||
следующей ревизией библиотеки. ⚠ Сказано вслух, а не заменено молча: при конфликте редакций
|
||
действует эта.
|
||
|
||
**Что несёт ответ `POST /v0/books` в каждом исходе:**
|
||
|
||
| исход | ответ | тело |
|
||
|---|---|---|
|
||
| движок нарезал **≥ 2 глав** | `201`, заголовок `Location` | `Book.status = "not_started"` · `chapter_count` = число глав движка · `character_count` = счёт интейка с **`character_count_exact: false`** и **`structure: null`** — ВСЕГДА. Оба поля пишет `SaveStructure` вместе с читательской поверхностью, а интейк её не материализует (уплотняет запрос на два движковых прогона): они приезжают позже, проходом материализатора, и клиент видит их следующей ревизией библиотеки. Правило `null` не меняется |
|
||
| движок прочёл и нарезал **0 глав** | `400` `invalid_request` | `errors: [{ pointer "/file", code "no_book" }]`; книга НЕ принята — ни строки в библиотеке, ни каталога на диске |
|
||
| движок прочёл и нарезал **1 главу** | `400` `invalid_request` | `errors: [{ pointer "/file", code "no_chapter_structure" }]`; тоже не принято ничего |
|
||
| **деплойный класс или таймаут** | `201`, заголовок `Location` | `Book.status = "parsing"` — прежнее поведение маршрута целиком; очередь и страховочный свип доделывают |
|
||
|
||
**Деплойные классы перечнем** (ни один не отказывает пользователю): `not_configured` — у книги нет
|
||
конфигурации движка, или шаблон деплоя не читается · `storage_unavailable` — корень хранилища книг не
|
||
смонтирован · `schema_mismatch` — проектная БД книги не той схемы, что бинарь · `parser_unavailable` —
|
||
движок не удалось ЗАПУСТИТЬ, либо он ответил классом, которого эта сборка не знает, либо обычным
|
||
выходом 1 · плюс превышение бюджета синхронного разбора (`books.CutBudget`).
|
||
|
||
**Новых значений `ErrorCode` НЕ заводится.** Оба отказа — существующий `invalid_request`; `no_book` и
|
||
`no_chapter_structure` живут в `errors[].code`, который сама спека объявляет НЕ закрытым («Not closed,
|
||
like `cause.code`»). ⚠ `no_chapter_structure` — ВРЕМЕННЫЙ: он снимается, когда построена структура глав
|
||
для выдачи (строка бэклога **283**), и в коде ветки стоит этот номер, чтобы её нашли и убрали.
|
||
|
||
### Батарея, мутации и деньги
|
||
|
||
- **`make check` MAKE-EXIT=0**, 20 пакетов `ok`, красных 0, скипов **8**. Невыполненное условие хоста
|
||
названо: `TM_PLATFORM_TEST_ENGINE_BIN` + `TM_PLATFORM_TEST_BOOK_TEMPLATE` (рецепт требует $0-пайплайн
|
||
РЯДОМ с `backend/prompts/`, то есть записи в чужую зону или полной копии дерева).
|
||
- **Тесты: 853 → 875 (+22).** Тестов, существовавших ДО пака, не удалено ни одного
|
||
(`git diff -- '*_test.go' | grep -c '^-func Test'` → 0). ⚠ Один тест, добавленный ЭТОЙ ЖЕ сменой,
|
||
удалён третьим кругом как тавтологичный (`TestTheUploadSettleBudgetCoversTheSynchronousCut`:
|
||
`UploadSettle` определён через `CutBudget`, поэтому утверждение выполнялось всегда) — в диффе против
|
||
`943617a` это не видно, и потому названо здесь. ⚠ Число снималось ПЯТЬ раз и первые четыре были
|
||
неверны: «866 → 863» (глоб захватил не-тестовые файлы), «853 → 863», «853 → 864», «853 → 866» и
|
||
«853 → 869» — каждое снято до пинов, добавленных следующим кругом самопроверки. В отчёте последнее,
|
||
после последней правки. ⚠ Само по себе это и есть измеренная цена преждевременного объявления
|
||
сходимости: шесть замеров одного числа, потому что кругов оказалось не два, а пять.
|
||
- **Мутации: посажено 18, поймано 14 сразу, выжило 3, одна поимка отозвана** (её тест удалён третьим кругом, см. таблицу). Две выживших — дыры в МОИХ пинах,
|
||
обе починены и пере-посажены (после починки красные); третья выживает СОЗНАТЕЛЬНО и названа.
|
||
⚠ Шестнадцатая посадка ОТБРОШЕНА мной как негодная: первая версия мутации охранника `claim`'а
|
||
краснила тест ошибкой ТИПИЗАЦИИ Postgres (`could not determine data type of parameter $2`), то есть
|
||
давала правый вердикт по неправой причине; пере-посажена корректно типизированной, и в таблице стоит
|
||
вторая.
|
||
|
||
| посадка | текст падения (или почему выжила) | хеш восстановлен |
|
||
|---|---|---|
|
||
| `report-failures` без строки сводки пакетов | `does not carry "textmachine/platform/internal/money"` | да |
|
||
| `report-failures` возвращён к грепу `--- FAIL` (исходный дефект 305) | то же, на всех трёх фикстурах | да |
|
||
| `report-failures` без счёта улик | `does not carry "Evidence, 1 lines"` | да |
|
||
| `AbandonRun` без сравнителя ПОПЫТКИ | ⚠ **ВЫЖИЛА**: случай ловился сравнителем ЗАЯВОК — правый вердикт по неправой причине. Пин починен (новой попытке даётся тот же счёт заявок + сверка ТЕКСТА отказа), пере-посажена → `answered <nil>, want ErrProofOvertaken` | да |
|
||
| `AbandonRun` без сравнителя ЗАЯВОК | `answered <nil>, want ErrProofOvertaken: the unit name reads exactly as the proof saw it` | да |
|
||
| `RecordSpawn` без инкремента свидетеля | `the claim counter went 0 → 0: a witness that does not move cannot catch the race` | да |
|
||
| свип не пишет парковку | `the parked attempt carries no ParkedAt` | да |
|
||
| свип пишет парковку каждый проход (снят порог) | ⚠ **ВЫЖИВАЕТ ПО ПОСТРОЕНИЮ**: охранник записи всё равно не двигает метку. Порог покупает СТОИМОСТЬ, а не свойство; названо в комментарии теста | да |
|
||
| `MarkParked` без охранника `is null` | ⚠ **ВЫЖИЛА**: два охранника прикрывали друг друга. Добавлено обращение к записи НАПРЯМУЮ, пере-посажена → `a second mark moved the stamp from … to …` | да |
|
||
| снятие метки сделано неисполнимым | `the attempt is materializing again and the row still says parked since …` | да |
|
||
| парковка посчитана карантинным гейджем | `parked=0 quarantined=1, want the park counted once and in its own series` | да |
|
||
| разрез выпал из `UploadSettle` (состояние ДО находки) | ⚠ **поимка НЕВОСПРОИЗВОДИМА:** ловивший её тест удалён третьим кругом как тавтологичный, и в «поймано» она больше не считается | да |
|
||
| знак `≥` снят с колонки SPENT | `SPENT prints an exact amount; the engine's meter is a lower bound` | да |
|
||
| ячейка PARKED слита с QUARANTINE | `a parked attempt shows no elapsed time in PARKED, so «how long has it been quiet» has no answer` | да |
|
||
| охранник claim'а у `DeleteRefusedIntake` всегда истинен | `a delete under somebody else's claim gave <nil>, want ErrNoBook` | да |
|
||
| пере-чтение снова на контексте, открытом ДО тела | `after a cut that outlived the write budget the response says "parsing" with 0 chapters, want the parsed row` | да |
|
||
| ветвь «манифест не читается» снова тратит попытку | `the intake's pass spent 1 attempts of a budget that bounds how often a broken HOST is asked` + `the claim is still held (…)` | да |
|
||
| коды двух отказов на HTTP поменяны местами | `item code no_chapter_structure, want "no_book": the two refusals are different answers to the user` | да |
|
||
- **Деньги двумя независимыми путями** на настоящих строках: сырой SQL по `credit_ledger` (7 строк,
|
||
сумма 4919187 micro) и чтение платформы `ReadAccount` (Balance=LedgerSum=$4.919187) — сходятся.
|
||
Расхождение до/после починки на тех же строках: было `note=""`, стало `at least: …` и, для
|
||
оборванной попытки, `…cut off mid-work… (PD-441)`.
|
||
|
||
### ⛔ СРАБОТАЛО ПРАВИЛО ОСТАНОВКИ — синхронный разрез НЕ ГОТОВ и должен уехать отдельным паком
|
||
|
||
Пятый круг дал major **внутри бюджета хвоста загрузки**, а оркестратор №23 отнёс этот бюджет к пути
|
||
синхронного разреза именно на этот случай. Правило исполнено: путь больше не чинится, находки ниже
|
||
записаны для следующего пака и НЕ закрыты.
|
||
|
||
**Чем правило сработало (замер, а не оценка).** `UploadSettle` не покрывает хвост ТРЕТИЙ раз подряд, и
|
||
каждый раз по новой причине. Сегодняшняя: перечень шагов в комментарии константы называет `FinishParse`
|
||
и `ReleaseParseClaim` АЛЬТЕРНАТИВАМИ («whichever end the cut reaches»), а код их СКЛЕИВАЕТ — ошибка
|
||
`FinishParse` уходит наверх (`internal/books/parse.go`, греп `owed, err := s.Store.FinishParse`), и
|
||
`cutNow` на любой не-`ErrBadIntake` ошибке зовёт `ReleaseParseClaim` на СВЕЖЕМ `writeCtx`. Худший путь:
|
||
`StartParsing 30 + разрез 90 + FinishParse 30 + Release 30 + ReadBook 30 + квитанция 10 = 220 с` при
|
||
`UploadSettle = 210 с`.
|
||
|
||
**Измерено, а не оценено (двоичный поиск по бутовому гейту через `Load()`):** гейт принимает дедлайн
|
||
до **26m29s**, ложь начинается с **26m21s** — окно шириной восемь секунд, достижимое только ручной
|
||
настройкой почти вплотную к потолку самого гейта; на дефолте **10m0s** запас **16m20s**. Наблюдаемое
|
||
следствие — **дубль книги, а не потеря**: претензия на ключ идемпотентности, пережившая `ClaimStale`,
|
||
перехватывается повтором с новым токеном, и человек, повторивший «висящую» загрузку, получает вторую
|
||
книгу вместо реплея первой. ⚠ Достижимость худшего пути без искусственного замедления **не измерена**:
|
||
она требует конъюнкции «большая книга» и «три полных `writeBudget` подряд», а гейт живого движка на
|
||
этом хосте не закрыт — подставное замедление на вопрос «достижимо ли БЕЗ него» не отвечает. Ряд —
|
||
`PD-464`. ⚠ Батарея этого не видит по построению: пин на сумму был
|
||
тавтологичным и снят третьим кругом, а мутация `4*writeBudget → 3*` оставляет и `internal/config`, и
|
||
`internal/books` зелёными.
|
||
|
||
**Почему это остановка, а не ещё одна починка.** Из одиннадцати major, найденных кругами 3–5, девять
|
||
лежат в одном месте — синхронном разрезе на приёме. Это новый механизм в конкурентном платном пути
|
||
(claim · очередь · транзакция · бюджет), и он ведёт себя как такой механизм: каждая починка открывает
|
||
новую площадь. Три круга подряд одна и та же константа оказывалась короче хвоста, и каждый раз по
|
||
другой причине — это свойство предмета, а не невнимательности.
|
||
|
||
**Что остаётся НЕ ЗАКРЫТЫМ в этом пути** (для пака, который его заберёт):
|
||
|
||
1. `UploadSettle` короче хвоста на 10 с (`PD-464`). ⛔ Лечение — **не поднять константу**: она
|
||
выводилась руками трижды и трижды была неверной, каждый раз по новой причине, поэтому четвёртый
|
||
вывод руками — подпорка (`D39.216`). Границу надо выводить **ИЗ кода пути**.
|
||
2. Комментарий в `cutNow` («the queue job is already enqueued») противоречит починке, ради которой
|
||
функцию правили: на этом пути задание ставит сам `ReleaseParseClaim`.
|
||
3. Отказ `ClaimParse` в `cutNow` не различает рутинную гонку (`ErrParseClaimed`) и сбой БД, и во
|
||
втором случае молчит: книга остаётся `parsing` без задания и без строки лога до свипа.
|
||
4. Параметр `enqueue` у `StartParsing` в бою мёртв (движок настроен всегда), а его doc описывает его
|
||
как штатный путь; решение «кто ставит задание» размазано по двум местам на одном предикате.
|
||
5. Охранник `RowsAffected() == 0 → задание не ставить` в `ReleaseParseClaim` не запинен ничем.
|
||
6. «ВСЕГДА» в дельте контракта — гарантия по ТАЙМИНГУ, не по построению: свип материализатора
|
||
теоретически успевает между `FinishParse` и `ReadBook`. Практически вероятность близка к нулю, но
|
||
тексту контракта полагается либо структурная гарантия, либо оговорка.
|
||
7. Свип `StuckIntake` — третий претендент на claim, в разборе гонки не назван: при
|
||
`TM_PLATFORM_UPLOAD_DEADLINE > claimGrace` он может взять claim раньше разреза.
|
||
8. Разрез потерял единственный ограничитель параллелизма: задание на приёме больше не ставится, а
|
||
`MaxWorkers: 4` был у очереди — одновременных разрезов теперь не ограничивает ничто.
|
||
9. После последнего байта тела маршрут МОЛЧИТ до 210–220 с; промежуточный прокси об этом не спрашивали.
|
||
10. Отказ уходит наружу только машинным кодом (`Detail` не заполняется) ⇒ «честная причина человеку»
|
||
из §4.3 сегодня не доезжает. Фронт заморожен, значит либо причина едет в `Detail`, либо пункт
|
||
признаётся неисполненным.
|
||
|
||
### Замер, который должен пережить пак: очередь выигрывала гонку у разреза 5 раз из 6
|
||
|
||
Синхронный разрез берёт `ClaimParse` ПОСЛЕ коммита `StartParsing`, а `StartParsing` ставил задание
|
||
очереди ВНУТРИ той же транзакции — то есть воркер становился видимым тем же коммитом и гонялся с
|
||
разрезом за один и тот же claim. Проба (энкьюер зовёт `Parse` сразу после коммита, как это делает
|
||
River на видимости строки), шесть прогонов: **очередь выиграла 5 раз, разрез 1 раз**.
|
||
|
||
Оба исхода стоят книге, и это делает гонку дефектом, а не шероховатостью:
|
||
- **выигрывает очередь** — синхронного вердикта нет вовсе, пустой файл принимается `parsing`,
|
||
попытка бюджета потрачена, отказ приезжает асинхронно и оставляет книгу в библиотеке;
|
||
- **выигрывает разрез** — задание съедено впустую (`ErrParseClaimed` → `nil`), и после возврата
|
||
claim'а книгу подбирает только страховочный свип, то есть через `claimGrace` = 20 минут.
|
||
|
||
⇒ задание ставится ТОЛЬКО там, где синхронного разреза не будет, а на пути «вердикта нет» — вместе с
|
||
возвратом claim'а одной транзакцией. Цена названа: процесс, умерший между строкой и разрезом,
|
||
оставляет книгу без задания, и её берёт свип через `claimGrace`, а не сразу.
|
||
|
||
### Команды и их вывод — числа этого отчёта
|
||
|
||
Сняты ПОСЛЕ последней правки кода; доковые правки после них Go-батарею не касаются.
|
||
|
||
```
|
||
$ TM_PLATFORM_TEST_DSN=… TM_PLATFORM_TEST_PGDUMP=… TM_PLATFORM_TEST_PGRESTORE=… make check
|
||
20 строк `ok`, ни одной `FAIL`, MAKE-EXIT=0
|
||
--- did NOT run: 8 skipped … UNSET: TM_PLATFORM_TEST_BOOK_TEMPLATE, TM_PLATFORM_TEST_ENGINE_BIN
|
||
|
||
$ git grep -h '^func Test' 943617a -- 'platform/**/*_test.go' | wc -l → 853
|
||
$ find platform -name '*_test.go' -exec grep -h '^func Test' {} + | wc -l → 875
|
||
$ git diff -- '*_test.go' | grep -c '^-func Test' → 0
|
||
$ git diff -- '*_test.go' | grep -c '^[-+]func Fuzz' → 0
|
||
|
||
$ python3 docs/scripts/carriers.py platform/docs/platform-PROGRESS.md
|
||
carriers: 0 помеченных утверждений без носителя · осмотрено строк: 491
|
||
|
||
$ grep -c '^| PD-' platform/docs/DEFECT_REGISTER.md → 465
|
||
$ python3 docs/scripts/counts.py --check
|
||
✗ docs/PROGRESS.md: «открытых 106» против пере-счёта 112 · «всего 458» против 465 (зона docs/)
|
||
|
||
$ grep -rc 'THE FIX IS NOT IN THIS PACKAGE' --include=*.go platform/ | grep -v ':0' | wc -l → 0
|
||
контроль: .go файлов осмотрено 197 · 'character_count_exact' найдено в 4 (v0.go, project.go, v0_test.go, books.go)
|
||
|
||
$ git status --short -- docs/architecture/14-api-contract/ platform/docs/ENGINEERING_STANDARDS.md \
|
||
platform/docs/PLATFORM_DIRECTION.md → пусто
|
||
$ git status --short -- backend/ | wc -l → 17
|
||
⚠ и это НЕ мой след: 17 файлов правит параллельная бэкенд-сессия. Что этот пак в `backend/` не
|
||
писал, из дерева НЕ измеримо — измеримо лишь то, что один файл я туда положила по инерции и
|
||
убрала (`backend/configs/` чист). Прежняя редакция подавала это как замер; это не замер.
|
||
$ git diff --cached --name-only | wc -l → 0
|
||
```
|
||
|
||
### Вынос журнала — что именно потеряно, пере-снято после всех правок
|
||
|
||
`comm` по непустым строкам: было **4156**, стало **4420** (три файла плюс баннеры срезов), не сошлось
|
||
**10** строк — и все десять названы, потому что «одна» в прежней редакции этого абзаца была верна лишь
|
||
на момент замера и устарела от моей же последующей правки:
|
||
|
||
- заголовок «Пинги оркестратора (живые ссылки для следующих сессий)» → «…ИСПОЛНЕНЫ» (все пять пингов
|
||
под ним отработаны);
|
||
- девять строк трёх ХВОСТОВЫХ секций-указателей («Эра пака P7 — в архиве», «Закрытые эры P0–P3 — в
|
||
архиве» и дублирующий их буллет) — они дублировали блок «Архив эр» и слиты в него; обе ссылки
|
||
(`-P7.md`, `-P0-P3.md`) в живом файле сохранены и резолвятся.
|
||
|
||
⚠ То есть потеряны ФОРМУЛИРОВКИ дублей, не содержание, и это утверждение проверяемо: все шесть ссылок
|
||
на срезы в живом файле разрешаются в существующие файлы. Ссылки ИЗ других файлов в вынесенные
|
||
диапазоны пере-нацелены: `sqlc.yaml`, `STACK_DECISIONS.md` (условия стенда — там же сказано, что архив
|
||
читается как археология, а не как инструкция), `README.md` (шапка и таблица).
|
||
|
||
### Находка собственного круга: хвост загрузки, о котором бутовый гейт не знал
|
||
|
||
Синхронный разрез добавил до полутора минут к тому, что загрузка делает ПОСЛЕ тела, а бутовый гейт
|
||
(`internal/config`: `UploadDeadline + books.UploadSettle < min(UploadGrace, ClaimStale)`) про этот
|
||
участок не знал — то есть оператор мог настроить дедлайн, при котором загрузка переживает окно
|
||
идемпотентного ключа. Вылечено переносом бюджета в саму константу: `UploadSettle = CutBudget +
|
||
writeBudget + 30s`. Существующий бутовый тест читает `UploadSettle`, а не литерал, поэтому подъём
|
||
`CutBudget` теперь автоматически ужимает допустимый дедлайн.
|
||
|
||
**Цена того, что `CutBudget` стал КОНСТАНТОЙ, а не настройкой — явно.** Деградация мягкая: бюджет
|
||
работает ПОРОГОМ, а не потолком — на хосте, где разрез книги законно дольше полутора минут, загрузка
|
||
не падает, она возвращается на прежний асинхронный путь (`201 parsing`, очередь доделывает), и
|
||
единственная потеря — менее информативный `201`. Ручка убрана не ради чистоты: за ней стоял способ
|
||
выстрелить себе в ногу — настройка, которой можно вытолкнуть хвост загрузки за окно, в котором её
|
||
ключ ещё можно переиграть, а окно это ни один экран не показывает.
|
||
|
||
### Дофикс по приёмке оркестратора №23 — шесть пунктов, все закрыты
|
||
|
||
| пункт | что было | что сделано | чем предъявлено |
|
||
|---|---|---|---|
|
||
| **Д1** | ⛔ **базис расчёта ВЫВЕРНУТ на самом частом окончании:** `finish()` считает исход в локальную переменную, а `settle()` получает ТОТ ЖЕ до-финишный снапшот ⇒ у прогона, кончившегося чисто, в леджер уезжал `halted` | снапшот несёт исход, который этот же вызов только что решил (`l.Status, l.PausedReason = status, pausedReason`) | **ИСПОЛНЕНИЕМ, не по коду:** прогон доведён до `ready`, строка леджера прочитана SQL-ом. До починки: `status="ready"`, а `note` — «cut off mid-work». Пин `TestACleanEndingIsSettledOnTheBasisOfTheEndingItReached`, мутация возвращает дефект и красит его текстом |
|
||
| **Д2** | гейдж `PARKED` не запинен на уровне ЭКСПОЗИЦИИ, и проводка `Observe → metrics.Runner` в демоне не запинена вовсе | ассерт на серию в экспозиции + гейт, читающий композитный литерал демона и требующий, чтобы каждое поле бралось из поля СВОЕГО имени | две мутации: подмена серии → `the exposition is missing "tm_platform_parked_attempts 7"`; обмен полей в демоне → `one state's number is published under another's name` |
|
||
| **Д3** | гейт 305 пинил ТАРГЕТ, а не его использование: откат ветви отказа к голому грепу оставлял батарею зелёной | гейт держит ветвь отказа рецепта `check`: она обязана звать `report-failures` и НЕ грепать `--- FAIL` сама | мутация — буквальный откат строки — красит обе проверки |
|
||
| **Д4** | рантбук утверждал, что у парковки нет ни колонки, ни гейджа, ни строки; после пака это ложь | рантбук переписан: у парковки СВОИ три сигнала, карантинных нет и не будет, потому что снимать её не надо. Плюс `≥` у SPENT и пятый отказ `run abandon` | `deploy/README.md`, греп `tm_platform_parked_attempts` и `ПЯТЫЙ ОТКАЗ` |
|
||
| **Д5** | комментарий `PD-424` объявлял исчерпывающие «two ways», а способов ТРИ | комментарий перестал объявлять полноту и называет третий способ с его радиусом; сам способ — ряд `PD-465` | заявка коммитится в `RecordSpawn`, юнит поднимается строкой ниже (`spawn.go`, `Runner.Start`) |
|
||
| **Д6** | 18 протухших якорей в регистре, сдвинутых этим паком | пере-нацелены ПО ТОКЕНУ, а не по памяти; девятнадцатый — мой собственный, я записала токен с многоточием, которого в коде нет | `counts.py --lint`: было 25 проблемных якорей в 120 доках, стало **5**, в регистре **0** |
|
||
| **Д7** | не заказан приёмкой, найден её же батареей: новый ряд `PD-465` несёт два маркера тревоги и не был объявлен в `alarmBaseline` | объявлен, с доводом почему он НИЖЕ major (окно в миллисекунды, расход ограничен потолком того же прогона, аккаунтом не эксплуатируем) | гейт `TestOpenRowsBelowMajorThatCarryAlarmMarkers…` красил батарею именно на нём; ⭐ и назвал его прибор **305** — «`check` упал» отличилось от «тест упал» на настоящем падении |
|
||
|
||
⚠ **Один якорь остался и он НЕ мой по зоне:** `docs/architecture/05-decisions-log.md:2678` целит в
|
||
`platform/internal/pgstore/books.go:314` по токену `FinishParse records a parsed book`; мой пак сдвинул
|
||
цель на **343**. Правка в зоне оркестратора — число передано пингом.
|
||
|
||
⚠ **Что приёмка нашла и что чинить НЕ надо** (правило остановки в силе, дописано к семи пунктам
|
||
остановленного разреза): разрез потерял единственный ограничитель параллелизма — задание на приёме
|
||
больше не ставится, а `MaxWorkers: 4` был у очереди · маршрут молчит до 210–220 с после последнего
|
||
байта, промежуточный прокси об этом не спрашивали · отказ уходит наружу только МАШИННЫМ кодом, `Detail`
|
||
не заполняется, то есть «честная причина человеку» из §4.3 сегодня не доезжает.
|
||
|
||
### Пять кругов самопроверки — что дал каждый
|
||
|
||
| круг | кто | major | итог |
|
||
|---|---|---|---|
|
||
| 1 | я, по готовой работе | — | две дыры в МОИХ пинах (посадки выживали) + хвост загрузки, о котором бутовый гейт не знал |
|
||
| 2 | я, против заказа | — | «≥» и парковка были предъявлены чтением кода, а не исполнением; охранники `DeleteRefusedIntake` не запинены; дельта контракта неверна. ⚠ **Сходимость объявлена здесь ПРЕЖДЕВРЕМЕННО** |
|
||
| 3 | опровергатель `fable` | **4** | три — дефекты, введённые этим паком: мёртвый контекст пере-чтения · три ветви без возврата claim'а · синхронная материализация; плюс незапиненный маппинг на проводе |
|
||
| 4 | опровергатель `fable` | **5** | гонка задания очереди с разрезом за один claim (**очередь выигрывала 5 из 6**) · бюджет снова короче хвоста · две ветви из трёх без пина · `!atIntake` без пина · ложная дельта контракта |
|
||
| 5 | опровергатель `fable` | **1** (+6 minor) | бюджет короче хвоста ТРЕТИЙ раз, по третьей причине ⇒ **правило остановки** |
|
||
|
||
⚠ **Цена преждевременной сходимости, измеренная:** одно число (счёт тестов) снималось ШЕСТЬ раз,
|
||
потому что кругов оказалось не два, а пять; и дельта контракта, которую оркестратор собирался
|
||
ратифицировать, дважды была ложной — первый раз по существу, второй раз после моей же починки.
|
||
|
||
⭐ **Что из этого стоит унести дальше:** восемь major из одиннадцати нашёл ЧУЖОЙ прибор, а не я, и
|
||
все восемь — в коде, который я только что написала и считала проверенным. Оба круга, которые я вела
|
||
сама, дали настоящие находки, но НИ ОДНОГО major в новом механизме: свой код я проверяла по тому,
|
||
что он должен делать, а опровергатель — по тому, что он делает.
|
||
|
||
### Что НЕ удалось и что считаю слабым местом
|
||
|
||
- **Не измерено:** маржа `PD-441` в деньгах — приборa нет на этой стороне (см. движковую половину).
|
||
- **Не предъявлено живым прогоном:** синхронный разбор гонялся на фикстурах и на живом Postgres, но
|
||
не на настоящем `tmctl` — гейт `TM_PLATFORM_TEST_ENGINE_BIN` требует записи в `backend/`.
|
||
- **Слабое место, которое называю сам:** `settlementBasis` судит по СТАТУСУ попытки, а не по факту
|
||
наличия вызовов в полёте. Классы огрублены сознательно (пере-пометка стоит менее точного сигнала,
|
||
недо-пометка прячет деньги), но `awaiting_bank` отнесён к «чистым» по чтению кода движка, а не по
|
||
замеру: если окажется, что банк-стоп тоже рвёт волну, класс придётся сузить.
|
||
- **Третье, названное опровергателем и оставленное сознательно:** если удаление отказанной книги
|
||
само упрётся в БД (`discard` → `removeIntake`), пользователь получит `400`, а строка останется
|
||
`parsing` под claim'ом; свип через `claimGrace` перечитает манифест, потратит бюджет попыток и
|
||
оставит книгу `rejected` в библиотеке — то есть ровно то состояние, которого отказ и избегает.
|
||
Не лечу: путь требует отказа БД РОВНО между двумя её же операциями в одном запросе, а лечение —
|
||
компенсирующая транзакция, то есть механизм заметно крупнее устраняемого класса. Названо, чтобы
|
||
следующий пак не открывал его заново.
|
||
- **Второе слабое место:** отказ книге без глав — продуктовое сужение. Оно временное и помечено
|
||
строкой 283 в коде ветки, но пока владелец его не подтвердил, это решение сессии.
|
||
|
||
**Работа завершена, править больше не планирую.** Дерево передано оркестратору №23 незакоммиченным:
|
||
37 файлов, все внутри `platform/`, индекс пуст. Синхронный разрез построен и ОСТАНОВЛЕН правилом
|
||
остановки — его семь незакрытых пунктов перечислены выше поимённо и уезжают отдельным паком; всё
|
||
остальное закрыто и предъявлено.
|
||
|
||
## Живые нормы зоны, у которых нет другого носителя
|
||
|
||
Каждая прошла триаж 06.09: она не выводится из кода и не стоит ни в `ENGINEERING_STANDARDS.md`, ни в
|
||
`STACK_DECISIONS.md`. ⚠ Оркестратору предложено абсорбировать их в нормы зоны — до тех пор живут здесь.
|
||
|
||
- **Ничего не отдавать на лендинг без прогона ПАКЕТА, которого правка касалась.** «Код написан и пин
|
||
заведён» — не то же, что «прогнано»; числа снимаются после ПОСЛЕДНЕЙ правки, включая комментарные
|
||
(генезис — `D39.211` п.6, где это зафиксировано как ошибка оркестратора, а не как норма зоны).
|
||
- **`go test` без `-v` строк `--- SKIP` не печатает вовсе**, поэтому греп по ним в не-verbose логе даёт
|
||
ЛОЖНЫЙ НОЛЬ скипов (`D39.213` п.3).
|
||
- **Зелёная батарея — это полный список ПАКЕТОВ плюс отсутствие `FAIL`**, а не отсутствие красных строк
|
||
в хвосте вывода (`D39.169`).
|
||
- **Гейты доков перегоняются ПОСЛЕ последней правки доков, а не кода**, и число из растущего файла —
|
||
не число (`D39.188`).
|
||
- **Шаблон книги второго гейта батареи обязан указывать на СНАПШОТ движковых конфигов того же коммита,
|
||
что и бинарь** (`git archive <commit> backend/configs backend/prompts`), а не на рабочее дерево:
|
||
иначе живой тест читает файлы параллельной бэкенд-сессии и краснеет без дефекта (`PD-432` покрывает
|
||
БИНАРЬ, не шаблон).
|
||
- **Механизм, который агрегирует чужой каталог и отдаёт результат наружу, обязан иметь СПИСОК ТОГО,
|
||
ЧТО НЕ БЕРЁТ, и список начинается с секретов.** Исключать по расширению — ловушка в обе стороны
|
||
(`D39.201`).
|
||
- **Красный тест, мерящий ВРЕМЯ, на загруженной машине — не результат:** прежде чем звать его
|
||
дефектом, гонять изолированно и смотреть `uptime` (`D39.194`).
|
||
|
||
## Вопросы и пинги оркестратору — ОТКРЫТЫ
|
||
|
||
- **Счёт рядов регистра сдвинут этим паком** и живёт в чужой зоне: `docs/PROGRESS.md` объявляет
|
||
«открытых 106 / всего 458», пере-счёт даёт **112 / 465** (`python3 docs/scripts/counts.py --check`).
|
||
Заведены `PD-459`…`PD-465`, `PD-458` переведён в `fixed`.
|
||
- ⛔ **Нужен пятый `RejectReason` в контракте (`PD-463`), либо решение, что асинхронная ветвь приёма
|
||
остаётся проницаемой.** Сужение входа до того, что умеет выдача, действует только на синхронной
|
||
ветви; на ветви, куда книга уходит при деплойном классе, «книга одним полотном» попадает в
|
||
библиотеку молча. Закрыть нечем: словарь закрыт четырьмя значениями, и ближайшее по словам
|
||
`source_unreadable` ТЕРМИНАЛЬНО и УДАЛЯЕТ исходник — то есть уничтожает файл пользователя из-за
|
||
НАШЕГО ограничения.
|
||
- **Предложение движка провести `--ceiling-usd` в `status`** (пинг №15, 09.08) не принято и не
|
||
отклонено с тех пор; `backend/internal/pipeline/status.go` отвечает «a read path never carries a
|
||
run-scoped override». Нужна диспозиция или строка бэклога.
|
||
- **`counts.py` держит регистр ОДНИМ путём и слайсы не глобит.** Это предусловие плана нарезки
|
||
`DEFECT_REGISTER.md` (`docs/DOC_CLEANUP_PLAN.md`, Б14в): вынос без него молча уронит счёт.
|
||
- **§2.12 компаньона контракта** (`docs/architecture/14-api-contract/README.md`) утверждает, что пять
|
||
денежных полей не могут доехать, тогда как аллоулист шва несёт `committed_usd`/`reserved_usd`
|
||
(`platform/internal/ingest/resync.go`); якоря на `pipeline/status.go` там же мертвы. Зона `docs/`.
|
||
- **Зеркало контракта у фронта — `0.2.3` против канона `0.12.0`** и несёт отозванное правило «денег в
|
||
интерфейсе MVP нет вообще» (`D39.196` п.2 его снял). Зона фронта заморожена; пинг на разморозку.
|
||
- **Восстановление из бэкапа не предъявлено на книге в сотни мегабайт, при заполненном диске и при
|
||
чужих соединениях** (`PD-462` покрывает соседний класс, этот — нет). Нужна строка или ряд.
|
||
- **Правило записи адресов в доках, живущее только в архиве и относящееся к прибору из `docs/`:**
|
||
адрес пишется БЕЗ якорной нотации там, где текст РАССКАЗЫВАЕТ о форме (линтер `counts.py` разбирает
|
||
форму где угодно, включая объяснение этой формы), а мёртвый адрес цитируется ПОРОЗНЬ — имя файла в
|
||
кавычках, номер словами, — иначе цитата сама становится якорем. В `counts.py` этого нет; носитель
|
||
нужен в зоне `docs/`.
|
||
- **Рантбук деплоя ни разу не прогонялся end-to-end с настоящим `migrate`** — названо предусловием
|
||
первого выката ещё пингом №17 и носителя не получило.
|
||
|
||
## Архив эр
|
||
|
||
- **Паки 04–06.09** («закрыть цикл» · «пустить внутрь можно» · «форма заказа перевода» · наблюдение за
|
||
живым платным потоком) — [archive/platform-PROGRESS-2026-09-04-06.md](archive/platform-PROGRESS-2026-09-04-06.md).
|
||
Ратификации **D39.194**, **D39.201**, **D39.208**, **D39.211**–**D39.214**.
|
||
- **Эры P9–P13 (27.08–03.09)** плюс приёмка P8-REVIEW, раздел «Состояние эры P8» и исполненные пинги
|
||
оркестраторов №15–№22 — [archive/platform-PROGRESS-P9-P13.md](archive/platform-PROGRESS-P9-P13.md).
|
||
Ратификации **D39.159**, **D39.162**, **D39.166**, **D39.169**, **D39.172**, **D39.180**, **D39.188**.
|
||
- **Эра пака P8-FIX (21–22.08) — В АРХИВЕ.** Отчёт пака, обе волны ревью, спил пер-термной подписи, инвентарь каналов и obstacle — [archive/platform-PROGRESS-P8.md](archive/platform-PROGRESS-P8.md). Ратификация — D39.154, лендинг `31f1f82`. Живое из этой эры: четыре открытые строки регистра (`PD-370` мажор — контрактная половина · `PD-371` · `PD-372` · `PD-373`/`PD-374`), инвентарь каналов шва в `STACK_DECISIONS.md`, граница sqlc в `BACKLOG.md` П-19.
|
||
|
||
- **Записи акта 5 пака P7, остававшиеся в живом журнале, — ДОСЛАНЫ в [archive/platform-PROGRESS-P7.md](archive/platform-PROGRESS-P7.md)** 22.08: заголовок «эра P7 в архиве» стоял, а тела лежали здесь.
|
||
|
||
- **Паки P4, P5, P6 и их дофиксы (08–15.08) приняты и залендены.** Ратификации: **D39.123** (P4 «раннер», `d29e30c`; формула аргумента потолка = `committed + прирост`, PD-158) · **D39.130** (P5, `69d485a`; Go-floor 1.26.6 — `toolchain`-директива в `go.mod` плюс сравнивающий гейт `make version-check`; интейк пишет `book.yaml` формой Б) · **D39.131** (эмиттер шва движка; словарь кадров — `internal/ingest/events.go`) · **D39.132** (P6 + дофикс; `PD-113` закрыт, у батареи появился второй гейт окружения). Записи паков с дофиксами, живыми пробами и посадками — [archive/platform-PROGRESS-P4-P6.md](archive/platform-PROGRESS-P4-P6.md); промт P5 — `archive/PLATFORM_P5_SESSION_PROMPT_2026-08-10.md`. ⚠ Счёт регистра и тестов тех дней устарел на порядок: живые числа берутся `python3 docs/scripts/counts.py --check` и `DEFECT_REGISTER.md`, а не отсюда.
|
||
|
||
- **Эра пака P7 (16–20.08)** — пять актов, приёмка и фикс-раунды: [archive/platform-PROGRESS-P7.md](archive/platform-PROGRESS-P7.md).
|
||
- **Эры P0–P3** (вход OIDC · кредиты · админ-CLI · деплой · фикс-паки) — исполнены и залендены
|
||
(D39.107/109/112/114): [archive/platform-PROGRESS-P0-P3.md](archive/platform-PROGRESS-P0-P3.md) (D39.124).
|
||
Решения оттуда живут в D-логе и `DEFECT_REGISTER.md`.
|