Issue the backend seam fix prompt: the door bricks a book on a lawful decline, and eight majors more, each with the acceptance's own repro
This commit is contained in:
parent
ff2e0061d5
commit
fa511e65b5
2 changed files with 489 additions and 1 deletions
488
docs/BACKEND_SEAM_FIX_SESSION_PROMPT.md
Normal file
488
docs/BACKEND_SEAM_FIX_SESSION_PROMPT.md
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
# Промт: БЭКЕНД ДОФИКС-ПАК «ВХОДНАЯ ДВЕРЬ ШВА» (по приёмке движковой половины D39.156)
|
||||
|
||||
> Выдан оркестратором №19, 25.08.2026. Активный промт зоны бэкенда — один; этот заменяет
|
||||
> `BACKEND_SEAM_PACK_SESSION_PROMPT.md` (отработан, уезжает в архив при лендинге).
|
||||
> Параллельно законен только `platform/docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md` (read-only, пересечений нет).
|
||||
|
||||
## 1. Какая проблема и что решит твой результат
|
||||
|
||||
Продукт — издательский перевод больших книг мультиагентным пайплайном. Его центральная ценность —
|
||||
**банк памяти**: сквозной глоссарий имён и терминов, держащий консистентность на всю книгу.
|
||||
|
||||
Предыдущий пак построил **входную дверь**: `tmctl bank-apply` принимает решения владельца о банке
|
||||
ДАННЫМИ, а не правкой YAML руками. Пак **принят не был**. Приёмка прогнала цепь целиком и
|
||||
подтвердила, что дверь работает по назначению — но нашла **один блокер и восемь мажоров**, и главный
|
||||
из них превращает дверь в свою противоположность: **один законный отказ пользователя необратимо
|
||||
ломает книгу так, что чинить её можно только тем самым текстовым редактором, ради отмены которого
|
||||
дверь и строилась.**
|
||||
|
||||
Твой результат — дверь, которую можно отдать пользователю. Пак не переделывается: он хороший,
|
||||
дыры узкие и адресные. Всё ниже — правки в уже написанном коде плюс пины к местам, где гейт
|
||||
оказался слепым.
|
||||
|
||||
⚠ **Дерево пака НЕ закоммичено и лежит в рабочем каталоге.** Ты продолжаешь его, а не начинаешь заново.
|
||||
|
||||
## 2. Зона записи и git
|
||||
|
||||
Пишешь ТОЛЬКО в `backend/`. Читать можешь всё; `platform/`, `frontend/`, `eval/`, `docs/` — чужие зоны,
|
||||
правки в них запрещены. **Единственное исключение:** пинги и итог сессии — в `docs/PROGRESS.md`, секция
|
||||
«Бэкенд». **Ты не коммитишь** — дерево готовишь и передаёшь на лендинг оркестратору (канон git-норм —
|
||||
`CLAUDE.md`). `git add -A`, `git add .`, `git commit -a` запрещены в любом случае. В дереве живёт
|
||||
незакоммиченная работа параллельных сессий — не трогай её и не «прибирай».
|
||||
|
||||
## 3. Карта чтения (пять позиций, больше не нужно)
|
||||
|
||||
1. **`backend/docs/SEAM_PACK_FINDINGS.md`** — доказательная база твоего же пака. Читать §0 (состав),
|
||||
§5 (закрытые дефекты — чтобы не чинить дважды), **§6 (класс полосы 14 — статус ратификации, см. ниже)**,
|
||||
§7 (что НЕ доказано), §9 (два остаточных риска), §10 (семь пингов).
|
||||
⚠ **Что из них стало заказом, точно:** §9 — ВТОРОЙ риск (кап размера документа) стал §4.5; ПЕРВЫЙ
|
||||
(необратимость канонического пере-рендера) заказом НЕ стал и остаётся риском, он получает только
|
||||
пин на поле-смягчение (§4.11 б) и уточнение точности этого поля (§4.13). §10 — заказом стала
|
||||
ПОЛОВИНА п.3 (текст отказа, §4.14); остальные шесть перечислены в приложении «пинги, которые
|
||||
дофикс НЕ исполняет». Не ищи в промте заказов, которых там нет.
|
||||
⚠ **Статус класса 14:** заводить его законно и это уже сделано — ратифицированный закон обещает,
|
||||
что двери встают паками, а прецедент кода 13 прошёл ровно так (направление → финализация приёмкой).
|
||||
Ноту и подъём обоих ратифицированных носителей полосы ставит ОРКЕСТРАТОР при лендинге; ты работаешь
|
||||
на классе 14 как на действующем и §4.11 (в) строишь на нём.
|
||||
2. **`docs/architecture/17-seam-inbound-law.md`** — ратифицированный закон входной двери, семь пунктов.
|
||||
Твой контракт. Пункты 1, 2, 3 и 7 ты трогаешь напрямую.
|
||||
3. **`docs/architecture/05-decisions-log.md`, нота D39.156** — почему закон такой.
|
||||
4. **`docs/PROGRESS.md` — строка БЭКЛОГА 199** ⚠ это ID строки ТАБЛИЦЫ, брать грепом `^| 199 |`;
|
||||
буквальный `sed -n '199p'` даст чужой текст. В её теле — ТРИ мины формата; мины 1 и 2 пак
|
||||
обезвредил, **мина 3 (кап реверс-секции 200 ПОСЛЕ фильтров) не обезврежена и попадает в §4.9**.
|
||||
5. **`docs/architecture/12-go-style-notes.md`** — норматив общности. Ревью-вопрос по умолчанию:
|
||||
«заработает ли на паре, которой в репо ещё НЕТ, без правки Go?»
|
||||
|
||||
**Код первичен.** Все `file:line` ниже — отправные точки, снятые приёмкой на дереве пака; открывай и
|
||||
проверяй сам (дисклеймер о происхождении доков — шапка `CLAUDE.md`).
|
||||
|
||||
## 4. Состав дофикса
|
||||
|
||||
Каждый пункт несёт **как приёмка это показала** — воспроизведи сам прежде, чем чинить: правка без
|
||||
воспроизведённого репро лечит не то.
|
||||
|
||||
### 4.1 ⛔ БЛОКЕР: reject-документ не проверяется на читаемость вообще — ДЕЛАЙ РОВНО ТАК инвариант, РЕШИ САМ форму
|
||||
|
||||
`documentProblems` (`internal/membank/decisions.go`) рендерит и парсит обратно **только дельту**.
|
||||
`res.Rejects` не участвует ни в одной проверке.
|
||||
|
||||
**Репро (воспроизвести первым делом):** решение `{"action":"decline","src":"方小子","note":"\nне термин"}` —
|
||||
`note` начинается с перевода строки, что для свободного текста пользователя абсолютно законно.
|
||||
Вызов принимается: `exit 0`, `changed=true`, `state: applied`, `rejected: []`. Записанные байты:
|
||||
|
||||
```
|
||||
rejects:
|
||||
- src: 方小子
|
||||
note: |4-
|
||||
не термин
|
||||
```
|
||||
|
||||
`seed.DecodeRejects` их не читает: `seed: yaml: line 1: did not find expected key`. После этого **любой**
|
||||
следующий `bank-apply` по книге — включая `--dry-run`, который закон п.7 объявляет безопасной
|
||||
проекцией, — падает с `exit 10`. Тот же `note` на `approve` дверь **отвергает** (`exit 14`, ничего не
|
||||
записано): механизм существует и просто не применён ко второму файлу.
|
||||
|
||||
**Заказ:** результат, который дверь СОБИРАЕТСЯ записать, обязан пройти round-trip `render → parse`
|
||||
**по обоим документам**, и провал этой проверки — отказ, а не запись. Форму (расширить
|
||||
`documentProblems` вторым аргументом, отдельная функция, что угодно) решаешь сам и аргументируешь.
|
||||
|
||||
⚠ **Корень глубже, и его тоже закрой:** `yaml.Marshal` рендерит строку с ведущим `\n` как блочный
|
||||
скаляр с индикатором отступа (`|4-`), который его же строгий декодер не принимает. Это бьёт по
|
||||
ЛЮБОМУ свободному текстовому полю обоих документов, а не только по `note` у `decline`. Решение о том,
|
||||
нормализовать ли такой вход на входе двери или чинить рендер, — твоё, но оно обязано быть общим
|
||||
для всех текстовых полей, а не заплаткой на одно поле.
|
||||
⚠ И прежде чем чинить: **это поведение библиотеки — сходи в её доку и трекер, не гадай** (гардрейл
|
||||
проекта на аномалии зависимостей). Если это её известный дефект — обход законен и должен нести
|
||||
ссылку; если наше злоупотребление — чини вызов. Формулировку «похоже, баг yaml» без сверки в
|
||||
комментарий не абсорбировать.
|
||||
⚠ **Фаззер здесь сильнее примера, и его слепота СТРУКТУРНА — вот она.** Оракул
|
||||
`FuzzSeedDocumentRoundTrip` («движок не должен рендерить документ, который сам не читает») **исправен**:
|
||||
приёмка досеяла один вход с ведущим `\n` в `note`, и он падает мгновенно. Не сработал он потому, что
|
||||
фаззится СЫРОЙ YAML — то есть корпус по построению состоит из строк, УЖЕ переживших YAML-разбор, а
|
||||
настоящий вход двери это JSON-строка, на которую YAML не накладывает никаких ограничений. Ни один из
|
||||
семи сидов не несёт многострочного значения вовсе.
|
||||
**Заказ:** фаззить путь ДВЕРИ — произвольные строки полей `Decision` через `ApplyDecisions` в рендер и
|
||||
обратно, — и оракул «что движок ЗАПИСЫВАЕТ, движок ЧИТАЕТ ОБРАТНО» распространить на ОБА документа.
|
||||
|
||||
### 4.2 Пред-существующие поломки нельзя чинить по одной — ДЕЛАЙ РОВНО ТАК
|
||||
|
||||
Гейт «отказывают только НОВЫЕ проблемы» сравнивает **тексты сообщений** (`known[p]`), а вердикт
|
||||
загружаемости приходит ОДНОЙ строкой, несущей ВСЕ битые термы сразу: `ParseBankSeed` копит
|
||||
`problems` по каждому терму и склеивает их в одну ошибку, а `documentProblems` сворачивает эту
|
||||
ошибку в один элемент `[]string`. Поэтому починка одного терма меняет строку ЦЕЛИКОМ, совпадения с
|
||||
`known` больше нет, и вызов отказывает.
|
||||
|
||||
⚠ **Это НЕ «загрузчик падает на первой строке» — прежняя редакция промта утверждала так и была
|
||||
неверна (испр. по опровергателю промта).** Разница несущая: чинить надо разложение
|
||||
многосубъектного вердикта на проблемы ПО СУБЪЕКТАМ, а не накопление ошибок в общем загрузчике сида —
|
||||
он уже накапливает, и он читает `glossary_seed` КАЖДОЙ книги.
|
||||
|
||||
**Репро:** дельта с двумя строками `status: approved, dst: ""`. Решение чинит одну → **`exit 14`**,
|
||||
и текст отказа называет ВТОРУЮ строку, которой решение не касалось. Обе одним документом → `exit 0`.
|
||||
|
||||
Это прямо опровергает собственный контракт двери (комментарий поля `Preexisting`: «a book whose
|
||||
files already contradict each other has to stay repairable through this door»).
|
||||
|
||||
**Заказ:** пред-существующая проблема опознаётся по СУБЪЕКТУ (терм/поверхность, которой она
|
||||
касается), а не по тексту сообщения. Проверено приёмкой: несвязанное решение при одной
|
||||
пред-существующей поломке проходит корректно, ломается именно инкрементальная починка НЕСКОЛЬКИХ.
|
||||
|
||||
### 4.3 Два ключа на один путь теряют решение молча — ДЕЛАЙ РОВНО ТАК
|
||||
|
||||
**Репро (воспроизведено приёмкой):** `book.yaml`, объявляющий `mined_delta` и `mined_rejects` на ОДИН
|
||||
путь, файл при этом пустой — то есть парсится обеими схемами. Результат: `exit 0`, оба решения
|
||||
`applied`, а на диске остаётся только reject-лист.
|
||||
Одобрение владельца исчезает молча, и файл после этого не грузится как дельта — следующий прогон
|
||||
падает на разборе конфига.
|
||||
|
||||
**Заказ:** `config.LoadBook` отказывает, если два пути совпадают. Одна проверка плюс строка отказа.
|
||||
|
||||
### 4.4 Гард флага ключён на ЗНАЧЕНИЕ, а не на присутствие — ДЕЛАЙ РОВНО ТАК
|
||||
|
||||
`invocation.go`: `if *decisions != "" && cmd != "bank-apply"`. Поэтому `--decisions=` с пустым
|
||||
значением гарда не задевает.
|
||||
|
||||
**Репро:** `tmctl translate --config b.yaml --decisions=` идёт по ПЛАТНОМУ пути ровно как без флага
|
||||
(проверено: обе формы падают на одной и той же поздней стадии). Реальный триггер — `--decisions=$DOC`
|
||||
с незаданной переменной в юните или скрипте. Это ровно класс R2 твоего же §5, который ты назвал
|
||||
«стоил бы ДЕНЕГ», уцелевший в одной форме.
|
||||
|
||||
**Заказ:** оба гарда (`--decisions`, `--keys-file`) ключить на ПРИСУТСТВИЕ флага, не на непустоту
|
||||
значения. Пустое значение у присутствующего флага — тоже отказ.
|
||||
⚠ Присутствие даёт **только `fs.Visit`** — он обходит ТОЛЬКО реально переданные флаги; `fs.Lookup`
|
||||
на этот вопрос не отвечает вовсе, он возвращает ОБЪЯВЛЕННЫЙ флаг независимо от того, передавали его
|
||||
или нет. Живой прецедент лежит в том же файле: `ceilingGiven` через `fs.Visit`, с комментарием,
|
||||
объясняющим ровно эту разницу. ⚠ Комментарий над самими гардами УЖЕ говорит «scoped to the flag
|
||||
being PRESENT» — то есть код расходится со своим же комментарием, и это дополнительная улика.
|
||||
|
||||
### 4.5 Документ решений не капирован, а OOM движка платформа читает как успех — ДЕЛАЙ РОВНО ТАК инвариант, РЕШИ САМ число
|
||||
|
||||
`os.ReadFile(decisionsPath)` без ограничения. Замерено приёмкой на документах из одних `approve`:
|
||||
20 000 решений → 0.73–0.80 ГБ RSS, 40 000 → 1.42 ГБ при документе всего в 2 МБ, то есть ~36 КБ
|
||||
памяти на решение. Разброс на 20 000 — два прогона документов слегка разной формы (с `note` и без),
|
||||
а не две разные оси.
|
||||
|
||||
⚠ **И вторая половина, которой в §9 не было:** движок при нехватке памяти выходит **кодом 2**
|
||||
(`fatal error: ... out of memory` — Go так завершается), а платформа читает 2 как
|
||||
`CompletedWithFlags` — «команда СДЕЛАЛА свою работу, часть юнитов на человека»
|
||||
(`platform/internal/ingest/exit.go`, читать, не править). То есть смерть движка от памяти
|
||||
прочитывается как успешный прогон. Класс пред-существующий и общий для всех глаголов — но
|
||||
`bank-apply` первый, чей размер входа задаёт ПОЛЬЗОВАТЕЛЬ.
|
||||
|
||||
**Заказ:** кап на размер документа решений плюс внятная строка отказа полосой. Число выбираешь сам
|
||||
и аргументируешь; ориентир — платформа капирует свои чтения от движка (16 МБ статус / 256 МБ банк).
|
||||
Хвост про exit 2 — **пингом**, не тихой правкой: он общедвижковый и не твой скоуп.
|
||||
|
||||
### 4.6 `artifacts{}` не доезжает до своего потребителя — РЕШИ САМ форму
|
||||
|
||||
Проверено приёмкой грепом: `Artifacts` существует **только** в `internal/pipeline/status.go`, а
|
||||
единственный код платформы, выводящий движковый путь (`runner.ReadBank` → `projectDB` →
|
||||
`filepath.Join(workdir, book_id+".db")` + суффикс `.bank.json`), зовётся из её read-model, чей
|
||||
интерфейс движка — `{Manifest, Export}`, без `Status` (`platform/internal/readmodel/`, читать, не править).
|
||||
|
||||
Буква §4.5 прежнего заказа исполнена, **цель строки бэклога 213 — нет**: платформа не может выкинуть
|
||||
свой `projectDB()`, не заведя третий вызов движка на каждую материализацию.
|
||||
|
||||
**Заказ:** тот же версионированный конверт путей отдать на поверхности, которую потребитель уже
|
||||
читает. `manifest --json` — самый дешёвый кандидат (read-model уже его гоняет), но форму и состав
|
||||
выбираешь ты и аргументируешь. ⚠ Пере-именование банк-экспорта в фикс-имя по-прежнему ЛОМАЮЩЕЕ,
|
||||
его окно — строка 161, сюда не тащить.
|
||||
|
||||
⚠ **Твой же пинг §10 п.5** («опубликованный путь читается как приглашение его парсить; единственная
|
||||
изгородь — комментарий») этим углубляется, и это осознанно: закон п.1 разрешает платформе читать по
|
||||
путям, СООБЩЁННЫМ движком, а п.3 требует версионированного конверта на каждом выходе. Изгородью
|
||||
становится конверт, а не комментарий. Если считаешь, что этого мало, — скажи аргументом, это законный
|
||||
ответ по §11.
|
||||
|
||||
### 4.7 Отказ по алиасу терма, одобренного в ТОМ ЖЕ вызове, принимается инертным — ДЕЛАЙ РОВНО ТАК
|
||||
|
||||
Проверка `aliasOwner` смотрит в `res.Delta` ДО цикла применения, поэтому терм, одобряемый решением
|
||||
`i`, ещё не лежит в дельте, когда судится решение `j`.
|
||||
|
||||
**Репро:** один документ = `approve 方源` + `decline 方小子`, где `方小子` — алиас кластера майнера у
|
||||
`方源`. Оба отчитываются `applied`, `rejected: []`. В дельте `方小子` остаётся ФАЙРЯЩИМ алиасом
|
||||
одобренного терма, а reject — только фильтр предложений. Прозвище продолжает подставляться в
|
||||
переводе, владелец уверен, что снял его. Те же два решения ДВУМЯ вызовами — корректно отвергаются
|
||||
по имени.
|
||||
|
||||
**Заказ:** правило судит НАБОР, как и заявлено в шапке `ApplyDecisions`, а не только состояние файла
|
||||
до вызова.
|
||||
|
||||
### 4.8 Перевёрнутое окно глав принимается — ДЕЛАЙ РОВНО ТАК
|
||||
|
||||
`resolveDecision` проверяет только `< 0`. **Воспроизведено приёмкой:** решение с
|
||||
`since_chapter: 90, until_chapter: 3` принимается и записывает в дельту строку, которая не сработает
|
||||
ни в одной главе. Отказывать.
|
||||
|
||||
### 4.9 Мина 3 строки 199: «применено» ≠ «стоп снимется» — РЕШИ САМ форму
|
||||
|
||||
`reverseSectionTerms` режет кап 200 ПОСЛЕ фильтров, поэтому на книге с >200 banknote-only
|
||||
поверхностями подпись всей карты стоп НЕ снимет: приедет свежая двухсотка, и актов подписи
|
||||
понадобится `ceil(N/200)`.
|
||||
|
||||
Механизм против этого паком не заказан и сейчас не заказывается. **Заказывается ЧЕСТНОСТЬ отчёта:**
|
||||
вызов, после которого стоп заведомо не погаснет, не должен читаться как «готово». Форму (поле в
|
||||
отчёте, строка предупреждения, что-то ещё) решаешь сам и аргументируешь; если считаешь, что дверь
|
||||
этого знать не может — скажи это аргументом, и это будет принято.
|
||||
|
||||
### 4.10 SIGTERM игнорируется, лок держится — РЕШИ САМ форму
|
||||
|
||||
Проверено приёмкой: на длинном документе процесс переживает `SIGTERM` и продолжает держать
|
||||
эксклюзивный лок проекта; снимается только `SIGKILL`. Платформа останавливает движок именно
|
||||
`SIGTERM` (`platform/internal/runner/`, читать, не править), а лок блокирует прогон книги.
|
||||
|
||||
Закон п.2 отводит `SIGTERM` роль «дочти и отпусти» для ПРОГОНА; для $0-глагола аналог — «брось
|
||||
работу и отпусти лок, ничего не записав»: у всё-или-ничего нет частичного результата, который стоило
|
||||
бы дописывать. Форму решаешь сам.
|
||||
|
||||
### 4.11 Восемь слепых пятен гейта, десять мутаций — ДЕЛАЙ РОВНО ТАК
|
||||
|
||||
Каждое ниже — место, где приёмка посадила мутацию и **батарея осталась зелёной**. Живого дефекта в
|
||||
них нет; отсутствует пин. ⚠ Считай по МУТАЦИЯМ, а не по строкам таблицы: строка (д) несёт три
|
||||
независимые посадки, и пин нужен на каждую. Каждый пин обязан краснеть на своей мутации — покажи это
|
||||
в отчёте прогоном.
|
||||
|
||||
| № | что не сторожится | мутация, которую пин обязан поймать |
|
||||
|---|---|---|
|
||||
| а | **арбитраж лока против ПРОГОНА.** `TestBankApplyRefusesABusyProject` держит лок тем же `store.LockProject`, поэтому проверяет «LockProject конфликтует с LockProject». Инвариант ратифицирован (закон п.2, «тот же flock проекта») | сменить файл лока в `LockProject` (`dbPath + ".lock"` → любой другой) — сегодня батарея зелёная. Пин обязан держать лок так, как его держит ПРОГОН (`store.Open`) |
|
||||
| б | **`canonical_rewrite` и `preexisting_problems`** — два опубликованных поля отчёта, на которых пак держит смягчение двух СВОИХ ЖЕ названных рисков (§9 необратимый пере-рендер, R7 проглоченная поломка) | занулить оба (`rep.CanonicalRewrite = false`, `PreexistingProblems: []string{}`) — сегодня батарея зелёная |
|
||||
| в | **гейт тотальности таблицы отказов слеп к сценарию, который сам называет.** `TestTheRefusalTableIsTotal` держит РУЧНОЙ список классов и сверяет его длину с длиной `refusalExit` | добавить шестой класс в `pipeline.RefusalClass`, не внося его НИ в `refusalExit`, НИ в список теста — сегодня тест ПРОХОДИТ, а класс уехал бы в catch-all 19. Перечисляй классы из ИСХОДНИКА. ⚠ `TestDispatchCommandsCoversTheSwitch` — прецедент ТЕХНИКИ, но не реализации: он ключён на `*ast.BasicLit` и потому слеп к `case` с константой (это его собственный пинг, §10 п.2 отчёта), а значения `RefusalClass` объявлены именно КОНСТАНТАМИ — копирование один-в-один воспроизведёт слепоту. Нужен обход `GenDecl`/`ValueSpec` |
|
||||
| г | **нормализация в REPLACEMENT-половине отказа.** Тесты используют поверхности, на которых `NormalizeSourceKey` — тождество | `nk := text.NormalizeSourceKey(src)` → `nk := src` в `dropTerms`. Пин: одобрен 虫, отказ приходит по 蟲 (традиционная форма) — отказ обязан снять одобрение |
|
||||
| д | **глубокое копирование алиасов в `withRubyAliases`** — ровно класс R4/R5, ради которого поле `Ruby` заведено; **перенос `note` при промоушене**; **рантайм-половина гарда ОБЪЯВЛЕННОГО пути** (`decisionFilePresent(path, declared=true)`) | снять глубокую копию · `if false { t.Note = d.Note }` · `case !declared && ...` → `case ...` — все три сегодня зелены |
|
||||
| е | **абсолютность путей в `artifacts{}` запинена ВАКУУМНО.** Проверка `filepath.IsAbs` в `statusartifacts_test.go` есть, но фикстура даёт уже абсолютный `--config`, поэтому `absPath` в ней ничего не делает и его удаление не краснит. Замерено: с ОТНОСИТЕЛЬНЫМ `--config` `absPath` — несущий, и без него потребитель в другом рабочем каталоге прочитает не тот файл | убрать `absPath` у любого из четырёх путей — сегодня батарея зелёная. Пин обязан звать глагол с относительным `--config` |
|
||||
| ж | **байтовый гейт `changedDoc` не запинен**: мутация `deltaChanged := res.DeltaTouched` (байтовый гейт снят целиком) оставляет и `TestBankApplyRepeatIsAByteNoOp`, и `TestBankApplyWritesTheConventionalFiles` зелёными. Контракт «байтовый no-op» держится сегодня только на `*Touched` из чистого слоя — второй рубеж не защищён | снять байтовое сравнение в `changedDoc` |
|
||||
| з | **правило «файлы решений едут С КНИГОЙ, а не с `project_db`»** не запинено: конвенционный дефолт можно перевесить на каталог `project_db`, и батарея зелёная — во всех тестах два каталога совпадают. Правило несущее: `project_db` может указывать куда угодно, а решения владельца обязаны ехать с книгой в бэкапе и экспорте каталога | перевесить дефолт на `filepath.Dir(b.ProjectDB)`; пин обязан развести два каталога |
|
||||
|
||||
⚠ Уже существующие пины, которые тоже придётся починить, вынесены отдельным номером — **§4.15**.
|
||||
|
||||
### 4.12 Цепь §8 — ЗАКРЫТА ПРИЁМКОЙ, забери её в репозиторий — ДЕЛАЙ РОВНО ТАК
|
||||
|
||||
Твой §8 просил приёмку прогнать цепь целиком и объяснял, что своими силами это невозможно: «до стопа
|
||||
надо дойти ОПЛАЧЕННОЙ черновой волной». **Довод оказался шире правды.** В репозитории уже есть
|
||||
харнесс, гоняющий тот же банк-стоп против ФЕЙКОВОГО провайдера за $0 —
|
||||
`internal/pipeline/miningstop_join_test.go`: `setupMiningStopProject` + `newJSONProvider` +
|
||||
`newVerifyRunner` + `runToSignatureStop` + `readSignatureMap`, на них стоит ~30 живых тестов.
|
||||
|
||||
Приёмка написала и прогнала цепь целиком, и она **работает**: стоп на 2 термах → `bank-apply`
|
||||
промотит dst-несущий и отклоняет WHICH-only → резюм → «empty delta, auto-continuing to the edit wave»,
|
||||
редакторская волна отработала. Мина 1 строки 199 (ливлок на `status: auto`) не сработала.
|
||||
|
||||
**Заказ: этот тест обязан жить в репозитории**, потому что он — единственный, кто видит ливлок
|
||||
уровня ЦЕПИ, которого частичные тесты по построению не видят. Собери его сам по описанию выше:
|
||||
дойти до стопа → разобрать карту подписи `seed.DecodeFile` → построить документ, где термы с `dst`
|
||||
идут в `approve`, а WHICH-only в `decline` → `ApplyBankDecisions` → пере-запустить прогон → убедиться,
|
||||
что стоп ПОГАС и редакторская волна пошла.
|
||||
|
||||
**Как понять, что собрал верно.** На фикстуре без опций стоп приходит на ДВУХ термах — один с
|
||||
`dst`, один WHICH-only, — то есть документ решений выходит «1 approve + 1 decline», `bank-apply`
|
||||
отвечает `changed=true`, `accepted=2`, а файлы у него КОНВЕНЦИОННЫЕ (ключей фикстура не объявляет).
|
||||
На резюме движок пишет в журнал `skipped_as_declined=1 emitted_by_miner=0` и «empty delta,
|
||||
auto-continuing to the edit wave», после чего редакторская волна отрабатывает. Если у тебя иначе —
|
||||
собрано не то.
|
||||
|
||||
⚠ **Ограничение назови в комментарии теста сам:** синтетический контраст меняет, КАКИЕ кандидаты
|
||||
предложены, но не то, проходит ли подписанная строка фильтр подписи. Для вопроса «гаснет ли стоп»
|
||||
это корректно; для замера качества майнера — нет.
|
||||
|
||||
⚠ Соседний факт, поймай его при написании: `TestMiningStopRejectClearsDelta` заканчивается
|
||||
проверкой «погасший стоп не оставляет карту подписи», но использует ВТОРОЙ, свежий проект, где карта
|
||||
никогда не писалась, — проверка почти вакуумна. На ОДНОЙ книге карта остаётся на диске: движок её
|
||||
нигде не удаляет (`os.Remove` в `mining.go` не встречается). Поведение пред-существующее, паком не
|
||||
введено — **пинг, не правка**, но пусть твой тест это зафиксирует явно.
|
||||
|
||||
### 4.13 Мелочи — РЕШИ САМ, чинить или отклонить с аргументом
|
||||
|
||||
- **`canonical_rewrite` — ИЛИ по двум файлам**, поэтому предупреждает про файл, который вызов не
|
||||
откроет (замерено: на книге, чьи оба файла записал движок, флаг корректно `false`; горит, когда
|
||||
комментарии оператора несёт reject-лист, а решение трогает только дельту). Потребитель рисует
|
||||
предупреждение не про то.
|
||||
- **`note` нельзя ОЧИСТИТЬ**: повторное решение без `note` сохраняет прежнюю заметку. Заказом не
|
||||
оговорено ни в ту, ни в другую сторону — назови поведение решением, каким бы оно ни было.
|
||||
- **`book_id` не проверяется на путевую безопасность**, а путь строится ДО валидации: `book_id: ../x`
|
||||
уводит оба файла решений выше каталога книги. Класс пред-существующий (`<book_id>.db`), пак
|
||||
расширил его с одного файла до трёх. На SaaS `book_id` генерируется платформой, то есть
|
||||
пользователю недостижим.
|
||||
- **Каталог книги без права записи → `exit 1`, ВНЕ полосы отказов** (`store: ... attempt to write a
|
||||
readonly database`). Платформа спишет проблему прав ХОСТА в бюджет попыток КНИГИ. Класс
|
||||
пред-существующий (`store.Open` ведёт себя так же), новый глагол его унаследовал.
|
||||
- **Проекция создаёт `<project_db>.lock`** в каталоге книги. Требование §4.2 «проекция БЕЗ мутации»
|
||||
держится (файлы решений не тронуты), но «не трогает ничего» буквально неверно.
|
||||
- **Порядок двух записей пессимален для `decline`.** Комментарий обосновывает «дельта первой» тем,
|
||||
что крах между записями оставит промоушен без снятого отказа — верно для `approve`. Для `decline`
|
||||
тот же порядок теряет решение ЦЕЛИКОМ: дельта переписана (одобрение снято), reject не записан.
|
||||
⚠ **Приёмка это НЕ воспроизвела** — атомарная замена обходит права на файл, а настоящее окно
|
||||
микросекундное; повтор вызова при этом сходится. Утверждение остаётся рассуждением, не замером:
|
||||
реши сам, стоит ли оно правки, и аргументируй.
|
||||
- **`artifacts.bank_export` печатается безусловно**, в том числе для книги, где файла ещё нет
|
||||
(проверено на копии стенда). Читатель платформы это переживает (обычная ошибка открытия), но поле
|
||||
именует МЕСТО, а не наличие — реши, достаточно ли этого, и назови решение.
|
||||
- **`aliasOwner` возвращается на ПЕРВОМ совпадении**, поэтому исход `decline` в теории зависит от
|
||||
порядка строк дельты, если поверхность одновременно алиас одной строки и `src` другой.
|
||||
⚠ Такой документ уже ловится `ApprovedSharedKeyCollisions`, и приёмка значимого расхождения
|
||||
**воспроизвести не смогла** — это гипотеза, а не находка. Отклонить с аргументом законно.
|
||||
- **Проекция отвечает «что применится», а не «почём».** Закон п.7 звучит как «почём ДО
|
||||
подтверждения», прежний заказ денег в проекции не требовал, а смета уже едет в `status --json`.
|
||||
Это вопрос ДИСПОЗИЦИИ, а не дефект исполнения: **не чини, а вынеси пингом** со своей рекомендацией —
|
||||
решать оркестратору.
|
||||
|
||||
### 4.14 Довод отказа `--keys-file` не покрывает всех, кого отвергает — ДЕЛАЙ РОВНО ТАК
|
||||
|
||||
Текст отказа (`cmd/tmctl/invocation.go`) звучит так: «`--keys-file` принимает только `translate`, не
|
||||
%q: **$0-читающие команды (report/status/export/manifest/seed-lint)** не должны требовать ключей вовсе
|
||||
(D20.4)». В ту же ветку попадают `redrive` (**платный** путь: «only a real redrive re-attacks and
|
||||
re-bills»), `backup` и `migrate` (деплой-шаги). Довод их не описывает, и оператор, получивший этот
|
||||
отказ на `redrive`, читает про себя неправду.
|
||||
|
||||
**Заказ:** довод обязан покрывать всех, кого ветка отвергает. Одна строка.
|
||||
⚠ Саму развилку «нужен ли `redrive` канал ключей аргументом» **НЕ решай** — прежний промт сказал
|
||||
«флаг только у `translate`», и это исполнено верно; развилка остаётся пингом.
|
||||
|
||||
### 4.15 Три уже существующих пина тавтологичны или узки — ДЕЛАЙ РОВНО ТАК
|
||||
|
||||
Вынесено отдельным номером, чтобы механическая сверка §7 их видела.
|
||||
|
||||
- **Вердикт `Preexisting` исполняется одной проверкой из четырёх:**
|
||||
`TestAPreExistingCollisionDoesNotBlockAnUnrelatedDecision` гоняет только
|
||||
`ApprovedSharedKeyCollisions`; `MinedDeltaSeedCollisions`, `GenderVocabViolations` и
|
||||
`UnknownVoiceCharacters` не исполнены ни одним тестом.
|
||||
- **Четвёртый оракул `FuzzDecisionDocument`** пере-выполняет ровно то, что `documentProblems` уже
|
||||
выполнила внутри `ApplyDecisions`, и заперт под `len(res.Rejected) == 0` — тавтология.
|
||||
- **`TestDecisionByBankID`** вычисляет ожидаемый id той же функцией, которую проверяет, поэтому не
|
||||
видит потери инъективности — свойства, ради которого в `TermID` и сделана преамбула про
|
||||
length-prefix. Сверяйся с независимо посчитанным значением, а не с самой функцией.
|
||||
|
||||
### 4.16 Деньги: платных вызовов в паке НОЛЬ — ДЕЛАЙ РОВНО ТАК
|
||||
|
||||
Санкции на платные вызовы нет и не запрашивается. Всё выше проверяется юнит-тестами, фейковым
|
||||
провайдером и пробой на копии книги стенда. Упёрся в место, где без денег не доказать, — **пинг с
|
||||
названной суммой, не трата**. Рантайм-вердикт по платному пути остаётся PLAUSIBLE — это ожидаемый
|
||||
исход, а не дефект отчёта; так и напиши.
|
||||
|
||||
## 5. Мандат самопроверки ИСПОЛНЕНИЕМ
|
||||
|
||||
Не «перечитал» — **исполнил**. Минимум:
|
||||
|
||||
- **Батарея целиком, ИЗ КАТАЛОГА `backend/`**, после каждой содержательной правки: `go build ./...`,
|
||||
`go vet ./...`, `go test ./... -count=1`, **`make lint`**. ⚠ `go.mod` в корне репозитория НЕТ — модули раздельные.
|
||||
⚠ **И `make battery-stand` — обязательно, минимум один раз в конце.** Пак владеет `internal/standdata`
|
||||
(тест-резолв корпуса), а регрессию в резолвере обычная батарея не ловит по построению: без корпуса
|
||||
эти тесты просто скипаются. Ожидаемая картина — всё зелено, кроме `TestMinerFullBookParity`, красного
|
||||
из-за отсутствующего jieba-контраста (он лежит по внутрирепозиторному пути, но вне git по построению —
|
||||
клон его не получает; это строка бэклога 123, не твой дефект).
|
||||
Красный тест не «подгоняется»: править или удалять тест, голден или гейт ради зелени —
|
||||
НЕДОПУСТИМО; несогласие с тестом — пинг оркестратору, а не правка.
|
||||
⚠ **В `internal/store` живут два пред-существующих `-race`-флейка** —
|
||||
`TestTheSeamIsNotChargedToTheStoreOperationBudget` и `TestKillMinus9LosesAtMostOneCall`. Приёмка
|
||||
доказала тремя независимыми путями, что они не от пака: правка `store.go` чисто аддитивна, покрытие
|
||||
новых функций под обоими тестами ноль, и чередующийся A/B против дерева без пака даёт ту же
|
||||
частоту. Оба меряют ВРЕМЯ и чувствительны к нагрузке. Красное ИМЕННО в них — шум; красное в любом
|
||||
другом тесте — регрессия.
|
||||
- **Собственные адверсариальные посадки ВНЕ списка §4.11.** Десять названных там — обязательный
|
||||
минимум, а не потолок: посади свои в новый код и покажи, что батарея краснеет.
|
||||
⚠ **Посадка обязана называть ПАКЕТ, в котором ищется пин** — не тот, где лежит правка: ложное
|
||||
«мутация выжила» опаснее пропущенной, потому что выглядит как результат.
|
||||
⚠ **Харнесс мутаций обязан снимать отпечаток целей до и после** — твой же §11 п.1 объясняет цену.
|
||||
- **Живая проба на копии книги стенда** (`<репозиторий>/books/gu-zhenren/*`). Работать в КОПИИ вне
|
||||
рабочего дерева; правки в `books/` не делать вовсе — это отдельный git-репозиторий и не твоя зона.
|
||||
- **Субагенты РАЗРЕШЕНЫ явно** — на ревью своего кода, на поиск дефектов вне твоей карты, на
|
||||
опровержение твоих же выводов. Дефолт-запрет харнесса иначе тихо победит. ⚠ Соразмеряй параллельность с ресурсами:
|
||||
субагенты, одновременно гоняющие полную батарею, топят друг друга, и упавший от нагрузки агент
|
||||
выглядит как отсутствие находок.
|
||||
- **Артефакт-находки обязателен:** дописывай `backend/docs/SEAM_PACK_FINDINGS.md` новой секцией
|
||||
(не переписывай старые — они улика приёмки), что посадил, что краснело, что выжило.
|
||||
|
||||
### 5.1 Обязательное для кодового пака
|
||||
|
||||
- **Последний абзац отчёта — это план или обещание? Значит сделай его СЕЙЧАС.**
|
||||
- **Дифф `^func Test` — исполнением, а не по памяти:** покажи командой, какие тесты добавлены и тронуты.
|
||||
- **Интервальная самоверификация субагентом** против ЯВНЫХ критериев в середине работы.
|
||||
- **Перед отчётом сверь каждый клейм с результатом инструмента ЭТОЙ сессии.**
|
||||
|
||||
## 6. Оси ревью (три, по характеру работы)
|
||||
|
||||
1. **Round-trip формата** — главная ось этого дофикса: всё, что дверь ЗАПИСЫВАЕТ, она обязана
|
||||
ПРОЧИТАТЬ обратно, на обоих документах и на всех текстовых полях. Фаззер здесь сильнее примера.
|
||||
2. **Слепота гейта** — для каждой правки: какой мутацией она сторожится и краснеет ли пин НА САМОМ
|
||||
ДЕЛЕ. Восемь мест §4.11 — это там, где гейт уже оказался слепым; не заведи девятое.
|
||||
3. **Общность** — заработает ли на паре, которой в репо нет, без правки Go. Приёмка проверила дверь
|
||||
на `en→de` и `ja→en` и нашла её чистой; твои правки не должны это сломать.
|
||||
|
||||
Ты вправе ЗАМЕНИТЬ любую ось своей, если аргументируешь, чем твоя лучше ловит дефекты этой работы.
|
||||
|
||||
## 7. Записка-план
|
||||
|
||||
До кода — короткая записка `backend/docs/SEAM_FIX_PLAN.md`: что делаешь, в каком порядке, какие
|
||||
развилки видишь. Комплектность против §4 сверяется МЕХАНИЧЕСКИ (таблицей или грепом), а не глазами:
|
||||
**пунктов шестнадцать** (`grep -cE '^### 4\.' `), и «кажется, всё» здесь не работает. Заказов ВНЕ §4
|
||||
нет — всё, что раньше жило в приложениях и в хвостах, вынесено в §4 отдельными номерами
|
||||
(§4.14, §4.15); приложения несут только справку и пинги.
|
||||
|
||||
## 8. Заявление = команда
|
||||
|
||||
Каждое число и каждая категорика в отчёте — **с командой, которой получены, прямо рядом**. Не по
|
||||
памяти о том, как ты это выяснял. Приёмка пере-ранит выборочно; расхождение отчёта с пере-раном
|
||||
дороже, чем честное «не проверял».
|
||||
|
||||
⚠ Прошлый отчёт дал повод: его замер цены глагола (`5000 → 1.60с/133МБ · 20000 → 9.08с/350МБ`) при
|
||||
пере-ране приёмки сошёлся по времени на 5k/10k и разошёлся вдвое по ПАМЯТИ (199/395/798 МБ), а
|
||||
выведенный из него потолок «до ~80k термов в 60-секундный бюджет платформы» оказался оптимистичен
|
||||
примерно вдвое. Числа, которые поедут в чужой пак как датум, стоят отдельной аккуратности.
|
||||
|
||||
## 9. Эхо-протокол старта
|
||||
|
||||
ДО работы — не больше десяти строк: скоуп как ты его понял · инварианты, которые не тронешь · что
|
||||
делать НЕ будешь. Расхождение с этим промтом видно сразу и стоит дёшево.
|
||||
|
||||
## 10. Что НЕ удалось
|
||||
|
||||
Обязательная секция отчёта. «Не проверено» ≠ «работает». Вердикт о рантайме без живого прогона —
|
||||
максимум PLAUSIBLE, и так и пиши.
|
||||
|
||||
## 11. Канал вопросов
|
||||
|
||||
Конфликт промта с кодом или доками — **пинг через владельца, не интерпретация**. Ты вправе сказать
|
||||
«этого делать не надо» с аргументом: пак, принёсший развилку вместо тихой девиации, — хороший пак.
|
||||
Четыре пункта выше (§4.6, §4.9, §4.10, §4.13) прямо приглашают такой ответ.
|
||||
|
||||
---
|
||||
|
||||
## Приложение: что приёмка ПОДТВЕРДИЛА — не чини это заново
|
||||
|
||||
Чтобы дофикс не тратил время на уже доказанное:
|
||||
|
||||
- **Цепь работает** (§4.12) · **мины 1 и 2 строки 199 обезврежены** (явный `status: approved` пишется;
|
||||
approve без `dst` отвергается).
|
||||
- **§4.1–§4.7 прежнего заказа исполнены полностью**, все девять семантик §4.2 — тоже, семь пунктов
|
||||
закона — тоже; скоуп-крипа нет. Пять вещей сверх заказа (класс 14, `export_version`, пакет
|
||||
`standdata`, строгость reject-читателя, два поля отчёта) — аргументированы и безопасны.
|
||||
- **Ни один платформенный канал не ломается**: аддитивные поля прошли через её собственные декодеры
|
||||
на реальных документах; строгих декодеров у неё нет ни одного на пути движковых документов;
|
||||
id термов банка не сдвинулись; `Refused(14)` истинно, `DeploymentFault` 14 не покрывает, а интейк
|
||||
отправляет нераспознанный код полосы в ветку, которая файл пользователя НЕ удаляет.
|
||||
- **Строгость не сломала ни один живой документ корпуса** — 44 из 44 (29 сид-схемы + 15 reject-схемы).
|
||||
- **Критерий §4.6 ПРЕЖНЕГО заказа (дефолт корпуса через `standdata`) выполнен буквально** — не путать
|
||||
с §4.6 ЭТОГО промта, который про другое; **§13 сходится точно** (59 тест-функций + 2 фаззера,
|
||||
удалено 0); все 28 пинов, названных отчётом, существуют ровно по одному разу.
|
||||
- **$0 доказан** структурно и исполнением; ледджер и база не сдвинулись ни на байт.
|
||||
- **Три заявки ревьюеров приёмка ОПРОВЕРГЛА, не поднимай их заново:** src, нормализующийся в пустой
|
||||
ключ, ловится дедупом поверхностей · повторный `decline` корректно отчитывается `already_applied` ·
|
||||
отказ по алиасу подписанного сид-терма отвергается по имени.
|
||||
|
||||
## Приложение: пинги, которые дофикс НЕ исполняет
|
||||
|
||||
Названы, чтобы ты их не «допилил попутно»:
|
||||
|
||||
- **exit 2 при OOM читается платформой как успех** (§4.5) — общедвижковый класс, не твой скоуп.
|
||||
- **`readEngine` платформы выбрасывает stdout при любом ненулевом коде, кроме 2** — а отчёт
|
||||
`bank-apply` при `exit 14` и есть продукт вызова. Требование к ПЛАТФОРМЕННОМУ паку.
|
||||
- **Развилка «нужен ли `redrive` канал ключей аргументом»** — остаётся пингом. Заказом стала только
|
||||
вторая половина этого пинга — неполный довод в тексте отказа, §4.14.
|
||||
- **`rubyToCandidates` мёртв в проде** · **гейт `TestDispatchCommandsCoversTheSwitch` слеп к `case` с
|
||||
константой** (⚠ эта слепота названа в §4.11 (в) как ловушка при копировании техники — не унаследуй её) ·
|
||||
**схема сида без `omitempty`** — три пинга твоего же §10, остаются пингами.
|
||||
- **Проекция берёт ТОТ ЖЕ лок, что и применение** (§10 п.4) ⇒ «поправить термин при живом прогоне»
|
||||
даёт `12` даже на ПРЕВЬЮ, на всю длину прогона. Проектируется ПЛАТФОРМЕННЫМ паком (его окно —
|
||||
состояние между прогонами, когда движок вышел и лок отпущен), не движковым.
|
||||
- **Опубликованные пути двух файлов решений читаются как приглашение их парсить** (§10 п.5) —
|
||||
⚠ см. §4.6: изгородь снимается сознательно и по ратифицированному направлению закона.
|
||||
- **Карта подписи не удаляется после погасшего стопа** (§4.12) — пред-существующее, пинг.
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
| Роль | Активный промт | Статус |
|
||||
|---|---|---|
|
||||
| Оркестратор | [ORCHESTRATOR_SESSION_PROMPT.md](ORCHESTRATOR_SESSION_PROMPT.md) | роль и нормы; счётчик роли — CURRENT-STATE |
|
||||
| Бэкенд | [BACKEND_SEAM_PACK_SESSION_PROMPT.md](BACKEND_SEAM_PACK_SESSION_PROMPT.md) | движковая половина закона входной двери шва (D39.156) + строка 218 (дефолт корпуса), дописана 24.08; запуск по слову владельца |
|
||||
| Бэкенд | [BACKEND_SEAM_FIX_SESSION_PROMPT.md](BACKEND_SEAM_FIX_SESSION_PROMPT.md) | дофикс движковой половины входной двери шва по приёмке 25.08: блокер round-trip reject-документа + восемь мажоров + пять слепых пятен гейта; дерево пака НЕ закоммичено и продолжается, не начинается заново; запуск по слову владельца |
|
||||
| Платформа | [PLATFORM_P8_REVIEW_SESSION_PROMPT.md](../platform/docs/PLATFORM_P8_REVIEW_SESSION_PROMPT.md) | read-only ревью четырёх осей; запуск по слову владельца |
|
||||
| Полигон | [POLYGON_EXP2223_REDO_SESSION_PROMPT.md](POLYGON_EXP2223_REDO_SESSION_PROMPT.md) (отложенный — [POLYGON_PACKAGE4_SESSION_PROMPT.md](POLYGON_PACKAGE4_SESSION_PROMPT.md), строка 85) | фаза Д ИДЁТ; ⚠ живой носитель курса — в `eval/dovodka/`, какой именно называет зона (⚠ [POLYGON_PHASE_D_HANDOFF.md](POLYGON_PHASE_D_HANDOFF.md) — перекрытый снимок, читать не как курс) |
|
||||
| Фронт | активного НЕТ | **ЗОНА ЗАМОРОЖЕНА** (D39.136 п.2 + D39.147: разморозка отдельным словом владельца, не привязана к P7); перечень первого касания — в зонном журнале |
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue