Rewrite both session prompts against the refuters' worklist: each blocker becomes a property with its boundary, and every number carries the command that prints it
This commit is contained in:
parent
801831a80e
commit
7f19ee48b1
2 changed files with 675 additions and 116 deletions
|
|
@ -1,91 +1,329 @@
|
|||
# Промт сессии: БЭКЕНД — «число согласия на пути второй покупки»
|
||||
|
||||
> **Роль:** бэкенд. **Зона записи:** `backend/`. **Не коммитишь** — дерево готовишь и передаёшь на
|
||||
> лендинг оркестратору. Чужие зоны читаешь, не правишь. Норматив стиля — `docs/architecture/12-go-style-notes.md`.
|
||||
> лендинг оркестратору. Чужие зоны (`platform/`, `frontend/`, `eval/`) читаешь, не правишь; в `docs/`
|
||||
> пишешь ТОЛЬКО в секцию «Бэкенд» журнала `docs/PROGRESS.md` — это единственное разрешённое каноном
|
||||
> исключение. Git: никаких `git add -A`/`git add .`/`git commit -a`, никаких `reset --hard`, `rebase`,
|
||||
> `checkout` поверх грязного дерева — рядом идут параллельные сессии, и rewrite стирает их работу
|
||||
> безвозвратно. Норматив стиля — `docs/architecture/12-go-style-notes.md`.
|
||||
|
||||
## Какая проблема и что изменит твой результат
|
||||
|
||||
Продукт продаёт главы **частями**. Покупатель берёт кусок, потом ещё кусок — и на этом пути число,
|
||||
которым движок спрашивает согласия на оплату, **врёт в трёх местах**, а один юнит может быть оплачен
|
||||
дважды. После пака покупатель, берущий книгу частями, видит число, которое не занижает при смене
|
||||
модели стадии, признаётся, какая его часть — старые деньги, и не платит второй раз за юнит, который
|
||||
уже двигал.
|
||||
которым движок спрашивает согласия на оплату, **врёт** (занижает при смене модели стадии и не
|
||||
раскрывает, какая его часть — старые деньги), а один юнит может быть оплачен дважды. После пака
|
||||
покупатель, берущий книгу частями, видит число, которое не занижает молча, признаётся, какая его
|
||||
часть — старые деньги, и не платит второй раз за юнит, который уже двигал; а три гарантии, которые
|
||||
сегодня держатся только на честном слове, держатся тестом и записью в мутационном каталоге.
|
||||
|
||||
## Карта чтения — пять позиций, дальше только по их ссылкам
|
||||
|
||||
1. Шапка-таблица `docs/architecture/09-target-architecture.md` — что построено.
|
||||
2. `backend/README.md` · `docs/architecture/03-implementation-notes.md` (через баннер).
|
||||
3. `docs/architecture/15-money-path.md` — денежный путь целиком, включая §2 «согласие на пере-оплату».
|
||||
4. Строка **197** единого бэклога (`docs/PROGRESS.md`) — фикс-лист ФЧ-1…ФЧ-8, и строка **232**.
|
||||
5. Нота **D39.150** — тело ратификации ФЧ-5 (греп `^## D39.150`).
|
||||
3. `docs/architecture/15-money-path.md` — денежный путь целиком, включая §2 «Деньги ДВИЖКА».
|
||||
4. Строки **232** и **197** единого бэклога (`docs/PROGRESS.md`).
|
||||
⚠ **Строка 197 — УКАЗАТЕЛЬ, а не фикс-лист:** её собственный текст отсылает тело списка в
|
||||
`docs/archive/PROGRESS-2026-08-17.md`, запись приёмки №18 (проверь: `grep -n '^| 197 ' docs/PROGRESS.md`).
|
||||
Определения ФЧ-1 и ФЧ-4, которые ты исполняешь, продублированы ниже В ТЕЛЕ ЭТОГО ПРОМТА — читать
|
||||
архив ради них НЕ надо. Если всё же захочешь дословный оригинал, законный вход ровно один и он
|
||||
назван здесь: `sed -n '68p' docs/archive/PROGRESS-2026-08-17.md` (`grep -n 'ФЧ-1 (HIGH' docs/archive/PROGRESS-2026-08-17.md` → `68`).
|
||||
Инструкции из архива НЕ исполняются — он источник ФОРМУЛИРОВКИ, приказ несёт этот промт.
|
||||
5. Журнал решений, два номера грепом: **D39.150** (`grep -n '^## D39.150' docs/architecture/05-decisions-log.md` → `535`)
|
||||
и **D39.181** (`grep -n '^## D39.181' docs/architecture/05-decisions-log.md` → `1712`).
|
||||
|
||||
## Состав — четыре пункта, в этом порядке
|
||||
|
||||
**1. ФЧ-5 — ось МОДЕЛИ в числе согласия. Ратифицирован 17.08 (D39.150 п.1) и НЕ построен.**
|
||||
Ратифицировано дословно: прайсить по модели, которую стадия резолвит **сейчас**; при незнании
|
||||
округлять **вверх**; оговорка в тексте согласия. Нота помечает это обязательным **до первого
|
||||
пере-прогона со сменой модели стадии**. Сегодня первый же такой пере-прогон занижает число согласия
|
||||
молча — ровно тот вред, ради которого чинилась строка 181.
|
||||
⚠ **Проверь исходное состояние САМ** и не бери с моих слов: оркестратор утверждал, что «шапка
|
||||
`rebill.go` заявляет обратное ратифицированному», и **это не подтвердилось** — в шапке речь про
|
||||
`$0 model`. Формулируй по коду, который увидишь.
|
||||
### 1. ФЧ-5 — ось МОДЕЛИ в числе согласия. Ратифицирован 17.08 (D39.150 п.1) и НЕ построен
|
||||
|
||||
**«Делай РОВНО так» — состав ратификации. D39.150 п.1 несёт ЧЕТЫРЕ обязательства, не три:**
|
||||
|
||||
1. **прайсить по модели, которую стадия резолвит СЕЙЧАС**, а не по той, что ответила тогда;
|
||||
2. **при невозможности знать — округлять ВВЕРХ**;
|
||||
3. **называть оговорку в тексте согласия**;
|
||||
4. **обещание ТОЧНОСТИ снимается с провода.** Дословно из ноты: «Следствие: обещание точности
|
||||
снимается с провода — комментарий „`status` не может назвать сумму, отличную от `translate`“ …
|
||||
при исполнении ФЧ-5 **обязан быть заменён** честной формулой „оценка, ошибается вверх, вот чем“».
|
||||
Контракт механизма после пака — **не точность, а НАПРАВЛЕНИЕ ошибки: «никогда не занизить молча»**;
|
||||
завышение безопасно (оператор согласился на большее, чем заплатит), занижение — тот вред, ради
|
||||
которого чинилась строка 181.
|
||||
|
||||
⚠ **Пункт 4 — это правка ТЕКСТА в двух живых местах, и оба надо снять, иначе ФЧ-5 придётся
|
||||
переоткрывать** (класс «текст обещает больше числа» — ровно тот, ради которого заведён фикс-лист):
|
||||
|
||||
- `backend/internal/pipeline/status.go:217` — «…so status can never quote a different number than the
|
||||
one translate enforces» (`grep -n 'can never quote a different number' backend/internal/pipeline/status.go` → `217`);
|
||||
- `backend/internal/pipeline/reprice.go:24` — шапка файла: «NOT projected (like the content axis in
|
||||
rebill.go): **ROUTING**. A row is priced by the model that ANSWERED, not by the one its stage resolves
|
||||
to now» (`grep -n 'NOT projected (like the content axis' backend/internal/pipeline/reprice.go` → `24`).
|
||||
Это заявление становится ЛОЖНЫМ ровно в тот момент, когда ты исполнишь пункт 1.
|
||||
|
||||
Нота помечает ФЧ-5 обязательным **до первого пере-прогона со сменой модели стадии** (D39.150 п.2).
|
||||
Замер вреда из приёмки: **`$0.003640` против честных `$0.036400`** при `HistoricalRows=0`
|
||||
(`grep -on '\$0.003640 против честных \$0.036400' docs/archive/PROGRESS-2026-08-17.md` → `68:...`).
|
||||
|
||||
⚠ **Проверь исходное состояние САМ и не бери с моих слов.** Оркестратор однажды утверждал, что «шапка
|
||||
`rebill.go` заявляет обратное ратифицированному», и **это не подтвердилось** — там речь про `$0`-строку.
|
||||
Противоречащая шапка есть, но в `reprice.go` (см. выше). Формулируй по коду, который увидишь.
|
||||
|
||||
**«Решаешь сам и аргументируешь»:** форма хранения и проводки «модели, которую стадия резолвит сейчас»
|
||||
в числе согласия; конкретная формула округления вверх при незнании; точные слова оговорки и обеих
|
||||
заменяемых формул.
|
||||
|
||||
### 2. Строка 232, ВТОРАЯ половина — слот объёмного гранта
|
||||
|
||||
Первая половина (читающий метод `store.AnnouncedOnceKeys()`) исполнена 31.08. Живой остаток по строке:
|
||||
**юнит, прерванный МЕЖДУ волнами (signature stop, денежный потолок, Ctrl-C), тратит слот объёмного
|
||||
гранта повторно** — покупатель платит за N глав и получает меньше. Замер строки — **4 купленных юнита
|
||||
→ 2 главы** (`grep -on '4 купленных юнита → 2 главы' docs/PROGRESS.md` → `175:...`). Разбор —
|
||||
`backend/docs/MONEY_HONESTY_REPORT.md` §4.5 (`grep -n '^### 4.5' backend/docs/MONEY_HONESTY_REPORT.md` → `206`).
|
||||
|
||||
⚠ **Остатков в коде названо ДВА, а не один — и это НЕ одно и то же.** Докстринг, приземлённый тем же
|
||||
коммитом, перечисляет их под заголовком «⚠ TWO RESIDUALS, named rather than implied»
|
||||
(`grep -n 'TWO RESIDUALS' backend/internal/pipeline/volume.go` → `523`):
|
||||
|
||||
- **(1) конвейер, получающий ПЕРВУЮ редакторскую стадию**, двигает отгружающую волну с draft на edit,
|
||||
и уже прочитанные юниты репортятся свежими: «closing it needs a record of what the shipping wave WAS,
|
||||
which nothing stores today». **ВНЕ СКОУПА этого пака** — закрытие требует носителя того, чем была
|
||||
отгружающая волна, а пак схему хранилища не трогает (см. ПОРЯДОК ДЕПЛОЯ). ⚠ Строка 232 считает свою
|
||||
первую половину ИСПОЛНЕННОЙ, а §4.5 отчёта называет только слот — то есть остаток (1) живёт СЕГОДНЯ
|
||||
только в этом докстринге. **Назови его в отчёте, чтобы он не умер молча вместе с паком.**
|
||||
- **(2) слот гранта** — В СКОУПЕ, это и есть твой пункт 2.
|
||||
|
||||
**Закрытие 232 объявляется ТОЛЬКО по остатку (2).** Написать «строка 232 закрыта» без этой оговорки —
|
||||
ошибка отчёта.
|
||||
|
||||
⚠ **«Делай РОВНО так» — ратифицированный ответ на развилку, которую строка 232 требует назвать в
|
||||
промте.** Ключ анонса несёт ВОЛНУ (`grep -n 'unitOnceKey is the identity' backend/internal/pipeline/events.go`
|
||||
→ `396`), значит «юнит отгружен» — факт per-wave, и надо решить, ПО КАКОЙ волне судить. Ответ есть и
|
||||
он не новый механизм: **отгрузкой считается волна, владеющая ОТГРУЖАЮЩЕЙ (последней) стадией —
|
||||
`finalStageWave`** (`grep -n 'finalStageWave is the wave that owns the SHIPPING' backend/internal/pipeline/snapshot.go`
|
||||
→ `243`; сама функция — `247`). На редакторском конвейере это `edit`, на черновом-только — `draft`.
|
||||
**Не draft-волна и не «любая»:** иначе черновой-только конвейер получит ось, которая НИКОГДА ничего не
|
||||
считает отгруженным. Отступление от этого — пинг, не решение.
|
||||
|
||||
**«Решаешь сам и аргументируешь»:** где именно вычитается уже потраченный слот, как это переживает
|
||||
рестарт, и что происходит с частично двинутым юнитом.
|
||||
|
||||
**2. Строка 232, ВТОРАЯ половина.** Первая (читающий метод `store.AnnouncedOnceKeys()`) исполнена
|
||||
31.08. Живой остаток: **юнит, прерванный МЕЖДУ волнами, тратит слот объёмного гранта повторно** —
|
||||
покупатель платит за N глав и получает меньше. Разбор — `backend/docs/MONEY_HONESTY_REPORT.md` §4.5.
|
||||
⚠ **Оговорка веса, проверь её:** платформа сегодня продаёт денежным потолком (`--ceiling-usd`), а
|
||||
`--max-units` не передаёт вовсе — то есть вред пока не доезжает до пользователя через платформу.
|
||||
Это меняет приоритет, но не отменяет дефект.
|
||||
`--max-units` не передаёт вовсе — то есть вред пока не доезжает до пользователя ЧЕРЕЗ платформу. Это
|
||||
меняет приоритет, но не отменяет дефект. Проводка `--max-units` в платформу — отдельный ГЕЙЧЕНЫЙ пункт
|
||||
очереди, не твой (`grep -n 'Проводка .--max-units. в платформу остаётся ГЕЙЧЕНОЙ' docs/PROGRESS.md` → `19`).
|
||||
|
||||
**3. ФЧ-4 — раскрытие «часть суммы — старые деньги» не доезжает до `status --json`:**
|
||||
`HistoricalRows` теряется на границе. Покупатель не может отличить новую трату от уже оплаченной.
|
||||
### 3. ФЧ-4 — раскрытие «часть суммы — старые деньги»
|
||||
|
||||
**4. ФЧ-1 — посадки на гарантии, чьи мутации ПЕРЕЖИВАЮТ зелёную батарею.**
|
||||
⚠ **Ограничь тремя**, поимённо: D39.149 п.5 называет три HIGH. Не превращай в бесконечный список —
|
||||
это последний пункт, и он берётся, если осталось время.
|
||||
⚠ **У ФЧ-4 ДВЕ половины. Закрыт = ОБЕ.** Дословно из фикс-листа: «раскрытие „часть суммы — старые
|
||||
деньги“ не доезжает до `status`/`--json` (`HistoricalRows` теряется на границе `status.go`), **а также**
|
||||
второй операторский текст отказа (`--accept-rebill`) цитирует пере-прайсенное число без basis».
|
||||
|
||||
- **(а)** `HistoricalRows` теряется на границе отчёта: `rebillOutcome` возвращает
|
||||
`(basis, units, usd, outputUnits)` и роняет его (`grep -n 'rebillOutcome' backend/internal/pipeline/status.go`),
|
||||
в `StatusReport` соответствующего json-тега нет. Покупатель не может отличить новую трату от уже
|
||||
оплаченной.
|
||||
- **(б)** текст отказа по названному потолку `--accept-rebill=<usd>` печатает `proj.USD` **без**
|
||||
`projectionBasis` и **без** `HistoricalRows`, тогда как оба других текста согласия их несут
|
||||
(`grep -n 'caps consent at' backend/internal/pipeline/rebill.go` → `425`, ветка
|
||||
`r.AcceptRebill.Capped`; для сравнения `grep -n 'func projectionBasis' backend/internal/pipeline/rebill.go` → `305`).
|
||||
|
||||
⚠ **Инвариант, которому подчиняется этот пункт — закон раскрытия D39.181 п.1** (ратифицирован словом
|
||||
владельца 31.08, самая свежая норма зоны): четыре обязанности — **не молчать · не называть причину,
|
||||
которую не сравнивал · ФИГУРА ЕДЕТ С БАЗИСОМ · КАНАЛ НАЗНАЧАЕТ ПОТРЕБИТЕЛЬ**. Ты добавляешь денежную
|
||||
фигуру на опубликованную поверхность — обе выделенные обязанности про твой случай прямо.
|
||||
⚠ **И не рассчитывай, что сборка тебя поправит:** спроектированный гейт Г1 (реестр раскрытия +
|
||||
рефлективный конформанс-тест), который поймал бы новое поле без записи, **НЕ построен** —
|
||||
`grep -rn 'Consumers\b' backend/internal --include=*.go | wc -l` → `0`. Компилятор молчит.
|
||||
|
||||
**«Делай РОВНО так»:** обе половины, и фигура едет с базисом. **«Решаешь сам и аргументируешь»:** форма
|
||||
раскрытия исторических строк на проводе и в операторском тексте (имя поля, форма basis, что печатать
|
||||
при `HistoricalRows=0`).
|
||||
|
||||
### 4. ФЧ-1 — посадки на гарантии, чьи мутации ПЕРЕЖИВАЮТ зелёную батарею
|
||||
|
||||
⚠ **«Делай РОВНО так» — ограничь ТРЕМЯ, и вот они поимённо.** D39.149 п.5
|
||||
(`grep -n '^## D39.149' docs/architecture/05-decisions-log.md` → `523`; сам пункт — `533`) говорит «Три HIGH фикс-листа —
|
||||
не дефекты поведения, а НЕПРИБИТЫЕ гарантии: мутации переживали зелёную батарею», но поимённо называет
|
||||
ровно ОДНУ; остальные две живут только в теле фикс-листа. Поэтому все три вписаны сюда, и брать надо
|
||||
ЭТИ, а не изобретать свои:
|
||||
|
||||
1. **Снятие фильтра по книге в `CheckpointUsageForBook`** — `WHERE j.book_id = ?` не валит ни store-,
|
||||
ни денежные pipeline-тесты (`grep -n 'func (s \*Store) CheckpointUsageForBook' backend/internal/store/ledger.go`
|
||||
→ `367`, сам фильтр — `371`). Единственная защита от того, чтобы деньги ЧУЖОЙ книги вошли в число
|
||||
согласия. **Воспроизведено оркестратором ЛИЧНО.** Существующий тест
|
||||
(`internal/store/ledger_test.go:10` `TestCheckpointUsageForBookReturnsInsertionOrder`) пинит только
|
||||
порядок вставки.
|
||||
2. **Ключ ячейки можно лишить `chunk_idx`** — обход начнёт съедать деньги соседнего чанка (во всех
|
||||
фикстурах глава = один чанк, поэтому никто не краснеет).
|
||||
3. **Ветка пометки «строка не пере-оценена» не ловится на продовом пути** — `fromHistory` из
|
||||
`(rp *repricer) usd` (`grep -n 'func (rp \*repricer) usd' backend/internal/pipeline/reprice.go` → `113`).
|
||||
|
||||
⚠ **Честность про источник:** гарантия (1) воспроизведена приёмкой лично, а **(2) и (3) приняты СО СЛОВ
|
||||
мутационной линзы** — приёмка №18 записала это в свою секцию «что НЕ проверено». Значит первый твой шаг
|
||||
по каждой из них — **воспроизвести мутацию и увидеть ЗЕЛЁНУЮ батарею**; если мутация на самом деле
|
||||
краснеет, гарантия уже прибита, и это **находка**, а не повод молча заменить её своей.
|
||||
|
||||
⚠ **Механизм посадок в зоне УЖЕ ПОСТРОЕН — не пиши четвёртый одноразовый харнесс.**
|
||||
`backend/cmd/tmmutate` + каталог `backend/cmd/tmmutate/mutations.json` (**73** записи:
|
||||
`python3 -c "import json;print(len(json.load(open('backend/cmd/tmmutate/mutations.json'))))"` → `73`).
|
||||
Его собственный докстринг объясняет, зачем он в репозитории: «Two packs and one acceptance in a row have
|
||||
each written this harness from scratch in a scratch directory, and it died with the session every time».
|
||||
Денежного пути в каталоге сегодня НЕТ вовсе — `grep -c 'reprice.go\|rebill.go\|volume.go\|ledger.go' backend/cmd/tmmutate/mutations.json`
|
||||
→ `0`, то есть твои три гарантии туда и просятся.
|
||||
|
||||
⚠⚠ **ЛОВУШКА РЕПОЗИТОРИЯ, читай прежде чем запускать:** `-root` **без дефолта**, и копия дерева — **не
|
||||
предложение**. Работать ТОЛЬКО на копии:
|
||||
|
||||
```
|
||||
cp -a backend /tmp/mut/backend && cd /tmp/mut/backend
|
||||
go run ./cmd/tmmutate -root . # весь каталог
|
||||
go run ./cmd/tmmutate -root . -id A-lock-arbiter # выборочно
|
||||
go run ./cmd/tmmutate -root . -logs /tmp/mutlogs # сохранить полный вывод тестов
|
||||
```
|
||||
|
||||
Причина в докстринге прямая: однажды посадка просидела в ОБЩЕМ дереве сорок минут после таймаута, и
|
||||
независимый ревьюер намерил по нему несуществующий дефект. Здесь параллельные сессии — это цена чужой
|
||||
работы.
|
||||
|
||||
**«Делай РОВНО так»:** ровно три гарантии выше · каждая заводится **ЗАПИСЬЮ в `mutations.json`**, а не
|
||||
только тестом · `tmmutate` гоняется на копии. **«Решаешь сам и аргументируешь»:** форма самих посадок
|
||||
(какую строку и как ломать), уровень теста (store / pipeline / оба), формулировка `why` в каталоге.
|
||||
|
||||
⚠ Это ПОСЛЕДНИЙ пункт: он берётся, если после первых трёх осталось время. Не превращай в бесконечный
|
||||
список — если времени хватило только на одну гарантию, это законный исход, назови его в отчёте.
|
||||
|
||||
## Границы — что НАМЕРЕННО не берём
|
||||
|
||||
- **Строка 160 «структура глав, Этап 0»** — хотя слово владельца 15.08 ставит её ближайшим паком и её
|
||||
блокер закрыт 30.08. Причина техническая: строка требует бампа **формы манифеста**, а параллельно
|
||||
идёт платформенный пак, и незнакомая форма манифеста отправляет КАЖДУЮ новую книгу в `rejected`.
|
||||
Берётся отдельно, со стоп-миром.
|
||||
- **Читающая сторона банка** (строки 221 · 224 · 226 · 228 · 230) — крупнейшая продуктовая дыра зоны,
|
||||
но её гейт не техника, а **слово владельца**. Вопрос вынесен ему отдельно.
|
||||
блокер снят 30.08 — строки 231 и 236 в таблице БОЛЬШЕ НЕТ
|
||||
(`grep -n '236 сужена D39.175' docs/PROGRESS.md` → `256`). Причина техническая и она в ПОРЯДКЕ ДЕПЛОЯ ниже: строка требует бампа
|
||||
**формы манифеста**, а параллельно идёт платформенный пак. Берётся отдельно, со стоп-миром.
|
||||
- **Читающая сторона банка — гейты РАЗНЫЕ, и их надо различать** (проверено по ячейкам:
|
||||
`for n in 221 224 226 228 230; do grep "^| $n " docs/PROGRESS.md | awk -F'|' '{print $4" | "$5}'; done`):
|
||||
- **221** — гейт «разморозка фронта», то есть слово владельца. Вопрос вынесен ему отдельно
|
||||
⚠ (следа этого вопроса в репозитории нет — если тебе это важно для планирования, спроси, а не считай фактом).
|
||||
- **224** — «держит экран подписи вместе с 221», зона «бэкенд + платформа». **Не берём: ресурс пака.**
|
||||
- **226** — «диагностики нет вовсе». **Не берём: ресурс пака.**
|
||||
- **228** — гейт «ЗАКАЗ по читающей стороне банка», то есть отдельный заказ оркестратора, а не слово
|
||||
владельца. **Не берём: гейт — отдельный заказ.**
|
||||
- **230** — зона «бэкенд + контракт». **Не берём: пак версию контракта не двигает** (см. ПОРЯДОК ДЕПЛОЯ).
|
||||
- **Остальная свободная работа зоны в этот пак НЕ входит:** строки **246** (СРОЧНО — две копии полосы
|
||||
отказов обещают разное), **247**, **233**, **229**, **141**-остаток, **131**. Из свободной очереди пак
|
||||
берёт ровно **232** и **197** (`grep -n 'Свободная работа зоны' docs/PROGRESS.md` → `254`).
|
||||
- **Следующий платный замер** — не в этом паке: у его оси «эффорт off против low» не разрешено
|
||||
предусловие (см. ниже), и до решения владельца он не заказывается.
|
||||
предусловие, и носитель предусловия назван — **строка 237(б)**, вендор-сверка маппинга эффорта
|
||||
`deepseek-v4-pro` (`grep -n '^| 237 ' docs/PROGRESS.md` → `178`; реестр
|
||||
`docs/architecture/13-tech-debt-anchors.md` §Б-108). По гардрейлу владельца закрывается ТОЛЬКО
|
||||
вендор-докой. До решения владельца замер не заказывается.
|
||||
|
||||
## ⚠ ПОРЯДОК ДЕПЛОЯ — почему пак безопасен параллельно платформе
|
||||
|
||||
Зоны независимы по коду, но **не по деплою**: форма манифеста требует «платформа первой», схема
|
||||
хранилища v16 — «движок первым». **Пак НЕ меняет форму манифеста, схему хранилища и версию
|
||||
контракта.** Понадобилось — **стоп и пинг**, не «сделаю аккуратно».
|
||||
**Пак НЕ меняет форму манифеста, схему хранилища и версию контракта.** Понадобилось — **стоп и пинг**,
|
||||
не «сделаю аккуратно». Почему это жёстко:
|
||||
|
||||
## Свобода и её разметка
|
||||
- **Форма манифеста БЕЗОПАСНОГО ПОРЯДКА НЕ ИМЕЕТ.** Гейт платформы — строгое равенство ОДНОЙ константе:
|
||||
`if m.Version != KnownManifestVersion` (`grep -n 'if m.Version != KnownManifestVersion' platform/internal/ingest/manifest.go`
|
||||
→ `112`; сама константа — `grep -n 'KnownManifestVersion = ' platform/internal/ingest/manifest.go` →
|
||||
`88`, `"tm-manifest-v2"`). Окна из двух версий НЕТ. Поэтому «платформа первой» так же смертельно, как
|
||||
«движок первым»: выкатив любую сторону в одиночку, ты отправляешь КАЖДУЮ новую книгу в отказ
|
||||
(`ReasonParserUnavailable`, который жжёт бюджет попыток). Бамп формы = **стоп-мир** либо специально
|
||||
построенное временное окно двух версий. ⚠ Это исправление: прежняя редакция промта писала «форма
|
||||
манифеста требует платформа первой» — носителя у этой фразы в корпусе НЕТ.
|
||||
- **Схема хранилища — «движок первым»**, и это документировано: движок первым → `tmctl migrate` по
|
||||
каждой книге до прогонов → и только потом платформа (D39.158 п.6,
|
||||
`grep -n 'движок первым' docs/architecture/05-decisions-log.md` → `715`).
|
||||
- **Версия контракта** — пак её не двигает. Твоё новое поле в `status --json` аддитивно и безопасно:
|
||||
платформа читает отчёт аллоулистом (`ingest.StatusReport`, `runner/engine.go` «decodes the allowlisted
|
||||
subset»), неизвестные поля игнорирует. Это ПРОВЕРЕНО, но проверь и ты — клейм «зоны независимы по
|
||||
коду» держится именно на этом.
|
||||
|
||||
«Делай РОВНО так» — порядок пунктов · границы выше · запрет на три вещи из порядка деплоя ·
|
||||
ограничение ФЧ-1 тремя посадками. **Решаешь сам и аргументируешь**: форма хранения и проводки
|
||||
модели в числе согласия, форма раскрытия исторических строк, состав посадок.
|
||||
## Свобода и её разметка — сводка
|
||||
|
||||
Каждый пункт состава размечен внутри себя; здесь только свод, тело — выше.
|
||||
|
||||
| Пункт | «Делай РОВНО так» (отступление = пинг) | «Решаешь сам и аргументируешь» |
|
||||
|---|---|---|
|
||||
| 1 ФЧ-5 | четыре обязательства D39.150 п.1, включая снятие обещания точности в ОБОИХ живых местах | форма проводки текущей модели, формула округления вверх, слова оговорки |
|
||||
| 2 строка 232 | ось = `finalStageWave`; закрытие объявляется только по остатку «слот»; остаток «первая редакторская стадия» — вне скоупа и НАЗЫВАЕТСЯ в отчёте | где вычитается потраченный слот, как переживает рестарт |
|
||||
| 3 ФЧ-4 | обе половины; фигура едет с базисом (D39.181 п.1) | форма раскрытия на проводе и в тексте отказа |
|
||||
| 4 ФЧ-1 | ровно три названные гарантии; запись в `mutations.json`; `tmmutate` только на копии | форма посадок, уровень тестов, текст `why` |
|
||||
|
||||
Плюс общее «ровно так»: **порядок пунктов** · **границы выше** · **три запрета из ПОРЯДКА ДЕПЛОЯ**.
|
||||
Всё, что здесь не названо, — твоё решение с аргументом в отчёте. Мои приоры **опровергаются аргументом
|
||||
или замером**; неопровергнутое исполняется как написано.
|
||||
|
||||
## Мандат самопроверки — исполнением
|
||||
|
||||
`make battery` — не отчёт, а предусловие. Обязательны: **дифф `^func Test` исполнением, не памятью** ·
|
||||
**собственные адверсариальные посадки ВНЕ списка выше** · **отдельный проход по СВОЕЙ ГОТОВОЙ работе**
|
||||
свежим контекстом. Субагенты разрешены явно; модель задавай ИМЕНЕМ.
|
||||
⚠ **Править или удалять тест ради зелени — недопустимо.** Правка, вызванная сменой поведения, которая
|
||||
заказана этим промтом, — обслуживание, и она **объявляется в отчёте**: что изменилось в поведении,
|
||||
какой тест это описывал, куда уехала гарантия.
|
||||
⚠ Перед отчётом **сверь каждый клейм с результатом инструмента ЭТОЙ сессии**. Последний абзац —
|
||||
план или обещание? Сделай сейчас.
|
||||
`make battery` — **не отчёт, а предусловие** (проверено на этой машине: `cd backend && make battery` →
|
||||
EXIT=0, `golangci-lint` `0 issues`). Зелёная батарея НИЧЕГО не доказывает про твою работу — весь смысл
|
||||
пункта 4 в том, что мутации её переживают.
|
||||
|
||||
**Обязательны, и каждое — с ПРЕДЪЯВЛЯЕМЫМ АРТЕФАКТОМ в отчёте:**
|
||||
|
||||
1. **Дифф `^func Test` исполнением, а не памятью.** Команда:
|
||||
`grep -rn '^func Test' backend --include=*_test.go | wc -l` (на HEAD этой смены → `1083`) — и
|
||||
пофамильный дифф имён до/после. **Удалённых имён должно быть 0**; если не 0 — назвать каждое и
|
||||
объяснить.
|
||||
2. **Собственные адверсариальные посадки ВНЕ трёх гарантий пункта 4** — механизм тот же `tmmutate` на
|
||||
копии дерева. Артефакт: **красный вывод на каждую посадку**, и отдельно — список посадок, которые
|
||||
батарея ПЕРЕЖИЛА (это и есть находки).
|
||||
3. **Отдельный проход по СВОЕЙ ГОТОВОЙ работе свежим контекстом.** **Субагенты разрешены явно; модель
|
||||
задавай ИМЕНЕМ и знай, сколько их у тебя работает** (Fable 5 — рекомендация одного-двух, не потолок;
|
||||
веер под предмет выбираешь ты).
|
||||
⚠ **Артефакт прохода — СПИСОК НАХОДОК С ДИСПОЗИЦИЯМИ в отчёте.** «Прошёл, находок нет» отчётом НЕ
|
||||
является: ноль находок обосновывается тем, ЧТО ИСКАЛОСЬ и какими командами.
|
||||
|
||||
**Оси ревью, которые я называю уязвимыми ИМЕННО В ЭТОМ ПАКЕ** (1–3 хватит; **вправе заменить с
|
||||
аргументом**, если найдёшь уязвимее):
|
||||
|
||||
1. **Направление ошибки.** Округление ВВЕРХ ложится на путь, где старое поведение занижало. Классика
|
||||
этого места — **двойной счёт** (округлили и там, и там) и **перелёт порога согласия** (число выросло
|
||||
настолько, что гейт спрашивает там, где раньше не спрашивал, или наоборот).
|
||||
2. **`HistoricalRows` на новой границе** — не теряется и не удваивается; текст отказа и `--json` говорят
|
||||
ОДНО И ТО ЖЕ число с одним и тем же базисом.
|
||||
3. **Слот гранта на черновом-только конвейере** — `finalStageWave` на нём равен `draft`; убедись
|
||||
исполнением, что ось не выродилась в «никогда ничего не отгружено».
|
||||
|
||||
⚠ **Особо недоверчиво к `usd()`:** его собственный докстринг называет вывод принадлежности к поколению
|
||||
«an inference, not an identity — **the one place in here worth distrusting**» и перечисляет ДВЕ
|
||||
предпосылки, каждая из которых ломается (`sed -n '86,112p' backend/internal/pipeline/reprice.go`). Твоя
|
||||
правка ложится прямо на него.
|
||||
|
||||
⚠ **Править или удалять тест ради зелени — НЕДОПУСТИМО.** Правка, вызванная сменой поведения, которая
|
||||
**заказана этим промтом или ратифицирована**, — обслуживание, и держать протухший тест не нужно; но она
|
||||
**ОБЪЯВЛЯЕТСЯ в отчёте**: что изменилось в поведении, какой тест это описывал, куда уехала гарантия.
|
||||
Заказанность решает ЗАКАЗ, а не ты: нет в этом промте и нет ратификации — **пинг** (D39.183).
|
||||
|
||||
⚠ Перед отчётом **сверь каждый клейм с результатом инструмента ЭТОЙ сессии**. Последний абзац — план
|
||||
или обещание? Сделай сейчас.
|
||||
|
||||
## Отчёт
|
||||
|
||||
Записка-план ДО работы (эхо: скоуп · инварианты · не-делать) в секцию «Бэкенд» `docs/PROGRESS.md`.
|
||||
Итог — туда же, каждое число **с командой получения**. Обязательная секция **«Что НЕ удалось и что
|
||||
НЕ проверено»**. Конфликт промта с кодом — **пинг через владельца, не интерпретация**.
|
||||
**Записка-план ДО работы** — эхо-протокол старта, ≤10 строк «скоуп · инварианты · не-делать», в секцию
|
||||
«Бэкенд» `docs/PROGRESS.md`. Первым действием, до кода.
|
||||
|
||||
**Комплектность против заказа — механически:** пройди по четырём пунктам состава и по таблице разметки и
|
||||
отчитайся ПО КАЖДОМУ. ⚠ Эта тема трижды проваливалась на том, что исполнялся первый пункт, а нумерованный
|
||||
остаток бросался.
|
||||
|
||||
**Итог — туда же, и «заявление = команда»:** каждое число и каждая категорика — **с командой получения**,
|
||||
которая это число реально печатает. Приёмка пере-ранит твои команды; команда, печатающая «ok» вместо
|
||||
числа, считается отсутствующей.
|
||||
|
||||
**Обязательная секция «Что НЕ удалось и что НЕ проверено».** ⚠ «Не проверено» ≠ «опровергнуто» — это
|
||||
РАЗНЫЕ исходы, и **третий законен**: «взялся, не смог, вот где встал» — полноценный результат смены, а не
|
||||
провал. Сюда же обязательно попадает остаток «первая редакторская стадия» (пункт 2) и любая гарантия
|
||||
ФЧ-1, до которой не дошли руки.
|
||||
|
||||
**Конфликт промта с кодом или доками — ПИНГ через владельца, не интерпретация.** И у тебя есть **право
|
||||
сказать «этого делать не надо»** с аргументом: сессия, приносящая развилку вместо тихой девиации, делает
|
||||
правильно. Этот промт писан по коду на HEAD своей смены; код первичен, и если ты видишь другое — это
|
||||
находка, а не твоя ошибка.
|
||||
|
||||
## Прямой канал
|
||||
|
||||
Механизм — `CLAUDE.md`, секция «Связь между сессиями». Свой блок в `/tmp/textmachine-channel` —
|
||||
первым делом. ⚠ Адрес из файла **не доказательство, что сессия жива**: сверяйся с `ListAgents`.
|
||||
Нужной роли нет ⇒ **канала нет, это НОРМАЛЬНЫЙ случай**, вопрос секцией в отчёт.
|
||||
Механизм — `CLAUDE.md`, секция «Связь между сессиями». **Указатель: файл `/tmp/textmachine-channel`** —
|
||||
впиши туда СВОЙ блок (`role=` `session=` `ref=` `written=` `note=`; имя — из `ListAgents`) первым делом,
|
||||
дописывая в конец и не трогая чужие блоки. Эхо-записку (блок выше) отправь первым действием по адресу из
|
||||
файла — это заодно и проверка канала.
|
||||
⚠ Адрес из файла **не доказательство, что сессия жива**: файл переживает смерть сессии, а `ListAgents` —
|
||||
нет. **Сверяйся с `ListAgents` перед тем, как писать.**
|
||||
⚠ Нужной роли в файле нет, файла нет или имя не отвечает ⇒ **КАНАЛА НЕТ, и это НОРМАЛЬНЫЙ случай: НЕ
|
||||
ищи, НЕ опрашивай сессии подряд.** Вопрос — секцией в отчёт; владелец прочитает и пере-передаст.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
> **Роль:** платформа. **Зона записи:** `platform/`. **Не коммитишь** — дерево готовишь и передаёшь
|
||||
> на лендинг оркестратору (канон — `CLAUDE.md`, таблица ролей). Чужие зоны читаешь, не правишь.
|
||||
> ⛔ Ни `git commit`, ни `add`, ни `reset`/`checkout`/`stash`/`clean` — ничего, что двигает индекс или
|
||||
> историю: рядом работают параллельные сессии, и их незакоммиченные файлы твой индекс унесёт.
|
||||
> **Обязателен к ссылке:** `platform/docs/ENGINEERING_STANDARDS.md`; отступление от него — пинг, не
|
||||
> самовольство. Направление зоны — `platform/docs/PLATFORM_DIRECTION.md`.
|
||||
|
||||
|
|
@ -11,99 +13,418 @@
|
|||
пользователя и без единого сигнала оператору**. Четыре независимых пути ведут к одному и тому же:
|
||||
человек заплатил, а получил либо не то, либо ничего, и вывести его из этого состояния нечем, кроме
|
||||
psql. После пака оплаченный прогон возвращается на **те деньги, за которые был продан**, оператор
|
||||
перестаёт начислять кредиты дважды, а чужой процесс перестаёт замораживать чужую проекцию.
|
||||
перестаёт начислять кредиты дважды, а чужая строка в общем журнале книги перестаёт замораживать
|
||||
проекцию чужого прогона.
|
||||
|
||||
## Карта чтения — пять позиций, дальше только по их ссылкам
|
||||
|
||||
1. `platform/README.md` — устройство зоны · `platform/docs/ENGINEERING_STANDARDS.md` (обязателен).
|
||||
2. `platform/docs/platform-PROGRESS.md` — зонный журнал: текущее состояние и **пинги оркестратора**
|
||||
(там же ПИНГ №21 про гейт батареи и ПИНГ №22 про заниженную ставку и снятый сайдкар).
|
||||
3. Ряды регистра, названные ниже поимённо: `PD-168` · `PD-89` · `PD-426` · `PD-214` · `PD-374` —
|
||||
`platform/docs/DEFECT_REGISTER.md`, греп по номеру.
|
||||
4. `docs/architecture/15-money-path.md` §3 — деньги платформы по шагам (холд, расплата, `run abandon`).
|
||||
5. `docs/architecture/14-api-contract/` — канон контракта; **зеркало фронта НЕ норматив**.
|
||||
|
||||
⚠ Ратифицированное вложено в тело этого промта; **код-факты добываешь САМ** — указатели ниже
|
||||
отправные точки, код первичен. Числа ниже даны С КОМАНДОЙ ПОЛУЧЕНИЯ: пере-ранишь — сойдётся; не
|
||||
сошлось — это находка, неси её пингом, а не подгоняй под текст.
|
||||
|
||||
## ⚠ ПРЕДУСЛОВИЕ, НЕ ПУНКТ РАБОТЫ — прочти прежде всего
|
||||
|
||||
**Экспортируй `TM_PLATFORM_TEST_DSN` до первой команды.** Замер 02.09 на этом хосте:
|
||||
`go test ./... -count=1` в `platform/` без DSN даёт **661 тест: PASS 342, SKIP 319, FAIL 0, exit 0**,
|
||||
все 18 пакетов «ok». В `pgstore` тёмно 167 из 177, в `runs` — 110 из 135, и среди пропущенных —
|
||||
**105 тестов денежного пути реконсилятора**.
|
||||
**Условий хоста у батареи ЧЕТЫРЕ, и все четыре на этом хосте выполнимы. Выставь их до первой
|
||||
команды** — иначе половина зоны молча не гоняется:
|
||||
|
||||
⚠ `make check` про это ЧЕСТЕН — печатает «did NOT run (set TM_PLATFORM_TEST_DSN…)» и имена скипов.
|
||||
| условие | как выставить | что откроет |
|
||||
|---|---|---|
|
||||
| `TM_PLATFORM_TEST_DSN` | стенд уже поднят (`/tmp/.s.PGSQL.55433`); DSN — `platform/docs/STACK_DECISIONS.md`, раздел стенда разработчика, греп `port=55433` | денежный путь `runs`, `pgstore`, `books`, `cmd/tmplatformctl` |
|
||||
| `TM_PLATFORM_TEST_ENGINE_BIN` + `TM_PLATFORM_TEST_BOOK_TEMPLATE` | движковый бинарь и шаблон деплоя | 3 теста `internal/runner` + `internal/books` |
|
||||
| достижимый пользовательский systemd | на этом хосте `/run/user/1000` жив, ставить нечего | `systemdOrSkip`, `internal/runner/systemd_test.go` |
|
||||
| **`export PATH=$HOME/go/bin:$PATH`** | ⚠ без него `make check` НЕ ЗАПУСКАЕТСЯ ВОВСЕ: `tools-check` падает на `sqlc` («got: none»), хотя нужный `v1.31.1` лежит в `~/go/bin` | весь `make check` |
|
||||
|
||||
Проверка последнего одной командой: `cd platform && make tools-check` → `Error 1` на sqlc;
|
||||
`cd platform && PATH=$HOME/go/bin:$PATH make tools-check` → выход 0. **Сети нет — ничего не ставь**,
|
||||
бинарь уже на диске, вопрос только в PATH.
|
||||
|
||||
**Замер 02.09 на этом хосте, без DSN** (числа — с командами; голый `go test ./...` печатает ровно
|
||||
18 строк «ok» и НИ ОДНОГО из этих чисел, поэтому команда счёта — verbose):
|
||||
|
||||
```
|
||||
cd platform && go test ./... -count=1 -v > /tmp/battery_v.txt 2>&1; echo "exit=$?"
|
||||
grep -cE '^=== RUN [^/]+$' /tmp/battery_v.txt # 661 верхнеуровневых тестов
|
||||
grep -c '^--- PASS' /tmp/battery_v.txt # 342
|
||||
grep -c '^--- SKIP' /tmp/battery_v.txt # 319 верхнеуровневых скипов
|
||||
grep -c '^--- FAIL' /tmp/battery_v.txt # 0
|
||||
grep -c '^ok ' /tmp/battery_v.txt # 18 пакетов
|
||||
grep -c -- '--- SKIP' /tmp/battery_v.txt # 330 — ТО ЖЕ САМОЕ вместе с подтестами
|
||||
```
|
||||
|
||||
⚠ **Снимаю кажущееся противоречие заранее:** ПИНГ №21 в зонном журнале пишет про пустое окружение
|
||||
**330** скипов, здесь стоит **319**. Это ОДИН И ТОТ ЖЕ прогон: `make check` грепает `-- '--- SKIP'`,
|
||||
то есть считает подтесты вместе с верхнеуровневыми. Оба числа верны, расходиться тут не о чем.
|
||||
|
||||
Среди пропущенных — **110 тестов денежного пути реконсилятора** (верхнеуровневых; вхождений
|
||||
сообщения вместе с подтестами — 112):
|
||||
|
||||
```
|
||||
cd platform
|
||||
go test ./internal/runs/ -count=1 -v 2>&1 \
|
||||
| awk '/reconciler.s money path/{p=1;next} p&&/^--- SKIP/{n++} {p=0} END{print n+0}' # 110
|
||||
go test ./internal/runs/ -count=1 -v 2>&1 | grep -c "reconciler's money path" # 112
|
||||
```
|
||||
|
||||
⚠ `make check` про скипы ЧЕСТЕН — печатает строку «did NOT run …» и имена скипнутых тестов.
|
||||
Молчит именно голый `go test ./...`.
|
||||
|
||||
**Цена, которую ты обязан не повторить:** в регистре **восемь рядов** заведены с формулировкой
|
||||
«мутация пережила ПОЛНУЮ батарею», **четыре из них открыты** (`PD-433`, `PD-386`, `PD-389`, `PD-388`),
|
||||
и ни один не называет DSN. На хосте без базы такой вердикт не значит ничего.
|
||||
**Цена, которую ты обязан не повторить:** в регистре **21 ряд** заведён с формулировкой «мутация
|
||||
пережила ПОЛНУЮ батарею», и **восемь из них открыты** — `PD-433` · `PD-375` · `PD-382` · `PD-383` ·
|
||||
`PD-386` · `PD-388` · `PD-389`, плюс `PD-423` (там фраза в смысле «пин ЗЕЛЁН в полной батарее со
|
||||
скипами 0»). **Ни один не называет DSN**, то есть «полная батарея» этих вердиктов могла быть той
|
||||
самой, где тёмно 319 тестов.
|
||||
|
||||
```
|
||||
cd platform && awk -F'|' '/^\| PD-/{l=tolower($0);
|
||||
if(l~/полную батарею|полной батарее|всю батарею/){st=$(NF-2);gsub(/^ +| +$/,"",st);
|
||||
if(st=="open")o++;else f++}}END{print "OPEN="o,"FIXED="f,"TOTAL="o+f}' docs/DEFECT_REGISTER.md
|
||||
# OPEN=8 FIXED=13 TOTAL=21
|
||||
```
|
||||
|
||||
⇒ **Каждая посадка мутации в твоём отчёте обязана нести строку «гонялось при DSN = да».** Иначе пак
|
||||
проверит себя ровно тем способом, каким заводились ряды, которые он чинит.
|
||||
|
||||
## Состав пака — четыре правки и один гейт
|
||||
|
||||
**1. ГЛАВНОЕ — денежный бюджет прогона ЧИТАТЬ, а не пере-выводить из ставки.**
|
||||
`internal/runs/reconcile.go`, греп `s.Pricing.Ceiling(l.CeilingChapters)` в `reopen`: бюджет берётся
|
||||
из ставки СЕГОДНЯШНЕГО деплоя. Вызывающих двое — свиповый `restart` и **контрактный `Resume`**, то
|
||||
есть путь дёргает пользователь. Смена `TM_PLATFORM_USD_PER_CHAPTER` между покупкой и продолжением
|
||||
молча пере-оценивает уже оплаченный прогон в обе стороны; при удвоении ставки на тонком счёте он
|
||||
становится невозобновляемым. Носитель — `PD-168` (стоит `minor`, вес занижен).
|
||||
**Задача:** сумма, на которую пользователь согласился, хранится при покупке и читается при
|
||||
перезапуске. Форму решаешь сам и аргументируешь.
|
||||
### 1. ГЛАВНОЕ — денежный бюджет прогона ЧИТАТЬ, а не пере-выводить из ставки
|
||||
|
||||
**2. `PD-89` — печатать ключ идемпотентности НА ПУТИ ОШИБКИ.** `write()` чеканит `id = newKey()`,
|
||||
зовёт денежную операцию и на ошибке возвращает `err`, **не напечатав ключ**. Неоднозначный обрыв на
|
||||
коммите → оператор повторяет без `--key` → **второе начисление**. Комментарий тремя строками ниже
|
||||
описывает ровно эту ловушку, но только для пути чтения баланса. Лечение — одна строка; посадка
|
||||
обязательна.
|
||||
`internal/runs/reconcile.go`, функция `reopen` (`:1350`). **Ставка читается там ДВАЖДЫ, и оба места
|
||||
денежные:**
|
||||
|
||||
**3. `PD-426` + `PD-214` — решать «ЧЕЙ handshake» ПРЕЖДЕ, чем его валидировать.**
|
||||
`internal/ingest/tail.go`, функция `apply`: четыре отказа (декод, `checkVersion`, пустой
|
||||
`EngineRunID`, `Seq != 1`) стоят ВЫШЕ ветки «another attempt's stream begins here». Чужой ЗАКОННЫЙ
|
||||
hello карантинит проекцию **оплаченного живого прогона**, снятия карантина в дереве нет, операторской
|
||||
ручки нет. Гейдж `tm_platform_quarantined_attempts` это видит и молчит о том, что делать.
|
||||
**Задача:** перестановка решения о принадлежности выше валидации + ручка снятия карантина.
|
||||
```
|
||||
cd platform && grep -n 'Pricing' internal/runs/reconcile.go
|
||||
# 1363: budget := s.Pricing.Ceiling(l.CeilingChapters)
|
||||
# 1397: consent = s.Pricing.Ceiling(l.CeilingChapters)
|
||||
```
|
||||
|
||||
**4. МЕЛКОЕ, закрывающее урок пака: гейт батареи печатает ВСЕ условия хоста, а не первое.**
|
||||
Сегодня `platform/Makefile` подсказывает роль первого недостающего условия и молчит про остальные.
|
||||
- `:1363` — **бюджет** перезапуска: `remaining = budget - spent`, отсюда `Ceiling` в `RestartInput`.
|
||||
- `:1397` — **funded consent на пере-оплату** при `book.BankMoved`, уезжает в `RestartInput.AcceptRebill`.
|
||||
Стоит в ветке `if from == fromAFinishedRun`, то есть **только на пользовательском `Resume`** — ровно
|
||||
на том пути, который и опасен. Комментарий `:1384-1386` подтверждает денежный смысл: «funded
|
||||
consent, the same figure Start grants».
|
||||
|
||||
**5. ГЕЙТ ПРОТИВ КЛАССА, а не против ряда** (заказ оркестратора №22, замер 02.09): колонка
|
||||
серьёзности регистра разъехалась с сутью — **21 открытый ряд** несёт два и более слова тревоги
|
||||
(деньги · холд · «молча» · блокирует · невидим · 500 · паника) при `minor`/`info`. Добавь в гейт
|
||||
зоны печать таких рядов. Без него следующая разведка снова доложит «major всего два».
|
||||
Вызывающих у `reopen` двое: свиповый `restart` (`:1224`, вызов `:1244`, `fromALiveRun`) и контрактный
|
||||
`Resume` (`:1481`, вызов `:1548`, `fromAFinishedRun`). Смена `TM_PLATFORM_USD_PER_CHAPTER`
|
||||
(`internal/config/config.go:485`) между покупкой и продолжением молча пере-оценивает уже оплаченный
|
||||
прогон в обе стороны; при удвоении ставки на тонком счёте он становится невозобновляемым. Носитель —
|
||||
`PD-168` (стоит `minor`, вес занижен; замер ряда: холд `5.500000` вместо `2.500000`).
|
||||
|
||||
**Задача (делай РОВНО так, отступление = пинг):**
|
||||
⚠ **Первая половина УЖЕ построена, строить нечего.** `PD-168` дословно: «Исходная сумма восстановима
|
||||
без пересчёта — она лежит в холде первой попытки (`reservations.ceiling_micro_usd` /
|
||||
`run_attempts.ceiling_micro_usd`)». Обе колонки в схеме есть:
|
||||
|
||||
```
|
||||
cd platform && grep -n 'ceiling_micro_usd' internal/pgstore/migrations/*.sql
|
||||
# ЧЕТЫРЕ хита, из них колонки — две:
|
||||
# 00007_credits.sql:72 — reservations.ceiling_micro_usd
|
||||
# 00009_runner.sql:36 — run_attempts.ceiling_micro_usd (:70 — её же `drop` в Down-миграции)
|
||||
# 00011_ceiling_argument.sql:5 — КОММЕНТАРИЙ-предупреждение, не колонка
|
||||
```
|
||||
|
||||
Значит задача **не «завести хранение»**, а «заставить `reopen` ЧИТАТЬ уже хранимое вместо
|
||||
`s.Pricing.Ceiling(...)`». **Схему хранилища платформы при этом не трогаем — миграция не нужна и не
|
||||
заказана.** Понадобилась вопреки этому — стоп и пинг, а не «сделаю аккуратно».
|
||||
|
||||
⚠ **Лечение обязано закрыть ОБА места, `:1363` и `:1397`.** Закрыт один — это половина, и в отчёте так
|
||||
и объявляется: путь остаётся под «согласием, которого пользователь не давал».
|
||||
⚠ Не спутай с третьей колонкой: `run_attempts.ceiling_arg_micro_usd` (`00011_ceiling_argument.sql`) —
|
||||
это КНИЖНЫЙ аргумент `--ceiling-usd` для движка, а не бюджет попытки; миграция прямо предупреждает
|
||||
«It is NOT ceiling_micro_usd».
|
||||
|
||||
**Решаешь сам и аргументируешь:** какую именно из двух колонок читать, как быть, если холда первой
|
||||
попытки нет (старые прогоны), и нужен ли отдельный аккуратный фолбэк.
|
||||
|
||||
### 2. `PD-89` — печатать ключ идемпотентности НА ПУТИ ОШИБКИ
|
||||
|
||||
`cmd/tmplatformctl/main.go`, функция `write()` (`:184`; ⚠ якорь ряда `:143-150` дрейфанул и указывает
|
||||
теперь на вызывающего `grant`). `id = newKey()` чеканится на `:189`, `applied, err := op(id, now)` —
|
||||
на `:191`, и `if err != nil { return err }` (`:192-194`) возвращает ошибку, **не напечатав ключ**.
|
||||
Неоднозначный обрыв на коммите → оператор повторяет без `--key` → `newKey()` чеканит новый →
|
||||
**второе начисление**. Комментарий `:195-198` описывает ровно эту ловушку, но только для пути чтения
|
||||
баланса («a failed BALANCE READ would have bought a double credit»).
|
||||
|
||||
**Задача (делай РОВНО так):** на пути ошибки ключ обязан быть виден оператору. **Посадка мутации
|
||||
обязательна.** Форма печати (куда, каким текстом, `out` или `stderr`) — **решаешь сам и
|
||||
аргументируешь**.
|
||||
|
||||
### 3. `PD-426` + `PD-214` — чужая строка не должна карантинить НАШУ проекцию
|
||||
|
||||
`internal/ingest/tail.go`, функция `apply` (`:145`). Четыре отказа стоят выше ветки, решающей
|
||||
принадлежность: `:150` декод payload · `:153` `checkVersion` · `:156` пустой `EngineRunID` · `:162`
|
||||
`Seq != 1`. Ошибка любого из них терминальна для проекции: `quarantines()`
|
||||
(`internal/runs/reconcile.go:770-781`) пропускает только `context.Canceled/DeadlineExceeded` и
|
||||
`pgstore.IsTransient`, всё прочее едет в `Store.Quarantine`. Снятия карантина в дереве НЕТ
|
||||
(`grep -rniE 'unquarantine|clearquarantine|quarantine_reason *= *null|dequarantine'` по `.go`/`.sql`
|
||||
— пусто при живом положительном контроле `internal/pgstore/runs.go:1470 func (s *Store) Quarantine`),
|
||||
операторской ручки нет, а гейдж `tm_platform_quarantined_attempts` (`internal/metrics/metrics.go:80`)
|
||||
это видит и молчит о том, что делать.
|
||||
|
||||
⚠ **ПОСЫЛКА, КОТОРУЮ НАДО ЗНАТЬ ТОЧНО — иначе живая проба будет искать несуществующее.**
|
||||
Полностью ЗАКОННЫЙ чужой hello сегодняшней сборки (мажор потока тот же, `engine_run_id` непустой,
|
||||
`seq == 1`) **НЕ карантинит** — он уходит в `default` и молча пропускается, и это защищено ЗЕЛЁНЫМ
|
||||
пином:
|
||||
|
||||
```
|
||||
cd platform && go test ./internal/ingest/ -count=1 -run TestAnotherAttemptsStreamInTheSameJournalIsSkipped -v
|
||||
# --- PASS (тело пина — tail_test.go:185-187: hello(1,"eng-1") … hello(1,"eng-2"), ошибки нет)
|
||||
```
|
||||
|
||||
Карантинит **чужая ИЛИ БИТАЯ** hello-строка, и условий ровно четыре — они же перечислены в `PD-214`
|
||||
(«ручной `tmctl` оператора в каталоге книги, старый мажор, баг чужой сборки»):
|
||||
другой **МАЖОР** потока (`checkVersion` сравнивает ТОЛЬКО мажор, `internal/ingest/events.go:196-208`;
|
||||
у платформы `StreamVersion = "1.1"`, у движка `"1.2"` — обе стороны на мажоре 1, поэтому сегодня
|
||||
проходит) · пустой `engine_run_id` · `seq != 1` · нераспарсиваемый payload.
|
||||
⚠ Формулировка `PD-426` («попасть можно по чужому ЗАКОННОМУ handshake'у») — переупрощение, ряд
|
||||
описывает класс шире, чем воспроизводится; читай `PD-214`, он точнее.
|
||||
|
||||
**Задача — СВОЙСТВО, а не перестановка (делай РОВНО так, отступление = пинг):**
|
||||
> **строка, которая не наша, не должна карантинить нашу проекцию.**
|
||||
|
||||
Что это значит по букве, чтобы ты не внёс дефект буквальным исполнением:
|
||||
|
||||
- ⚠ **Отказ по ДЕКОДУ (`:150`) остаётся ВЫШЕ и не двигается.** `EngineRunID` живёт ВНУТРИ payload
|
||||
`Hello` (`internal/ingest/events.go:82-92`), в `Envelope` (`:71-79`) его нет — значит без успешного
|
||||
декода принадлежность просто НЕИЗВЕСТНА. Переставляемы только три отказа, не четыре.
|
||||
- Понижаются **только** `checkVersion`, пустой `EngineRunID` и `Seq != 1` — **и ТОЛЬКО в ветке, где
|
||||
`want` уже известен** (`want != ""`): там принадлежность решается сравнением `h.EngineRunID == want`,
|
||||
и чужая строка обязана уходить в «не наше, пропустить» ДО валидации.
|
||||
- ⛔ **При `want == ""` валидация обязана остаться полной.** Ветка `case want == "":` (`:166`)
|
||||
УСЫНОВЛЯЕТ поток — зовёт `sink.Begin` и ставит `want, mine = h.EngineRunID, true`. Снять валидацию
|
||||
там значит принять поток ЧУЖОГО МАЖОРА как свой. Это ровно тот дефект, который пак чинит, только с
|
||||
другой стороны.
|
||||
- Зелёный пин `TestAnotherAttemptsStreamInTheSameJournalIsSkipped` обязан остаться зелёным: он
|
||||
описывает уже правильную половину поведения.
|
||||
- Курсорная семантика (`held` против `pos`, движение курсора на hello) — не предмет правки; тронул —
|
||||
объясни зачем.
|
||||
|
||||
**Вторая половина ряда — ручка снятия карантина.** Что она есть — «делай РОВНО так»; **форма
|
||||
(подкоманда `tmplatformctl`, аргументы, что именно сбрасывается, кто имеет право) — решаешь сам и
|
||||
аргументируешь**.
|
||||
|
||||
**Посадка мутации обязательна** на обе половины.
|
||||
|
||||
### 4. МЕЛКОЕ, закрывающее урок пака: гейт батареи печатает ВСЕ условия хоста, а не одно
|
||||
|
||||
⚠ **Дефектов тут ДВА, и они в РАЗНЫХ целях. Заказывается (а), не (б).**
|
||||
|
||||
**(а) — заказ.** Цель `check` печатает над списком скипов **жёстко зашитый литерал**:
|
||||
|
||||
```
|
||||
cd platform && sed -n '91,100p' Makefile
|
||||
# if grep -q -- '--- SKIP' .check.log; then \
|
||||
# echo "--- did NOT run (set TM_PLATFORM_TEST_DSN for the schema tests) ---"; \
|
||||
```
|
||||
|
||||
Строка ни от чего не зависит: она печатается и когда DSN ВЫСТАВЛЕН, а скипы дал совсем другое
|
||||
условие, — то есть отправляет читателя к ручке, которая уже включена. Демонстрация: с выставленным
|
||||
DSN `go test ./internal/books/ ./internal/runner/ -count=1 -v` даёт 3 скипа, причина у них
|
||||
`TM_PLATFORM_TEST_ENGINE_BIN`, а рецепт всё равно советует DSN. Это `PD-374` («отправляя читателя к
|
||||
ручке, которая тут ни при чём») и ПИНГ оркестратора №21 в зонном журнале с таблицей **пусто 330 /
|
||||
DSN 3 / +ENGINE_BIN 0**.
|
||||
|
||||
**(б) — НЕ заказ, не путай.** Поведение «останавливается на первом недостающем» принадлежит цели
|
||||
`tools-check` (`Makefile:60-66`): там каждая проверка делает `exit 1`, поэтому при живом
|
||||
golangci-lint и отсутствующем sqlc печатается только про sqlc. Это отдельная история; тронешь —
|
||||
объяви отдельно.
|
||||
|
||||
**Задача (делай РОВНО так):** цель `check` печатает ВСЕ условия хоста и что каждое включает.
|
||||
**Решаешь сам и аргументируешь: форма.** Приор ПИНГа №21 — выводить перечень из тех же переменных,
|
||||
которые читают хелперы тестов, чтобы список не разъехался с реальностью третий раз; довод против
|
||||
«просто дописать вторую строку» — он чинит сегодняшний счёт, а не механизм. Условий сегодня четыре
|
||||
(таблица в предусловии), и `systemdOrSkip` с PATH-до-`sqlc` в подсказке сегодня не названы вовсе.
|
||||
|
||||
### 5. ГЕЙТ ПРОТИВ КЛАССА, а не против ряда
|
||||
|
||||
Колонка серьёзности регистра разъехалась с сутью: открытых рядов **94**, из них `major` — **2**,
|
||||
и следующая разведка снова доложит «major всего два», хотя тревожных рядов на порядок больше.
|
||||
|
||||
```
|
||||
cd platform && awk -F'|' '/^\| PD-/{st=$(NF-2);gsub(/^ +| +$/,"",st);w=$4;gsub(/^ +| +$/,"",w);
|
||||
if(st=="open"){t++; if(w~/major/)m++}}END{print "open="t,"major="m}' docs/DEFECT_REGISTER.md
|
||||
# open=94 major=2
|
||||
```
|
||||
|
||||
⚠ **Предикат «слова тревоги» задаю дословно, потому что от его прочтения число прыгает вдвое.**
|
||||
Список маркеров (стеммы): `деньг` · `холд` · `молча` · `блокир` · `невидим` · `500` · `паник`.
|
||||
|
||||
```
|
||||
cd platform && for M in any distinct; do printf "%s: " "$M"; awk -F'|' -v mode="$M" '
|
||||
/^\| PD-/{st=$(NF-2);gsub(/^ +| +$/,"",st);w=$4;gsub(/^ +| +$/,"",w);
|
||||
if(st=="open" && w !~ /major/){l=tolower($0); n=split("деньг холд молча блокир невидим 500 паник",m," "); c=0;
|
||||
for(i=1;i<=n;i++){ if(mode=="distinct"){ if(l~m[i]) c++ } else { t2=l; c+=gsub(m[i],"",t2) } }
|
||||
if(c>=2) k++}}END{print k+0}' docs/DEFECT_REGISTER.md; done
|
||||
# any: 21 (≥2 ВХОЖДЕНИЯ любого маркера)
|
||||
# distinct: 13 (≥2 РАЗНЫХ маркера)
|
||||
```
|
||||
|
||||
То есть **21** — это счёт по «≥2 вхождения», а не по «два разных слова»; по «двум разным» их 13
|
||||
(а по буквальным словоформам — 11 и 18 соответственно). Обе величины настоящие, но гейт обязан
|
||||
знать, какую он считает.
|
||||
|
||||
**Задача (делай РОВНО так):** гейт зоны печатает открытые не-`major` ряды, несущие маркеры тревоги.
|
||||
**Решаешь сам и аргументируешь: место, предикат и порог** — и объявляешь выбранный предикат в отчёте
|
||||
командой, дающей его число. Приор по месту: `platform/internal/gates/` — там уже есть прецедент
|
||||
чтения канона выше модуля (`contract_test.go`, константа `zoneRoot + "/../docs/..."`).
|
||||
|
||||
⛔ **`docs/scripts/counts.py` НЕ ТРОГАТЬ** — это единственное, что сегодня читает
|
||||
`platform/docs/DEFECT_REGISTER.md` (`grep -rn 'DEFECT_REGISTER' --include=*.go --include=Makefile
|
||||
--include=*.py --include=*.sh platform/ docs/scripts/` → один хит, `counts.py:51`), и его шапка прямо
|
||||
запрещает правку чужим сессиям: «скрипт — зона ОРКЕСТРАТОРА». Красный `--check`/`--lint` не обходить
|
||||
и не «чинить» подгонкой — это тот же запрет, что подгонка тестов; непонятно или кажется неправым →
|
||||
пинг.
|
||||
|
||||
## Границы — что НАМЕРЕННО не берём
|
||||
|
||||
- **`PD-412`/`PD-413` (LATERAL в полосном агрегате).** Находки настоящие и замеренные (1.359 мс → 0.307 мс
|
||||
на книге 2283 глав), но это переписывание запроса в горячем read-модельном пути, а арифметика полосы
|
||||
в этом паке не трогается. Контенция, а не корректность денег.
|
||||
- **`PD-162` (клин на удалённом каталоге).** Настоящий и денежный, но его терминальность — это эскроу
|
||||
(П-18), а половину эскроу рядом с проектируемым целым зона уже отказывалась строить (диспозиция P7).
|
||||
- **Дверь выдачи** — отдельный пак: он требует минора канона и сводит на себя все точки столкновения.
|
||||
- **`PD-412`/`PD-413` (LATERAL в полосном агрегате).** Находки настоящие и замеренные (1.359 мс →
|
||||
0.307 мс на книге 2283 глав — числа принадлежат `PD-412`), но это переписывание запроса в горячем
|
||||
read-модельном пути, а арифметика полосы в этом паке не трогается. Контенция, а не корректность денег.
|
||||
- **`PD-162` (клин на удалённом каталоге).** Настоящий и денежный, но его АВТОМАТИЧЕСКАЯ терминальность
|
||||
— это эскроу (`platform/BACKLOG.md` П-18/П-20), а строить половину эскроу рядом с проектируемым
|
||||
целым зона уже отказывалась: **пере-диспозиция P6 по `PD-154`** (греп `половину эскроу` в
|
||||
`platform/docs/` — единственный хит, `DEFECT_REGISTER.md:101`).
|
||||
⚠ Ряд называет и ВТОРОЕ, дешёвое лечение — «либо проверкой каталога при допуске». Этим паком оно
|
||||
тоже НЕ берётся: пак закрывает денежный шов возврата, а валидация каталога на допуске — это правка
|
||||
входной двери `Start`, отдельный предмет с собственной ценой на ложных отказах. Операторская ручка
|
||||
на этот класс уже построена (`run abandon [--release-hold]`, P8-FIX; строка П-20 фиксирует, что
|
||||
«пока эскроу нет, ручка остаётся ручной»). ⚠ Считаешь границу неверной — скажи аргументом, право
|
||||
такое у тебя есть (см. «Канал вопросов»).
|
||||
- **Дверь выдачи** — отдельный пак: `createExport`/`getExport` в каноне 0.9.0 есть, платформой не
|
||||
смонтированы (`grep -rn 'createExport\|getExport' platform/internal/ --include=*.go` — пусто), но
|
||||
дверь требует минора канона и сводит на себя все точки столкновения.
|
||||
|
||||
## ⚠ ПОРЯДОК ДЕПЛОЯ — почему пак безопасен параллельно бэкенду
|
||||
|
||||
Зоны независимы по коду (разные модули, импортов нет, `go.work` в корне отсутствует), но **НЕ по
|
||||
деплою**, и сцеплений ДВА с противоположным порядком: форма манифеста ⇒ «сначала платформа, потом
|
||||
движок» (незнакомая форма → `parser_unavailable`, интейк массово отклоняет); схема хранилища v16 и
|
||||
обход ⇒ «движок первым → `tmctl migrate` → потом платформа» (D39.158 п.6).
|
||||
**Поэтому:** пак НЕ меняет форму манифеста, схему хранилища и версию контракта. Понадобилось —
|
||||
**стоп и пинг**, не «сделаю аккуратно».
|
||||
Зоны независимы по коду (разные модули, перекрёстных импортов нет в обе стороны, `go.work` в корне
|
||||
отсутствует), но **НЕ по деплою**, и сцеплений ДВА с противоположным порядком: форма манифеста ⇒
|
||||
«сначала платформа, потом движок» (незнакомая форма → `parser_unavailable`); **схема хранилища
|
||||
ДВИЖКА (v16)** и обход ⇒ «движок первым → `tmctl migrate` по каждой книге → и только потом платформа
|
||||
снимает свой обход» (D39.158 п.6).
|
||||
|
||||
**Поэтому пак НЕ меняет: форму манифеста · схему хранилища ДВИЖКА · версию контракта.** Понадобилось
|
||||
— **стоп и пинг**, не «сделаю аккуратно».
|
||||
|
||||
⚠ Уточняю, потому что прежняя редакция этого места читалась двояко: «схема хранилища v16» — это
|
||||
персистентная память ДВИЖКА (D39.158 п.1), а не Postgres платформы. Платформенная миграция этим
|
||||
абзацем не запрещена — она не нужна по пункту 1 (сумма уже персистится) и по этой причине не
|
||||
заказана. Понадобилась — тоже пинг, но по другому основанию.
|
||||
|
||||
## Свобода и её разметка
|
||||
|
||||
«Делай РОВНО так» — предусловие DSN · строка «DSN = да» у каждой посадки · границы выше · запрет на
|
||||
три вещи из порядка деплоя. Всё остальное — **решаешь сам и аргументируешь**: форма хранения бюджета,
|
||||
форма ручки снятия карантина, состав посадок, порядок работ внутри пака.
|
||||
**«Делай РОВНО так» (отступление = пинг):**
|
||||
- предусловие хоста (четыре условия) · строка «DSN = да» у каждой посадки;
|
||||
- **ФАКТ каждой из пяти правок** — что именно закрывается: (1) `reopen` читает хранимую сумму в ОБОИХ
|
||||
местах, `:1363` и `:1397`, схему платформы не трогая; (2) ключ печатается на пути ошибки;
|
||||
(3) свойство «чужая строка не карантинит нашу проекцию» с тремя оговорками пункта 3 (декод остаётся
|
||||
выше · понижение только при известном `want` · при `want == ""` валидация полная); (4) цель `check`
|
||||
печатает ВСЕ условия; (5) гейт против класса живёт в зоне платформы;
|
||||
- **обязательные посадки у пунктов 2 и 3**;
|
||||
- границы выше · три запрета из порядка деплоя · запрет трогать `docs/scripts/counts.py`.
|
||||
|
||||
**«Решаешь сам и аргументируешь» — ФОРМА и только она:** какую колонку холда читать и как быть без
|
||||
холда · форма печати ключа · форма ручки снятия карантина · форма печати условий в `Makefile` ·
|
||||
место, предикат и порог гейта пункта 5 · остальной состав посадок · порядок работ внутри пака.
|
||||
|
||||
⚠ Катч-олла «всё остальное — сам» больше нет: если чего-то нет ни в одном списке — это пинг, а не
|
||||
твоё усмотрение.
|
||||
|
||||
## Мандат самопроверки — исполнением, не перечитыванием
|
||||
|
||||
Батарея зелёная — это НЕ отчёт. Обязательны: **живая проба каждого из четырёх путей** под DSN ·
|
||||
**собственные адверсариальные посадки ВНЕ списка выше** · и **отдельный проход по СВОЕЙ ГОТОВОЙ
|
||||
работе** свежим контекстом. Субагенты разрешены явно; модель задавай ИМЕНЕМ и знай, сколько их
|
||||
работает. ⚠ Класс, купленный этой сменой: веер по исходным уликам находит втрое меньше, чем веер,
|
||||
читающий готовый результат.
|
||||
Батарея зелёная — это НЕ отчёт. Обязательны все три:
|
||||
|
||||
1. **Живая проба каждого из пяти пунктов состава.** Поимённо, с пометкой про DSN:
|
||||
- п.1 `reopen` — **DSN нужен** (денежный путь `internal/runs`, 110 тестов тёмных без него);
|
||||
- п.2 `write()` — юнит-посадки идут **без DSN**, но сквозной `TestCommandsAgainstALiveDatabase`
|
||||
в `cmd/tmplatformctl/main_test.go` **DSN-гейчен** (без DSN в `./cmd/...` скипается 6 тестов);
|
||||
- п.3 `ingest/tail.go` — **DSN не нужен**, пакет гоняется целиком (0 скипов);
|
||||
- п.4 `Makefile` — DSN не нужен, но проба обязана гоняться при РАЗНЫХ окружениях, иначе она не
|
||||
видит того самого дефекта;
|
||||
- п.5 гейт — DSN не нужен.
|
||||
2. **Собственные адверсариальные посадки ВНЕ списка выше.**
|
||||
3. **Отдельный проход по СВОЕЙ ГОТОВОЙ работе** свежим контекстом. ⚠ Класс, купленный прошлой сменой:
|
||||
веер по исходным уликам находит втрое меньше, чем веер, читающий готовый результат.
|
||||
|
||||
**Субагенты разрешены явно.** Модель задавай ИМЕНЕМ и знай, сколько их у тебя работает; обычно хватает
|
||||
одного-двух, но веер под предмет выбираешь ты.
|
||||
|
||||
### Предметные оси ревью — под характер ЭТОЙ работы
|
||||
|
||||
**(1) ШОВ — инвентарь каналов другой стороны чтением ЧУЖОГО кода, не по памяти (D39.132).** Пункт 3
|
||||
живёт целиком на шве с движком, и спроектировать порядок проверок по своей половине шва нельзя.
|
||||
Открой и прочитай: `backend/internal/runevents/` (что движок реально пишет в общий `events.jsonl`,
|
||||
словарь типов, `StreamVersion`, комментарий `Envelope` — «a resumed run is a NEW process that appends
|
||||
a second hello to the same file and numbers from 1 again») и `backend/internal/pipeline/events.go`
|
||||
(греп `hello at seq 16` — отказ `Seq != 1` наблюдался НА РЕАЛЬНОМ БИНАРЕ, 33 строки, 15 байт-в-байт
|
||||
дублей). Вопросы, на которые обязан быть ответ ЧТЕНИЕМ: с какого `seq` нумерует второй процесс ·
|
||||
какие мажоры потока сегодня возможны в одном журнале · что происходит при дозаписи. ⛔ Читаешь —
|
||||
не правишь: `backend/` чужая зона.
|
||||
|
||||
**(2) ДЕНЬГИ — пере-вывод сумм из холда первой попытки.** Не «код компилируется», а «на живой БД при
|
||||
изменённой `TM_PLATFORM_USD_PER_CHAPTER` резервируется та сумма, на которую человек согласился» —
|
||||
и по пути `Resume`, и по пути свипа, и в `AcceptRebill`.
|
||||
|
||||
### ⚠ ПРОВЕРЯЕМЫЙ АРТЕФАКТ находок — обязателен
|
||||
|
||||
Тест на вырожденность, применяй к каждой строке своего отчёта: **если её букву удовлетворяет
|
||||
НИЧТО — строка дефектна**. «Батарея зелёная», «перечитал сам», «субагент ответил: находок нет» —
|
||||
это не отчёт, это его отсутствие. В журнал идут ТРИ таблицы:
|
||||
|
||||
- **Посадки:** строка на посадку — что мутировано (`file:line` + сама мутация) · какой тест покраснел
|
||||
(ИМЕНЕМ) · `DSN = да/нет`. ⚠ Если по посадке НЕ покраснел ни один тест — так и пиши: это находка
|
||||
(дыра в покрытии), а не провал отчёта, и она сама становится строкой регистра.
|
||||
- **Живые пробы:** путь · команда · наблюдённый выход **цитатой, не пересказом** · вердикт.
|
||||
- **Веер:** на каждого агента — модель ИМЕНЕМ · что он читал · находки с `file:line`. «Находок нет»
|
||||
принимается ТОЛЬКО со списком того, что проверялось; голое «находок нет» означает, что веер не
|
||||
отработал — задание переформулируй и гоняй заново.
|
||||
|
||||
⚠ **Править или удалять тест/гейт ради зелени — НЕДОПУСТИМО.** Запрет про МОТИВ (D39.183): правка,
|
||||
вызванная сменой поведения, которая ЗАКАЗАНА этим промтом, — обслуживание, и **держать протухший тест
|
||||
не надо**; но она ОБЪЯВЛЯЕТСЯ в отчёте (что изменилось в поведении, какой тест это описывал, куда
|
||||
уехала гарантия). Несогласие с тестом — **пинг**, не правка.
|
||||
|
||||
## Отчёт
|
||||
|
||||
Записка-план ДО работы (эхо-протокол: скоуп · инварианты · не-делать) в `platform/docs/platform-PROGRESS.md`.
|
||||
Итог — туда же. Каждое число и каждая категорика — **с командой получения**: приёмка пере-ранит.
|
||||
Обязательная секция **«Что НЕ удалось и что НЕ проверено»** — «не проверено» ≠ «опровергнуто».
|
||||
Конфликт промта с кодом — **пинг через владельца, не интерпретация**; право сказать «этого делать не
|
||||
надо» с аргументом у тебя есть.
|
||||
**Записка-план ДО работы** (эхо-протокол: скоуп · инварианты · что НЕ делаю, ≤10 строк) — в
|
||||
`platform/docs/platform-PROGRESS.md`, и тем же текстом первым действием по каналу (см. ниже).
|
||||
Итог — туда же. ⛔ В `docs/PROGRESS.md` платформа НЕ пишет (канон, таблица ролей).
|
||||
|
||||
**Каждое число и каждая категорика — с КОМАНДОЙ ПОЛУЧЕНИЯ, которая это число реально печатает.**
|
||||
Приёмка пере-ранит. ⚠ Команда, печатающая «ok» вместо числа, командой получения не является — это
|
||||
ровно та ошибка, на которой споткнулась прежняя редакция этого промта.
|
||||
|
||||
Обязательная секция **«Что НЕ удалось и что НЕ проверено»**: исходов ТРИ, и «не проверено» ≠
|
||||
«опровергнуто» ≠ «сделано». Третий исход законен и ожидаем — молчание об упёршемся пункте дороже
|
||||
самого упора.
|
||||
|
||||
## Канал вопросов и право отказаться
|
||||
|
||||
**Конфликт промта с кодом — пинг через владельца, не интерпретация.** Если число выше не сошлось,
|
||||
если факт о коде не воспроизводится, если два места этого промта говорят разное — это находка, неси
|
||||
её, а не подгоняй работу под текст.
|
||||
|
||||
**Право сказать «этого делать не надо» с аргументом у тебя есть** — включая пункты состава и границы
|
||||
выше. Сессия, приносящая развилку вместо тихой девиации, делает правильно.
|
||||
|
||||
## Прямой канал
|
||||
|
||||
Механизм — `CLAUDE.md`, секция «Связь между сессиями». Впиши свой блок в `/tmp/textmachine-channel`
|
||||
первым делом; адрес роли бери оттуда, но **сверяйся с `ListAgents`: адрес из файла не доказательство,
|
||||
что сессия жива**. Нужной роли нет ⇒ **канала нет, и это НОРМАЛЬНЫЙ случай — не опрашивай сессии
|
||||
подряд**, вопрос секцией в отчёт.
|
||||
Механизм — `CLAUDE.md`, секция «Связь между сессиями». **Впиши свой блок в `/tmp/textmachine-channel`
|
||||
первым делом** (`role=` `session=` `ref=` `written=` `note=`; имя — из `ListAgents`), дописывая в
|
||||
конец и не трогая чужие блоки.
|
||||
|
||||
**Эхо-записку (скоуп · инварианты · не-делать, ≤10 строк) отправь ПЕРВЫМ действием по адресу
|
||||
оркестратора из файла — это и есть проверка канала**; в зонный журнал она идёт тем же текстом.
|
||||
|
||||
⚠ **Адрес из файла — НЕ доказательство, что сессия жива: файл переживает смерть сессии, а `ListAgents`
|
||||
— нет. Перед отправкой сверься с `ListAgents`.** В файле прямо сейчас лежат блоки закрытых смен, один
|
||||
с пометкой «⚠ СМЕНА СДАНА 31.08 … по адресу не писать».
|
||||
|
||||
Нужной роли в файле нет, файла нет или имя не отвечает ⇒ **канала нет, и это НОРМАЛЬНЫЙ случай — НЕ
|
||||
опрашивай сессии подряд**. Вопрос — секцией в отчёт; владелец прочитает и пере-передаст.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue