textmachine/docs/archive/prompts/BACKEND_BANK_ZERO_LAYER_SESSION_PROMPT.md

31 KiB
Raw Blame History

ОТРАБОТАН И ПРИНЯТ 16.09 — акт D39.257, 29 путей в backend/ плюс отчёт, пак $0. Инструкции ниже НЕ исполняются: пак закрыт. Половина ряда 445 закрыта (мусор больше не едет законом), остаток — отказ прозой до заполнения реестра. Новые строки: 462467. Главное, что купил пак, — не код: посылка, на которой стоит D39.256 («у смен передачи не было новой улики»), перестала быть посылкой. 25 из 26 смен при идентичном входе, и компаратор НЕ слеп — у неменявшихся строк он находит движение входа в 4 из 56 и 8 из 51. Два мажора приёмки: сдача сдвинула 25 якорей живых доков и отчёт молчал (перенаведены лендингом, норма — ряд 467); арм unresolved считается по знаменателю, из которого сам вычтен (ряд 462). ⚠ Условие §4.1 в теле УЖЕ реальности: дверей не три, а пять — сессия расширила по праву и была права.

Пак бэкенда: «БАНК — ЧТО МОЖНО УЗНАТЬ И ПОЧИНИТЬ ЗА $0»

Оркестратор №23. Пак $0: ни одного платного вызова моделей. Всё, что ниже, либо детерминированный код, либо замер по УЖЕ КУПЛЕННОМУ сырью. Если тебе покажется, что предмет требует покупки — это вопрос оркестратору (§11), а не повод купить.

1. Какая проблема и что решит твой результат

Банк памяти — таблица (src, sense, since_ch, until_ch) → dst, gender, status, которую движок инжектит переводчику и редактору, чтобы имена и термины были одинаковы на всю книгу. Владелец назвал это место самым умным участком движка: «там скорее консилиум алгоритмов должен работать, который выведет, что инжектить редактору».

Этот пак — НЕ тот консилиум. Это его фундамент: четыре вещи, которые сегодня либо молчат, либо не измерены, и каждая из них делает будущий «умный участок» либо невозможным, либо недоказуемым.

Почему именно $0-слой первым, и это не осторожность, а порядок работ. Умная маршрутизация кандидатов (кого спрашивать, с каким контекстом, когда считать решённым) стоит на двух вещах, которых сегодня нет: на предикате «спорный» с ИЗМЕРЕННОЙ популяцией и на честном разборе ответа роли. Построить маршрутизатор раньше — значит маршрутизировать по признаку, чьё распределение никто не видел.

2. Зона и git

Твоя зона записи — backend/. Ты не коммитишь: готовишь дерево и передаёшь на лендинг оркестратору. Чужие незакоммиченные файлы не трогай. Свои итоги пиши в docs/PROGRESS.md, секция «Бэкенд» — это единственное исключение из «docs/ — зона оркестратора».

Читать чужое можно и нужно. Расхождение, которое ты нашёл вне своей зоны, — пинг в отчёт, а не правка.

3. Карта чтения — ЗАКОН, дальше только по её ссылкам

  1. Этот файл целиком.
  2. docs/research/35-bank-memory-consilium.md — итог консилиума. Читай §П3 «Исходы решения», §2.0 «что двигает каждый шаг» и обязательно пометку в КОНЦЕ файла (16.09): §П3 верен про ноту и неверен про дерево, и это прямо касается §4.2 ниже.
  3. docs/BACKLOG.md — ряды 441, 445, 449, 459 (заказаны) и ряды 435, 438 (НЕ заказаны, но прочти: там названы две денежные мины следующего пака, и твой код не должен их углубить).
  4. docs/architecture/05-decisions-log.md — тела ## D39.102 (греп) и ## D39.254. Первое закрывает МОДЕЛЬНЫЙ совет замером, второе несёт пять решений владельца 16.09.
  5. Предмет в коде: backend/internal/pipeline/terminologist.go и backend/internal/pipeline/mining.go.

Журнал решений целиком НЕ читать. Номер грепается: тело — строка ^## D<номер>; подномер (D39.2) собственного тела не имеет, отбрось хвост.

4. Состав и разметка свободы

4.1 Цель без текстов инъекции обязана останавливать прогон ГРОМКО — делай РОВНО так

Посылка сформулирована ТОЧНО, потому что первая редакция этого промта была тут НЕВЕРНА. Сегодня книга с не-русской целью до молчания НЕ доходит: загрузчик падает раньше и громко, потому что промтов пары нет — backend/internal/config/pipeline.go:870=A missing convention prompt («a book whose pair has no prompt pack must stop, never silently run another pair's conventions»), а в backend/prompts/ лежит только zh-ru. Молчаливый путь достижим для БУДУЩЕЙ пары, у которой промты и langpack уже есть, а строк инъекции ещё нет: тогда прогон покупается, а оба банк-блока рендерятся в ноль байт молча. Гейт стоит в рендерерах: backend/internal/membank/memory.go:745=func RenderGlossaryBlock и backend/internal/membank/memory.go:841=func RenderEditorConstraintBlock, оба начинаются с if !tx.HasData() { return "" }, где HasData — это всего лишь наличие заголовка (backend/internal/lang/embedded.go:406=func (t InjectionTexts) HasData() bool). А источник молчания — строкой выше по потоку: backend/internal/lang/embedded.go:425=return InjectionTexts{} возвращает нулевое значение без ошибки.

Где ставить отказ: в openRunner, рядом с уже существующим отказом того же рода — backend/internal/pipeline/runner.go:364=forWrite && r.Pipeline.Gates.Terminology.Enabled && r.pack == nil. Там в руках и книга, и пайплайн, и флаги гейтов, и это до открытия волн и до первого платного вызова. Форма — RefusalBadConfig, как у соседа, а не WARN: граница между ними названа в комментарии рядом (backend/internal/pipeline/runner.go:369-371=THE BANKNOTE CHANNEL BUYS OUTPUT NOBODY WILL READ) — «механизм не может работать вовсе» против «стоит лишних денег».

Не ставь это в runTerminologist. Первый платный вызов книги — ЧЕРНОВАЯ волна, а банк-роли платятся позже, на стопе майнинга. Гейт у роли опоздает на целую волну, каждый вызов которой уже нёс банкнотный промт. Довод дословно тот же, что у соседнего CheckMiningContrast: «costs nothing to verify now and a whole draft wave to discover later».

Условие отказа ШИРЕ, чем «включён гейт», и это второй денежный путь, а не придирка. Инъекция строится при r.baseMemory != nil (backend/internal/pipeline/waverun.go:592=if r.baseMemory != nil), то есть мимо гейтов: книга с сидом (backend/internal/config/book.go:92=GlossarySeed string) и ВЫКЛЮЧЕННОЙ терминологией даёт тот же молчаливый ноль. Условие бери такое: forWrite && (Gates.Terminology.Enabled || Gates.Banknote.Enabled || book.GlossarySeed != "") && !lang.InjectionTextsFor(book.TargetLang).HasData().

Текст отказа обязан сказать, ЧТО делать: строки живут в go:embed-файле backend/internal/lang/data/injection.txt, и добавление цели двигает lang.EmbeddedVersion() ⇒ снапшот. Отказ, который этого не называет, отправляет будущую пару в молчаливую пересборку Go.

Пин обязателен, и он должен краснеть на ДАННЫХ, а не на отказе соседа: фикстура обязана ПРОЙТИ загрузчик и соседний отказ :356 (синтетическая пара: промты + каталог langpack) и сверять СВОЙ текст и класс отказа. Иначе пин удовлетворяется чужой уликой и зелен ни о чём.

4.2 Отказ роли не должен становиться передачей — делай РОВНО так по форме, границу реши сам и обоснуй

Ряд 445: роль отвечает прозой («не термин») или мусором («90»), и это сегодня едет редактору законом, побайтно так же, как подписанное человеком решение.

Сначала прочти ряд 459 и пометку в конце research/35 — там развилка, которую ты обязан закрыть явно, а не по умолчанию. В боевом промте zh-ru отказ приглашается: backend/prompts/zh-ru/terminologist.md:36=не можешь выбрать перевод уверенно велит вернуть сентинел и называет это нормальным исходом; парсер его ждёт (backend/internal/terminology/terminology.go:694=const NoDst = "⟦TM-NO-DST⟧", backend/internal/terminology/terminology.go:1248=func isNoDst), счёт печатается прогоном (backend/internal/pipeline/terminologist.go:649="declined", res.Declined). При этом ратифицированная нота D39.102 числит отказ-сигнал закрытым.

Выбора у тебя здесь НЕТ — исполним только путь (а), и это исправление первой редакции промта. Снятие сентинела есть отмена ратифицированного режима, а не настройка: «A declined term emits status:auto (inert) instead of a guess — §C2-7's other mode» (backend/internal/terminology/terminology.go:693=A declined term emits status:auto), а ИСТОЧНИК — нота: D39.42 п.1 ратифицирует дословно «двухрежимная эмиссия §C2-7 (есть dst → draft, нет → auto)», и нота сильнее комментария в коде. Считаешь, что сентинел надо снять, — это вопрос оркестратору на ноту (§11) с расчётом перекупки, а не диспозиция сессии.

Что делаешь: ужесточаешь РАЗБОР ответа, не трогая сентинел. И раскладываешь по ДВУМ корзинам — сегодня они разные, и следующий пак маршрутизирует именно по этому различию:

  • отказ (сентинел, а также отказ прозой) → ветка isNoDstDeclined (backend/internal/terminology/terminology.go:1136=if isNoDst(dst) кладёт out[key] = ""; backend/internal/pipeline/terminologist.go:581=res.Declined++);
  • мусор (dst БЕЗ БУКВ ВООБЩЕ — «90», пунктуация) → плохая строка, ключа в ответе нет ⇒ backend/internal/pipeline/terminologist.go:579=res.Unanswered++.

Класть отказ в «неотвечено» НЕЛЬЗЯ: это слово означает, что роль промолчала, и комментарий счётчика прямо так и говорит. Смешав, ты сломаешь и счётчик, и будущую маршрутизацию (отказ → фоллбек другим семейством; пустота и обрыв → регенерация).

Два уточнения критерия, оба проверены деревом: «dst без букв ЦЕЛЕВОГО языка» уже построено (OffLanguage) — не перестраивай его; дыра именно в «без букв вообще». И не отвергай dst с цифрой: цифры законны внутри имён и серий, критерий — отсутствие букв, а не наличие цифр.

Отказные фразы — ЯЗЫКОВЫЕ ДАННЫЕ, а не литералы в Go: русские строки в коде прямо нарушают цель «движок один, пара — данные». Реестр фраз живёт в langpack, пустой реестр = инертный no-op.

Что здесь дешевле, чем кажется, и это проверено: правка ПАРСЕРА меняет содержимое банка (а с ним memory_version и пере-редактуру под согласием), но не трогает тождество запроса — парсер в кортеж attemptRequest не входит. То есть путь (а) не перекупает батчи.

4.3 Предикат «спорный» — это ДВА предиката, а не один — делай РОВНО так

Будущий фоллбек и рецензент будут звать модель «на спорных кластерах». Слово «спорный» сегодня знаменателя не имеет. Построй его детерминированно и измерь популяцию за $0 на уже купленном сырье. Читать ТОЛЬКО на чтение (sqlite3.connect('file:<путь>?mode=ro', uri=True); CLI sqlite3 на машине нет). Понадобится звать buildBankCandidates/bankStopRows вне платного прогона — это $0-харнесс реплея по БД, и он часть заказа.

ДВЕ БАЗЫ ЕСТЬ, НО ОНИ В РАЗНЫХ СОСТОЯНИЯХ, и наивный реплей намеряет артефакт состояния вместо популяции. Замерено мной 16.09:

  • B — снимок НА СТОПЕ: /home/ubuntu/tm-coldrun-b/evidence/paid-bankstop/project.dbglossary 0, bank_stop_presented 67, request_log 18, checkpoints 24. Рядом сайдкары (*.bank.json 66 кандидатов, *.bank-stop.txt 648 строк).
  • A — состояние ПОСЛЕ прогона: /home/ubuntu/tm-coldrun-a/stand/books/bk_ROEHZBD46ALFI43E/project.dbglossary 69, bank_stop_presented 70, request_log 33. Сайдкары A лежат ОТДЕЛЬНО, в /home/ubuntu/tm-coldrun-a/evidence/bankstop-paid/ (69 кандидатов, 660 строк листа), самой БД там нет, а пути в манифесте указывают на ЧУЖОЙ хост и протухли. ⇒ на A фильтр решённых увидит ЗАПОЛНЕННЫЙ глоссарий и «решит банком» то, что на покупке никто не решал. Либо гони реплей на A с глоссарием, откатанным к стопу, либо считай pre-call-признаки по сайдкарам — и назови, что выбрал.

ДРЕЙФ СХЕМЫ САЙДКАРОВ — готовый ложный ноль: в A поля conventions нет вовсе (0 записей из 69), в B оно есть у всех 66. Посчитаешь «conventions ≥ 2» на A наивно — получишь 0 и это будет число об ОТСУТСТВИИ ПОЛЯ, а не о предмете; пересчитывай из variants той же свёрткой. ⚠ И ключи бери по ВСЕМ записям, а не по первой: часть полей пишется с omitempty (invented есть в 11 записях из 69 у A и в 17 из 66 у B).

ЧТО УЖЕ ИЗВЕСТНО ДО ТВОЕГО ЗАМЕРА — чтобы ты не отчитался нулём как свойством предмета. Мои числа: A spread ≥ 2 = 9, invented = 11 (из 69); B spread ≥ 2 = 8, conventions ≥ 2 = 8, invented = 17 (из 66). А вот post-call-признаки конфликта на обоих источниках имеют популяцию НОЛЬ: в листах стопа CONTRADICTS, THE BANK ALREADY HOLDS и NOT ASKED — по 0 вхождений при непустых файлах (660 и 648 строк — контроль напечатан). Значит живые post-call-сигналы здесь только два, и предикат, построенный на конфликтах, ты проверить на этом сырье НЕ сможешь: так и скажи, а не выдавай ноль за измерение.

Предикатов ДВА, и у них разные поля и разные места. Если сделаешь один, маршрутизация ДО вызова останется пустой, а отчёт скажет «предикат есть».

  • pre-call (решает, кого и с каким контекстом спрашивать). Доступно до первой траты — там, где живёт «$0-половина роли»: backend/internal/pipeline/terminologist.go:270=buildBankCandidates is the $0 half of the role, она «runs whether or not the gate is on». Поля: разброс вариантов, конвенции, сигналы ранжирования, и «уже решено банком» — последнее снимается фильтром backend/internal/pipeline/terminologist.go:379=r.dropBankSettled(ctx, cands), который стоит ДО первого платного вызова (backend/internal/pipeline/terminologist.go:413=r.runClassifier(ctx, snapID, paid)). ⚠ Назови честно ограничение: до классификатора тип кандидата эвристический, поэтому pre-call-предикат видит «спорный по черновикам», а не «спорный по типу».
  • post-call (решает, кого отправлять на фоллбек). Поля живут в строках стопа: backend/internal/pipeline/mining.go:479=func bankStopRows. ⚠ И там же — ловушка знаменателя: при выключенном гейте результат роли пуст (backend/internal/pipeline/mining.go:480-485=A stop can fire with the terminology gate OFF объясняет почему), то есть популяция считается по-разному в двух режимах, и это надо назвать, а не усреднить.

Знаменатели ЗАДАНЫ здесь, а не выбираются тобой — иначе «доля спорных» окажется утверждением о твоём пороге:

  • pre-call: знаменатель = кандидаты на входе роли (дошедшие до buildBankCandidates после фильтра решённых);
  • post-call: знаменатель = строки, на которые роль ОТВЕТИЛА (у неотвеченных признаки не определены вовсе).

Результат — не одно число, а таблица чувствительности: доля «спорных» при нескольких порогах, на обоих источниках, с обоими знаменателями, плюс ответ «сколько строк ушло бы на второй вызов, если бы он существовал». Одно число при невыбранном пороге замером не является.

4.4 Посылка «новой улики не было» обязана получить носитель — делай РОВНО так

Экономика будущего базиса решённого стоит на утверждении «у 25 из 26 смен решения новой улики не было». Тела запросов к банк-ролям не логировались, то есть якорь модели не отделён от её стохастики. Это $0: по уже купленному сырью собери, что РЕАЛЬНО менялось во входе между двумя покупками одной строки.

Скорее всего честный ответ — «тел нет»: чекпойнты хранят ОТВЕТ, лог запросов — хеш и токены, тела пишутся только в лог под отдельным флагом. Тогда результат пака — не число, а дизайн персистенции: что именно надо сохранять (тело запроса банк-роли, снимок канона и кандидатов), чтобы вопрос стал отвечаемым, и чего это стоит.

Опасное умолчание, назови его сам: пере-рендерить сообщения роли по ТЕКУЩЕЙ базе и заключить «вход не менялся» — значит ответить о сегодняшнем состоянии, а не о той покупке. «Не воспроизвелось» без названной цепи звеньев отчётом не считается.

4.5 Чего в паке НЕТне делай, и это объявленные сужения

  • Регенерация банк-роли и фоллбек на другую модель — следующий пак. И вот почему их нельзя взять «заодно»: у внутренних вызовов хоп нулевой НАМЕРЕННО (backend/internal/config/internal_call.go:80=no escalation hop), maybeEscalate выходит на пустом хопе (backend/internal/pipeline/escalation.go:125=st.ResolvedHop == ""), а все три боевых escalate_to стоят у стадии draft. Включение escalation.budget_usd банку не поможет и включит эхо-эскалацию черновика. Плюс проба «уже оплачено» у банк-батча спрашивает попытку 0 с БАЗОВЫМ бюджетом (backend/internal/pipeline/terminologist.go:840=paidAfterBurns(st, st.Model) — она обходит сгоревшие индексы, но только на ТОМ ЖЕ бюджете, — а бюджет попытки входит в тождество запроса, поэтому регенерация с другим бюджетом пробе невидима.
  • Базис решённого (N-1), род-улика (N-5), стратифицированный KWIC (ряд 449) — не в этом паке.
  • Модельный совет в любой форме — закрыт замером D39.102 п.2 и не переоткрывается.

5. Мандат самопроверки ИСПОЛНЕНИЕМ — «перечитал сам» не считается

Промт требует ревью исполнением: своего кода, своих замеров и своих чисел. Самоотчёт «проверено» без исполнения регулярно оказывается ложным.

  • Каждый пин — прогнать и показать вывод. Пин, который на твоей машине скипается, не стережёт ничего.
  • Каждое число — пере-снять вторым прибором либо напечатать рядом контрольную величину. Отрицательный замер обязан доказать, что спросил существующее: «0 совпадений» и «нет такого поля» неразличимы.
  • Сверх исполнения — адверсариальный проход по собственной готовой работе. Где твой пак уязвим: предикат §4.3 легко померить на удобной половине данных; отказ §4.1 легко проверить на конфиге, который и так не запускается.

6. Оси ревью

Характер «Код» плюс ось денег: любой твой механизм обязан ответить, что он делает с чужими деньгами, если сработает ошибочно. Ось вправе заменить с аргументом.

7. Записка-план ДО работы

До первой правки — короткая записка в docs/PROGRESS.md: что берёшь, в каком порядке, чем будешь мерить.

8. Заявление = команда

Любое «проверено / измерено / не воспроизводится» в отчёте обязано быть командой, которую можно повторить.

9. Эхо-протокол старта

Первым сообщением — своими словами: что заказано, где предмет уже, чем ты его смеришь, и какие опасности ты видишь в самом заказе. Право сказать «этого делать не надо» у тебя есть (§11).

10. Что НЕ удалось — обязательная секция отчёта

Пустой она не бывает. Если кажется, что бывает, — значит не назван остаток.

11. Канал вопросов и право сказать «этого делать не надо»

Вопрос — секцией в отчёт; работа продолжается. Владелец прочитает и пере-передаст. Если ты видишь, что пункт заказа неверен, — скажи это и не делай, с доводом. Это норма, а не отказ от работы.

Тебе разрешён ОДИН советчик-фабл (model: "fable" задавай явно). Он советчик, а не источник истины: его посылки проверяются деревом — у меня две его ссылки из десяти оказались с неверными номерами строк.

12. Прямой канал

Связь между сессиями — /tmp/textmachine-channel: впиши свой блок (role= session= ref= written= note=), дописывая в конец и не трогая чужие.

13. Критерий завершённости — по нему тебя примут

  1. §4.1 построен, отказ краснеет пином на данных, и пин прогнан.
  2. §4.2: разбор ужесточён по пути (а); отказ и мусор РАЗВЕДЕНЫ по корзинам (Declined против Unanswered), обе ветви запинены; реестр отказных фраз лежит в langpack, а не литералами в Go.
  3. §4.3: два предиката; $0-харнесс реплея по БД работает; таблица чувствительности по порогам снята на обоих источниках, оба ЗАДАННЫХ знаменателя использованы и названы в отчёте.
  4. §4.4: ответ или честное «данных нет, нужен такой-то лог».
  5. Гейт зоны зелёный целиком — make battery, а не go test; в отчёте названо, что прогнано.
  6. Мутации новых мест введены в гейт, а не прогнаны разово.
  7. Секция «что не удалось» заполнена.

Деньги

Пак $0. Платных вызовов моделей нет ни одного. Замеры — по уже купленному сырью. Если предмет требует покупки, это вопрос оркестратору, а не решение сессии.

Отчёт

docs/PROGRESS.md, секция «Бэкенд»: что построено, чем проверено, какие числа сняты и каким прибором, что осталось. Дерево передаёшь на лендинг — не коммитишь.