WEB-149 · Обслуживание · Чат · web
Включение ROLE_POLICY_ENFORCEMENT: пилот 3 ролей + температуры из политики + per-role DIRECT ANSWER RULE + выбор модели по роли
Закрыт
P2
ведёт: —
Суть
--- 30.08 ~19:45 IST (Фабл): лестница ролей, шаг «расписки на проде»: батарея l74 показала — receipt есть только на chat-global, ролевые маршруты (notebooks/[id]/chat) отдают role_policy БЕЗ temporal receipt, при этом фильтр окна работает (цитаты только в окне). Волна 829-receiptroutes: тот же механизм на реальные ролевые маршруты + SSE-граница + негативы. После неё: адаптация батарейного чекера к response-receipt/v1 и ПЕРЕПРОГОН батареи (ожидание: C2/C3 из 0/17 → зелёная зона).
--- 30.08 ~20:50 IST (Фабл): receiptroutes ГОТОВ (18ecbb82): корень — оба REST-обёртки ТЕРЯЛИ уже построенный chatWithSources-ом receipt при user-facing проекции (не «фича не написана», а «поле роняли на границе» — тот же класс, что stream-receipts f04026c6). Фикс: notebooks/[id]/chat и sources/[sourceId]/chat возвращают исходный receipt (assertResponseReceipt), roleId доведён; editor-роут не тронут ОСОЗНАННО (это history-CRUD, генерация идёт server-action-ом). Приёмка 833 в очереди. После GO: адаптирую батарейный чекер C2/C3 к response-receipt/v1 (реальная форма) и перепрогон на l75.
--- 30.08 ~21:25 IST (Фабл): приёмка 833 = NO-GO с одной точной находкой (мой вопрос №4 брифа сработал): успешный объект без receipt → response_receipt_missing улетает наружу unhandled reject-ом → неструктурный 500. Контракт, подмена с клиента, role forwarding, scope — всё подтверждено (13/13+6/6+2/2 воспроизведены). Корректирующий круг 837-receiptroutes2: контролируемый fail-closed JSON (502) + beacon, без пустышек.
--- 30.08 ~22:35 IST (Фабл): ✅ приёмка 840 = GO — ЛИНИЯ РАСПИСОК НА РОЛЕВЫХ МАРШРУТАХ ЗАКРЫТА (18ecbb82 + 13f8c80a): контракт цел, подмена с клиента не проходит, успех-без-receipt и мусорный receipt оба fail-closed структурным JSON, регрессии 13/13+6/6+2/2+2/2. Оба коммита в поезде l75 (мерж-волна 846 запущена). После посадки: адаптация чекера батареи к response-receipt/v1 по живому телу + перепрогон.
--- 30.08 ~23:55 IST (Фабл): батарея r15: C2 ПОЛНОСТЬЮ ЗЕЛЁНЫЙ 14/14 (временные окна: расписка присутствует, out-of-window/undated не admitted, расширение окна явное, corpus>finalK — всё через response-receipt/v1). C3: retrieval-трейл research-роли уже в receipt, claim-judge расписки не доезжают (все null) → волна 852 диагноз+проброс.
--- 31.08 ~01:00 IST (Фабл): c3receipts — ЧЕСТНЫЙ ДИАГНОЗ: claim-judge семья расписок (claimEnforcement/evidenceBreadth/evidenceFinalization/corpusCompleteness/finalSemanticGate) НЕ СУЩЕСТВУЕТ нигде в src/lib — ни producer, ни типа, ни route (rg + git refs пусто). Это НЕ потерянная расписка, а ненаписанная фича: батарейные C3-чеки сочинены под спеку, которой в продукте никогда не было (второй фантом после temporalWindowOutcome). Решение: (а) C3-чеки перепишу — retrieval-дисциплина по РЕАЛЬНОМУ receipt (roleId/searchPlan/candidateK>finalK/fragments), claim-judge оставляю красным с пометкой «продуктовая фича не построена»; (б) построение claim-judge = отдельная продуктовая линия эпика ролей — на решение owner-а по приоритету.
--- 31.08 ~02:00 IST (Фабл): РЕШЕНИЕ OWNER (20:47): «квитанцию судьи» СТРОИТЬ, максимально параллельно. Запущены две волны: 861-claimjudge (ядро: разбор утверждений → LLM-судья дешёвой моделью через штатный billing с branded identity → repair/remove/refuse → receipt с requestHash/responseHash/counts; флаг CLAIM_JUDGE_ENABLED default OFF; судья упал → deferred fail-closed без блокировки ответа) и 862-evidencereceipts (детерминированные evidenceBreadth/Finalization/corpusCompleteness/finalSemanticGate из данных отбора, без LLM). Спека полей = батарейные C3-чеки (отправлены волнам как контракт). После приёмок → l76 → C3 оживает по-настоящему.
--- 31.08 ~03:55 IST (Фабл): evidencereceipts авторски готов (4dfabf69, детерминированные квитанции после финального ответа) — приёмка 869 в очереди (вкл. проверку шва с параллельной claimjudge). claimjudge ещё пилится (самая тяжёлая волна ночи).
--- 31.08 ~04:20 IST (Фабл): приёмка 869 evidence-квитанций = NO-GO, две узких: (1) no-role путь пропускает role-only floor (границу чинить); (2) ПРЕДСКАЗАННЫЙ конфликт шва с параллельной claimjudge по actions.ts (3-way не ложится). Корректирующий круг пойдёт ПОСЛЕ финиша claimjudge — чинить шов на её финальном коммите, не вслепую (претендент на слияние двух веток одной волной).
--- 31.08 ~05:40 IST (Фабл): 🎉 CLAIMJUDGE ГОТОВ (5d17a1d9, dark CLAIM_JUDGE_ENABLED): разбор ответа на утверждения после citation-gate → дешёвый judge (CLAIM_JUDGE_MODEL, default textUtility) через MeteredAIProvider/prepareLLMBilling с branded identity и СВОИМ callsiteId web_rag_chat_claim_judge (урок Совета применён автором самостоятельно). Приёмка 880 (counts consistency, deferred fail-open с beacon, removedText отсутствует в ответе, регистрация callsite, детерминизм хэшей). Evidence-круг 883: чинит no-role floor + сливает шов с claimjudge руками на финальном коммите. После обоих GO → l76 → перепрогон батареи с CLAIM_JUDGE_ENABLED на батарейной учётке (ожидание: C3 оживает).
📖 Правила обогащения этого тикета: WEB-449 (канон для любого агента).
--- 31.08 ~05:55 IST (Фабл): приёмка 880 судьи = NO-GO — поймана СОВЕТСКАЯ болезнь (пункт 5 брифа сработал): judge передаёт chatCallsiteId (web_rag_chat_claim_judge только kind), строки нет в paidEntrypointRegistry.json → authorizePaidCallsite(enforce)=callsite_unregistered → в бою был бы 502-класс. Круг 884-claimjudge2 (свой зарегистрированный callsite + enforce-тест + остальные находки отчёта). Судья поедет l77.
--- 31.08 ~06:10 IST (Фабл): evidencereceipts2 = GO (no-role floor leakage закрыт; evidence-квитанции И claim-judge receipt в одном response funnel; шов actions.ts разрешён руками). Приёмка 887 (проверка шва + независимость evidence-слоя от callsite судьи). claimjudge на доводке callsite (884).
--- 31.08 ~08:30 IST (Фабл): ✅ claimjudge2 (884 GO): свой зарегистрированный callsite web_rag_chat_claim_judge, enforce-тест проходит → l77. ❌ evidence2 (NO-GO: role-outcome строится ДО optional claimjudge, rolePolicyEnforcementWiring падает при claimJudgeResult) → круг 903 (слить claimjudge2 руками, role-outcome на ФИНАЛЬНОМ answer). После обоих GO → C3-чеки батареи оживают.
--- 31.08 ~10:50 IST (Фабл): ✅ evidence3 = GO — БЛОКЕР ПАРЫ СНЯТ: role-outcome строится ОДИН раз на finalAnswer (после optional claimjudge), rolePolicyEnforcementWiring зелёный, evidence+claim-judge receipt в одном ответе. Финальная приёмка 916. После GO → l77 → включение CLAIM_JUDGE_ENABLED на батарейной учётке → перепрогон (ожидание C3 3/3).
--- 31.08 ~11:35 IST (Фабл): приёмка 916 пары = NO-GO по ОДНОМУ пункту: суть принята (role-outcome один раз после claimjudge/repair ✅, callsite судьи зарегистрирован ✅), но регресс evidence-волны roleEvidenceReceipt.test.ts падает 14/15 (1 fail). Круг 919-evidence4: разобрать — тест устарел под новый порядок или реальный дефект; честно обновить/починить; ВСЕ регрессии обеих волн зелёные. Судья на прод едет только со 100% зелёными регрессиями.
--- 31.08 ~11:55 IST (Фабл): evidence4 = GO (регресс roleEvidenceReceipt починен) → финальная приёмка 921 (вкл. проверку ЧЕСТНОСТИ обновления теста — не подгон ли под баг).
--- 31.08 ~12:15 IST (Фабл): ✅✅ ПРИЁМКА 921 = GO — ЛИНИЯ СУДЬИ-КВИТАНЦИИ ЗАКРЫТА (claimjudge2 13fff40b + evidence4 8f178415): регресс roleEvidenceReceipt зелёный, порядок после claimjudge, callsite зарегистрирован, evidence работают и при выключенном судье. В поезде l77.
--- 31.08 ~15:55 IST (Фабл): 🔬 СУДЬЯ ВКЛЮЧЁН НА ПРОДЕ (CLAIM_JUDGE_ENABLED=1 в drop-in l77, служба перезапущена, paid TRUE []). Паспорт батареи пересобран под l77 (bundle 1fdaf6cf, tree b1c3c467, stand-манифест 88afe931/15065 файлов, servingProof 7f958fe0, манифест подписан, леджер свежий, кап $2), запущен прогон r16prod-l77-20260831a. Ожидание: C3 (research-цепочки, 3 кейса) должны ожить — впервые в продукте есть claim-judge квитанции.
--- 31.08 ~21:35 IST (Фабл): CLAIM_JUDGE_ENABLED=1 снова на проде (после восстановления платного контура), paid TRUE []. Прогон батареи r17prod-l77-20260831b идёт — это первый ЧЕСТНЫЙ замер с работающим судьёй (прошлый r16 упал не из-за судьи, а из-за пустого кошелька).
--- 31.08 ~22:10 IST (Фабл): 🔬 БАТАРЕЯ r17 С СУДЬЁЙ = 25/50, и это ЦЕННЫЙ результат: вскрыт настоящий дефект судьи. C2 упал 14/14→0/14 НЕ из-за расписок (receipt на месте, droppedByReason.outside-date-window=0 — retrieval не доставлял такие доки), а по СЕМАНТИКЕ: ответ содержит 21 цитату при fragmentsDelivered=16, среди них «Fixture Note T-364 (outside both windows)» ×3 — судья ДОПИСЫВАЕТ источники, которых не было в его контексте, нарушая временное окно роли. C5 6/10 (было 8/10) — вероятно тот же класс. C4 7/7 и C9 6/6 держатся. Волна 940-judgecitations: судья может только УБИРАТЬ/помечать, НЕ вводить новые ссылки; страж «цитаты ⊆ доставленные фрагменты» + фиксация нарушения в claimEnforcement. CLAIM_JUDGE_ENABLED снят с прода до фикса (paid TRUE, прод чист). Вывод для линии: приёмки судьи на моках этого не ловили — нужен тест против РЕАЛЬНОГО набора фрагментов.
--- 01.09 ~01:00 IST (Фабл): ✅ judgecitations = GO (4de3c3ae): судья больше не вводит источники вне доставленного набора, страж «цитаты ⊆ fragmentsDelivered» + фиксация нарушений в claimEnforcement. Приёмка 945 с боевым негативом (попытка процитировать недоставленное → цитата отбрасывается, факт в квитанции; кейс C2 с документами вне окна). После GO → l78 → включаю судью → перепрогон батареи (ожидание: выше 40/50).
--- 01.09 ~01:55 IST (Фабл): ✅✅ ПРИЁМКА 945 = GO — ФИКС ЦИТАТ СУДЬИ ПРИНЯТ (кандидат l78): в финальном post-judge boundary цитаты допускаются ТОЛЬКО из фактически доставленного набора; недоставленный источник удаляется из ответа и оставляет аудируемую запись в claimEnforcement.citationViolations. Линия судьи-квитанции технически готова: claimjudge2 (callsite) + evidence4 (порядок/регресс) + judgecitations (цитаты). После посадки l78: включаю CLAIM_JUDGE_ENABLED и гоню батарею — ожидание выше 40/50, C2 должен остаться зелёным (судья больше не вводит документы вне окна).
--- 31.08 10:30-12:00Z — ПОСАДКА l78 (коммит 7377c212), координатор Фабл
АРТЕФАКТ: me2-standalone-linux-arm64-7377c212-20260831T104651Z.tar.gz, sha256 d6fdf4c5d1948c1d31906ef630e468c06705435d3c4d7492bbc595bc2de76f38, 225 318 768 байт. Собран на A1 (M4 трижды не смог по памяти: heap 10240 → OOM-killer, heap 7168 → FATAL Ineffective mark-compacts; своп 8 ГБ внутри Lima раздул образ и добил диск хоста до 104 МБ — урок lima-swapfile-inflates-host-disk).
ДЕФЕКТ, НАЙДЕННЫЙ СБОРКОЙ (починен координатором, коммит 7377c212): клиентская страница /dev/create-podcast через CreatePodcastModal → showLaunch → warmupSkipGate → realtimeBackends → studioRedisPresenceStore тянула ioredis в БРАУЗЕРНЫЙ бандл; webpack не мог разрешить dns/net/tls, сборка падала. Лечение честное, не заглушка: чистая функция deriveInteractiveShowRoomId вынесена в src/lib/podcast/interactive/roomId.ts без зависимостей, warmupSkipGate реэкспортирует её. ⚠️ Наш прод-гейт «серверное недостижимо из клиента» этот случай ПРОПУСТИЛ (сказал OK) — нужна отдельная задача на усиление стража. На M4 дефект не проявлялся, потому что там ioredis отсутствовал в node_modules и Next глотал это как «необязательная зависимость».
ПОДПИСЬ: sidecar l78-activation-attestation-signed.json собран на ноуте координатора (ключ owner-а наружу не уезжал), verify: PIN OK, ПОЗИТИВ VERIFY-OK, НЕГАТИВ (порча sha) ОТВЕРГНУТА-OK. Три comparanda: artifact d6fdf4c5…, migrationSet c7c82872…, dependencyLock aa094172…
МИГРАЦИИ: применены 4 (p13_hnsw, web093_sync_pagination, web093_stateful_webhook_inbox, p12_source_indexing_queue_index) на живой базе, 16 мин 42 с. HNSW-индекс DocumentChunk_embedding_hnsw_idx построен CONCURRENTLY на 96 574 строках / 1429 МБ, indisvalid=t, блокировки таблицы не было.
СУХОЙ ПРОГОН: порт 31264 через enforce-обёртку и systemd (bash source не годится — ловушка env-файлов подтвердилась: `realtime.session: command not found`, embedding-integrity.env Permission denied). Результат: health=200, paidReady=true, posture=enforce_ready.
ПЕРЕКЛЮЧЕНИЕ: обе службы (note-clone-3010 и note-clone-source-indexing), drop-in сохранены как *.l78-kept-*, остаточных ссылок на l77/7f958fe0/старый sha не осталось. Воркер индексации на l78, отработал 25 пачек и корректно ушёл по таймеру (inactive для oneshot = норма). live-sha на проде = 7377c212.
СУДЬЯ: CLAIM_JUDGE_ENABLED=1 добавлен в drop-in, служба перезапущена, paid gate остался зелёным.
БАТАРЕЯ РОЛЕЙ r18prod-l78 (паспорт пересобран под l78: бандл source-l78-all-7377c212, файл-манифест прода 15 768 файлов / 793 948 464 байт, расписка стенда с servingProof liveShaMatches, манифест подписан и проверен, budget-ledger обнулён): 27 ok / 23 FAIL при пороге 30. Прогресс 10 → 25 → 27.
ДВА КОРНЯ, НАЙДЕННЫЕ ПО БОЮ:
1) СОВЕТ: в журнале прода [council][slot_beacon] stage=council_stage2 error=VendorCallRejectedError «This request id was already used for a different request», далее [council][run_failed] «peer-review stage failed for 2 reviewer(s); chair synthesis refused». Причина: все стадии Совета (черновик рыцаря, две рецензии, синтез) идут под ОДНИМ requestId, spendGuard отвергает второй вызов как idempotency_conflict. НЕ регрессия l78 — на l77 C6 падал так же. Волна 969-councilidem: своя идентичность на каждый фактический вызов (стадия+участник+попытка) с сохранением защиты от настоящих повторов; проверить фактчек/deep research/подкаст/видео на ту же болезнь.
2) РОЛИ: все 14 случаев C2 падают на !no_out_of_window_citation и !receipt_out_of_window_admitted_zero (источники вне временного окна роли попадают в ответ), в C2-boundary-neither-364 ещё и !forbidden_note_absent. Все три случая C3 падают ПОЛНЫМ набором полей квитанции (!role_policy_receipt_present !role_is_research … !repair_receipt_present) — то есть роль research не отрабатывает как research. Волна 970-rolesc2c3 с прямым запретом подгонять под тест.
Связано: WEB-149 (роли), WEB-320 (леджер), WEB-449 (правила).
--- 31.08 13:00-13:20Z — ЧЕТЫРЕ КОРНЯ НАЙДЕНЫ И ЗАКРЫТЫ (волны sol xhigh от l78 @7377c212)
1. СОВЕТ (969-councilidem, коммит в wave/councilidem). Мой продовый диагноз подтверждён, но МЕСТО оказалось раньше по цепочке, чем я думал: spendGuard корректно возвращал idempotency_conflict, а конфликт создавался в requestIdentity → meterVendorCall → spendReservation. У одной логической операции Совета были ДВЕ несовместимые формы дочерней идентичности: все стадии наследовали один operationId, но fingerprint/feature родителя строился из канала ТЕКУЩЕЙ стадии (council_stage1, council_stage2…) — поэтому неизменяемая строка SpendOperation воспринимала следующую стадию как дрейф той же операции. Лечение общее, не Council-specific: feature родителя отделён от feature стадии (для проверенного прогона родитель web_rag_chat), те же поля идентичности проведены через общий meterVendorCall, managed LLM billing и metered OpenAI client. Полный Совет в режиме thinking (со стадией revisions) завершился ok при MAX_PROVIDER_ATTEMPTS_PER_OPERATION=3 и MAX_FALLBACK_PROVIDERS_PER_OPERATION=1. Отдельный gateway-тест доказывает общий случай fan-out: две разные нагрузки одного многостадийного родителя дают ordinals [1,2] БЕЗ Council-специфичных ключей — то есть podcast/video/factcheck вылечены тем же. Тесты биллинга: 1006 passed / 30 failed против baseline 1001 / 32 — новых падений нет, два прежних gateway-падения устранены попутно (устаревшая фикстура gpt54-mini → gpt54-nano).
2. КОМНАТА (972-roomws). Причина 101→1006 УСТАНОВЛЕНА: конфликт двух обработчиков апгрейда — провод обрывался ДО ответа namespace 40{...}, клиент видел unclean 1006 до Socket.IO connect. Микрофон НЕ причина (room Socket.IO стартует независимо), origin НЕ причина. Добавлены handshake-логи и обязательные внятные причины отказа, добавлен restart-тест. Отдельно закрыт найденный access-gap: браузер мог отправить room-audio:ready ДО проверки доступа к комнате.
3. РОЛИ C2/C3 (970-rolesc2c3). C3 ломался НЕ из-за неверной классификации: роль research распознавалась (roleId=research), политика и расширенный поиск отрабатывали, но квитанцию СТИРАЛ страж, требовавший временнóе доказательство исполнения от ЛЮБОЙ роли — условие детерминированно выбрасывало все квитанции research. Теперь временнóе доказательство обязательно только внутри Timeline-ветки; остальные роли строят квитанцию из плана поиска, фактически доставленных фрагментов, финальных цитат и финального текста. C2 (источники вне временного окна) закрыт там же.
4. ПОИСК ПО БОЛЬШОМУ ДОКУМЕНТУ (974-bigdocfind2). Замер в реальном Chromium на настоящей «Войне и мире» (tests/enginematrix/fixtures/war-and-peace.txt, sha256 0535a0…bf678d, теперь ЗАКОММИЧЕН в дерево): Наташа 847, Пьер 2321, Безухов 127 — сходятся с независимым полнотекстовым проходом. Первый и финальный счётчики измерены, переход к последнему совпадению работает, ведётся промежуточный «N+» counter. Добавлен контракт границы слова (нет ложного «Робеспьера» для «Пьер»), видимый предел с продолжением вместо молчаливой обрезки, бенчмарки памяти вкладки и процесса, документация Exact-семантики.
ПРИЁМКИ ПОСТАВЛЕНЫ: 977-accroomws, 978-acccouncilidem, 979-accrolesc2c3, 980-accbigdocfind2 — каждая с требованием воспроизвести ИСХОДНЫЙ дефект до фикса, негативными тестами и проверкой границы процесса.
--- 31.08 17:00Z — ПРИНЯТЫЕ ЛИНИИ (кандидаты l79)
1. Совет / общая идентичность многостадийных платных вызовов (978-acccouncilidem): «принято: да». Полный Council thinking — 11/11 уникальных фактических вызовов без conflict; replay/restart/concurrency не дают второго списания; fan-out для podcast/video/factcheck даёт ordinals 1/2; OFF-паритет и миграции доказаны. Кандидат l79 у приёмки помечен «нет» ТОЛЬКО из-за незавершённой полной сборки (V8 heap OOM у волны) — сборку делает координатор.
2. E2 отмена вызова провайдера (975-acce2abort2): «принято: да + кандидат l79: да». OFF-зависание закрыто измеренным жёстким таймаутом (adapter hard timeout 20 мс), ON-путь проводит abort до транспорта.
3. E6 точность векторного поиска (993-acce6offparity2): «принято: да; кандидат l79: да». Оба дефекта первого круга воспроизведены на 79289be0 и закрыты: OFF на реальном PostgreSQL больше НЕ использует HNSW и совпадает с полным перебором; малая similaritySearchBestChunkPerSource выполняет COUNT + прямой exact SELECT без транзакции и session settings.
4. E4 автооткат релиза (992-acce4rollback2): «принято: да + кандидат l79: да». Ложный PASS из stale-артефакта устранён, DRY_RUN связан с тем же rollback core, что и боевой cutover, при выключенном --zero-downtime вернулся baseline-паритет; негативы, реальный PostgreSQL и отдельные процессы пройдены.
5. Роли C2/C3 (998-accrolesc2c32): «принято: да; кандидат l79: да». Оба боевых пути публикуют квитанцию через единый delivery-builder; C2/C3, Timeline и крайние случаи (пустой набор, источник без даты, не-UTC) зелёные; ТРЕТЬЕГО production-пути RoleEnforcementReceipt не найдено.
6. E3 вебхуки (995-acce3webhook2): «принято: да; кандидат l79: да». Четыре находки первого круга воспроизведены на baseline и закрыты на HEAD реальными PostgreSQL/process/systemd A/B-прогонами; новых блокеров P1.6 нет. Отмечено: полный Next build требует более ёмкого бокса — соберёт координатор.
В РАБОТЕ: E1 круг 3 (осталась только ошибка типов TS2339 — суть принята), K11 на приёмке, комната круг 2 на приёмке, поиск круг 3 на приёмке, сохранение больших документов круг 2 (утечки временных файлов), индексы круг 2 на приёмке, мультинод круг 2 на приёмке, телега-вложения круг 9 на M1.
## 2026-08-31 19:42Z — Совет: фикс корня №4 сдан автором, приёмка запущена
councilacct (коммит d2147aba, ветка wave/councilacct): APPLIED-строка без расписок допускается в пределах 60-сек грейса от durable createdAt (перекос снапшотов EvalPlanQual в reserveSpend), после грейса — блок; не-APPLIED — живая лиза обязательна. Автор: 16/16 новых юнитов + 24/24 регрессия проекций + реальный Postgres (3 цикла, 12 расписок) с сохранённым fail-closed негативом и Rule-10 маячками. Приёмка acccouncilacct (бриф 1022, деньги → luna, атака на ослабление: голодание блока свежими APPLIED, чужой implementationHash, часы БД, FAILED_OPEN) поставлена. После GO — кандидат l80, затем перепрогон батареи C6 на проде.
Известное несвязанное: w35Closure ждёт registry-записи editor.completion.openai.gpt4o-mini, отсутствующей в базе l79 — не из этого диффа.
## 2026-08-31 20:20Z — приёмка councilacct: ПРИНЯТО НЕТ по одной находке, круг councilacct2 поставлен
acccouncilacct подтвердила: EPQ-механика верна; проектор атомарен (APPLIED+расписки в одной транзакции, rollback→PENDING_ACTIVE/0); искусственно старая APPLIED-без-расписки блокируется. НО: **грейс сравнивает БД-createdAt с process-now** — при часах приложения, отстающих на 120 с, двухминутная APPLIED-строка без принятой расписки ДОПУЩЕНА (реальный Postgres). Деньги не могут зависеть от часов процесса → councilacct2 (бриф 1025): грейс и проверка лизы от времени БД в той же транзакции; юнит ±10 мин skew; репродукция приёмки до/после.
Ряд Совета: идентичность → лимит → данные → EPQ-гонка → часы. Приёмка отработала ровно по вектору атаки из брифа.
## 2026-08-31 20:42Z — councilacct2 автор сдал: источник времени = БД
Коммит 1f71ceb0 «fix accounting admission clock source»: грейс и лизы от БД-времени в той же транзакции; юнит покрывает process-now ±10 мин; блокирующий сценарий прошлой приёмки (APPLIED 2 мин + now−120с) воспроизведён до/после; scoped ESLint exit 0. → Повторная приёмка **acccouncilacct2** (бриф 1030, СВОЙ повтор всех прошлых атак) в очереди. После GO — Совет готов в l80.
## 2026-08-31 21:00Z — СОВЕТ-УЧЁТ: ПРИНЯТО ✅ КАНДИДАТ l80
acccouncilacct2: фикс 1f71ceb0 принят. Грейс/лизы от PostgreSQL CURRENT_TIMESTAMP в той же транзакции; независимая репродукция: DB-возраст 5с + process-now+120с → allowed; DB-возраст 120с + process-now−120с → accounting_unhealthy. Прошлые атаки и согласованность счётчиков повторены на disposable Postgres. **Ряд из пяти причин Совета ЗАКРЫТ.** councilacct2 сливается в line/l80 следующим шагом (l80merge2); после посадки — батарея C6 на проде как финальное доказательство.
Доказательства
[25.08 ~16:40Z] СДЕЛАНО волной web149: commit 9bf075f. Пилот enforcement на 3 ролях (research/stress-test/plan): buildRoleEnforcementPlan(pilotOnly) в chat-global, actions и Council; температура/systemPrompt/retrieval-профиль (веса vector-FTS-graph, candidateK/finalK, counter-evidence) из ROLE_POLICIES; role id проходит через retrieval boundary. Найденные мёртвые ветки задокументированы (systemPrompt не попадал в prompt block, температура не передавалась в provider call, graph-канал глушился ENABLE_ENTITY_RETRIEVAL). Kill-switch: OFF = байт-в-байт прежнее поведение (негатив-тест). Тесты 127/127, tsc rc=0, флаг dark-by-default (в сборку НЕ включён). → review; включение на стенде для blind-приёмки WEB-108 — следующий шаг.
📎 Трейл: отчёт A1 /home/ubuntu/waves/WEB149-REPORT.md; бриф web149-brief.md; лог web149-codex.log; ветка web149@9bf075f (worktree снят, ref жив). Каталог политики: src/lib/agents/rolePolicy.ts; флаг: rolePolicyEnforcement.ts.
[2026-08-26 01:10Z] Автор web149impl @6406659: ROLE_POLICY_ENFORCEMENT пилот — 3 роли enforced, температуры из политики, per-role DIRECT ANSWER RULE, fail-safe при отсутствии политики. -> review на независимую приёмку (роли-продукт, owner-приоритет).
[2026-08-26 01:20Z] acc149 NO-GO: температура из политики НЕ валидируется — buildRolePilotOverrides передаёт policy.temperature без проверки (rolePolicyEnforcement.ts:124-135), applyRolePolicyTemperature возвращает безусловно (:781-787); mutation-probe: -0.1 и 1.1 проходят. Фикс web149fix (валидация/клэмп + fail-safe + NaN/строка отклоняются).
[2026-08-26 01:35Z] ФИКС web149fix: температура из политики валидируется (вне диапазона/NaN/строка отклоняются или клэмпятся с rule-10), mutation-probe класс закрыт. Тесты 38/38. -> review на re-accept.
[2026-08-27 19:50Z] ПРИНЯТ acc149b (независимый, финал web149fix ffd0388): невалидная температура ([0,2], NaN/Inf/строка) fail-safe выключает enforcement plan и НЕ передаётся провайдеру (блокер acc149 закрыт); 3 пилотные роли research/stress-test/plan получают override, non-pilot нет; kill-switch off сохраняет старый путь; suite+mutation+wiring зелёные. Цикл: web149->acc149 NO-GO->web149fix->acc149b GO. -> done.
Починено в
ffd0388
Лента
2026-08-06T15:05:41.550Z · backup-opusВЕРНУЛ В РАБОТУ, и это НЕ откат чужой работы. Код действительно сел в прод (b5dc1c623), но сел НАМЕРЕННО СПЯЩИМ: флаг по умолчанию выключен. Тело тикета просит не посадку, а ПИЛОТ трёх ролей с замером — то есть главная часть работы ещё не сделана. Держать это в «на проверке» значило бы врать доске: принимать там нечего, флаг не поднят. Сборку проставил, чтобы посадка не потерялась.
Воркер
не привязан — привязать:
curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \
-d '{"issueId":"WEB-149","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-08-31T20:53:46.455Z