WEB board

всеканоны и докиворкеры↗ iOS↗ Легаси
WEB-447 · Задача · — · web

P1: переподпись — 78К фрагментов остались без подписи: воркер ставит done при 0 подписанных (18 ист.) + 992 content-NULL источника вне очереди

Закрыт P2 ведёт: —
Суть

--- 30.08 ~08:20Z (Фабл): круг 7 (indextail7) АВТОРСКИ ЗЕЛЁНЫЙ.
Волна 752: закрыты находки ACCINDEXTAIL6 — карантин stale body writers (chunk.sourceContentRevision, status=skipped/stale-source-content), forced-indexing не обнуляет contentHash/contentRevision. Real-PG (143 миграции) verifier-ы кругов 5/6/7 PASS. Коммит 6b26636a2258b2f06d0d6f10975518f22c065e49 (ветка wave/indextail7). Отчёт: A1 /home/ubuntu/waves/INDEXTAIL7-REPORT.md. Приёмка 754-accindextail7 в очереди (свежий приёмщик, негативы as-is, охота на новые классы, проверка legacy-fixture-заявления автора на 319ba292).

--- 30.08 ~08:35Z (Фабл): приёмка круга 7 = NO-GO (свежий приёмщик, 754/accindextail7, ветка wave/accindextail7 60a20372).
Негативы кругов 5/6/7 PASS as-is; собственный hunt дал НОВЫЙ bypass: vectorStore.ts:270-289 верит caller-supplied digest, processDocumentRuntime.ts:139-168 копирует digest полного content в произвольные options.chunks → stale custom chunk получает revision живого Source и попадает в retrieval как fresh. Репро: scripts/acceptance/indextail7-digest-proof-verifier.test.ts. Legacy-fixture-заявление автора подтверждено (pre-existing на 319ba292, не регрессия).
ЭТО 4-Й КРУГ ОДНОГО КЛАССА (freshness→FNV→stale-writer→digest-bypass) → круг 8 (755-indextail8, в очереди) требует смены устройства: единая server-side точка приёма chunk↔body binding (offsets+пересчёт digest среза), полная матрица мусора, инвентарь всех путей записи. Отчёт приёмки: A1 /home/ubuntu/waves/ACCINDEXTAIL7-REPORT.md.

--- 30.08 ~09:12Z (Фабл): круг 8 АВТОРСКИ ГОТОВ (волна 755, коммит 0b652fd0, ветка wave/indextail8) — СМЕНА УСТРОЙСТВА, не латка: единственная точка записи DocumentChunk = writeSourceBoundChunks (src/lib/rag/sourceChunkWriter.ts): серверный snapshot Source в транзакции, offsets обязательны, точное равенство slice, SHA-256 среза пересчитывает СЕРВЕР (caller digest никогда не proof), revision строго равна серверной, server-keyed HMAC. Отчёт: A1 /home/ubuntu/waves/INDEXTAIL8-REPORT.md. Приёмка 759-accindextail8 в очереди — ключевые вопросы: единственность точки (греп всех путей записи), репро digest-bypass круга 7 as-is, слом offsets/конкурентности/UTF-16-граней, оценка legacy full-body канала.

--- 30.08 ~09:45Z (Фабл): приёмка круга 8 (ACCINDEXTAIL8) = строгий NO-GO при ЗАКРЫТОМ классе: digest-bypass круга 7 as-is теперь отказывает (accepted=[], stale-source-content), негативы кругов 5-8 все PASS (6/6+1) на реальном PG, но полный греп-аудит нашёл 3 пути записи мимо writeSourceBoundChunks: (1) ПРОДАКШН-РИСК actions.ts:7324 — update index/offsets вне writer, ломает HMAC (index входит в подпись) и не доказывает offsets по Source snapshot; (2) maintenance HMAC-backfill raw UPDATE (sourceReadGateway.ts:398); (3) скрипт seed-graph-audit-corpus.cjs. Круг 9 поставлен (762-indextail9): свести все пути к writer-модулю, ремонт offsets с пересчётом HMAC и доказательством по snapshot, guard-ы на исключения. Отчёт: A1 /home/ubuntu/waves/ACCINDEXTAIL8-REPORT.md.

--- 30.08 ~10:20Z (Фабл): круг 9 АВТОРСКИ ГОТОВ (b179ac49, wave/indextail9) — все 3 обходных пути сведены к writer-модулю/огорожены. Приёмка 765-accindextail9 в очереди (свой греп-инвентарь, negative offset-repair, hunt-тест приёмщика 8 as-is, проверка штатного queue-пути воркера). GO 765 = добивка индекса разблокирована.

--- 30.08 ~10:45Z (Фабл): приёмка круга 9 (ACCINDEXTAIL9) = NO-GO с ВАЖНЫМ поворотом: инвентарь путей записи принят ЧИСТЫМ (0 runtime-путей мимо writer; repair/backfill/guard-негативы зелёные, repairedIntegrityValid=true), но обязательный критерий «retrieval после repair» упёрся в PRE-EXISTING дефект: vectorStore.similaritySearch() зовёт несуществующий this.formatVector (vectorStore.ts:419, живёт с base 752c7982) → semantic retrieval падает «this.formatVector is not a function». Круг 10 поставлен (768-indextail10): починить единым форматтером записи/чтения, прогнать verifier приёмщика as-is, и ГЛАВНОЕ — выяснить какие продовые пути зовут мёртвый similaritySearch (возможная связь с ролевыми отказами). Отчёт: A1 /home/ubuntu/waves/ACCINDEXTAIL9-REPORT.md.

--- 30.08 ~11:50Z (Фабл): круг 10 АВТОРСКИ GO за 9 мин (faca0f11, wave/indextail10). История дефекта вскрыта: formatVector удалён в 0b652fd0 (круг 8) вместе с переносом writer-а, но 4 read-вызова остались — дефект ЖИЛ ТОЛЬКО В ВЕТКЕ кругов 8-9, прод l71/l72 чист (там старый vectorStore с методом). Фикс: единый экспорт formatVector из sourceChunkWriter, все 4 read-пути + maintenance на одном форматтере (гарантия запись=чтение). Несуществующих this.* в классе больше нет (проверено). Verifier приёмщика 9 as-is зелёный. ФИНАЛЬНАЯ приёмка линии 773-accindextail10 в очереди — её GO = добивка индекса стартует.

--- 30.08 ~12:25Z (Фабл): ⭐ ЛИНИЯ ЗАКРЫТА: ACCINDEXTAIL10 = GO, WEB-447_LINE_VERDICT=GO (кандидат в l73: wave/indextail10 faca0f11, вся цепочка кругов 5-10). ДОБИВКА НАЧАТА: backfill dry-run на проде (l72c, sudo env, node --conditions=react-server) — счётчики: 96574 total, 25069 signed, 71505 unsigned; ВСЕ 71505 отклонены missing-embedding-text-hash (embedding есть, text-hash NULL). Просто подписать нельзя — нужен либо локальный дериват text-hash (если контракт подписи допускает), либо re-embedding с капом. Волна 776-tailsigner разбирает семантику и готовит инструмент (деньги не тратит). Отчёты: A1 ACCINDEXTAIL10-REPORT.md; Pi /tmp/backfill-dry.json.

--- 30.08 ~13:20Z (Фабл): волна tailsigner ГОТОВА (b92a7beb, wave/tailsigner) — ЧЕСТНЫЙ вердикт: локальный derive ОТВЕРГНУТ (подпись не свидетельствует связь текст↔вектор — криптографически связь нигде не закодирована), нужен provider-backed re-embedding. ЦЕНА: $0.26-0.86 с 20% запасом (71505 chunks, text-embedding-3-small; таблица оценок в отчёте). Инструмент: EmbeddingIntegrityReembedJob (immutable scope, keyset cursor, lease, CAS-запись — результат провайдера не перезапишет изменившуюся строку), scripts/reembed-embedding-integrity.ts (preview/create-job/apply с капами). Второй гейт учтён: строки без свежего source snapshot в job НЕ входят — им нужен штатный re-index. Отчёт: git show b92a7beb:TAILSIGNER-REPORT.md. Owner-у отправлен запрос на трату (id=15233). Приёмка 778-acctailsigner в очереди (денежный гейт: dry-run без провайдера, CAS, lease, обрыв/докат). Тех-заметка: волна закоммитила отчёт в ветку, но сессия умерла до копирования в /waves — маркер поставлен координатором.

--- 30.08 ~14:05Z (Фабл): денежная приёмка ACCTAILSIGNER = NO-GO/GO-HOLD, но ЯДРО ПОДТВЕРЖДЕНО ЦЕЛИКОМ: миграция идемпотентна на грязной базе, dry-run = 0 provider-вызовов (mock counter), CAS (изменение строки между read/write → не перезаписана), immutable scope, lease-гонка, crash recovery без дублей/пропусков, цена пересчитана независимо. Два пункта HOLD: (1) CLI падает на server-only — координатор проверил: с NODE_OPTIONS=--conditions=react-server работает, нужен закреплённый launcher; (2) legacy verifier-10 fixture несовместим с pair-guard (тот самый pre-existing seed) — rebase. Микро-круг 783-tailsigner2 поставлен. После его GO + быстрой пере-приёмки — ЗАПУСК добивки (добро owner-а на $0.26-0.86 получено 11:53). Отчёт: A1 ACCTAILSIGNER-REPORT.md.

=== КАРТА ЛИНИИ ДЛЯ НУЛЕВОГО АГЕНТА (30.08, Фабл) ===
Суть: воркер переподписи ставил done при 0 подписей; выросло в закрытие целого класса «stale/чужой контент читается как валидный» + инструмент добивки.
ХРОНОЛОГИЯ КРУГОВ (все отчёты на A1 в /home/ubuntu/waves/, ветки в объектной базе /home/ubuntu/waves/wt-l59-gdiag/.git):
• К5 freshness: INDEXTAIL5-REPORT.md / ACCINDEXTAIL5-REPORT.md
• К6 FNV-collision: INDEXTAIL6 / ACCINDEXTAIL6 (NO-GO→К7)
• К7 stale-writer: INDEXTAIL7 (6b26636a) / ACCINDEXTAIL7 NO-GO: digest-bypass (тест indextail7-digest-proof-verifier)
• К8 СМЕНА УСТРОЙСТВА: writeSourceBoundChunks = единственная точка записи (0b652fd0) / ACCINDEXTAIL8 строгий NO-GO: 3 обходных пути
• К9 сведение путей (b179ac49) / ACCINDEXTAIL9 NO-GO: pre-existing formatVector
• К10 formatVector общий экспорт (faca0f11) / ACCINDEXTAIL10 GO = ЛИНИЯ ЗАКРЫТА. Кандидат l73 = wave/indextail10 (faca0f11).
ДОБИВКА: dry-run прода: 96574 total / 25069 signed / 71505 без embeddingTextHash (Pi /tmp/backfill-dry.json). Волна tailsigner (b92a7beb): derive отвергнут честно, re-embedding $0.26-0.86, инструмент EmbeddingIntegrityReembedJob + scripts/reembed-embedding-integrity.ts (launcher: NODE_OPTIONS=--conditions=react-server). Owner-добро на трату: телега 30.08 11:53. Приёмка ACCTAILSIGNER: ядро GO, HOLD на launcher+verifier10 → круг tailsigner2 (wave/tailsigner2).
Связанные: WEB-320 (леджер посадок), WEB-99 (роли ждут добивку), WEB-093/WEB-283 (движок).

--- 30.08 ~14:50Z (Фабл): tailsigner2 готов (08690cf5): launcher bin/reembed-launcher.sh (root-независимый, NODE_OPTIONS=--conditions=react-server), verifier-10 seed rebased без ослабления guard, ядро не тронуто. Финальная денежная приёмка 790-acctailsigner2 в очереди — её GO = запуск добивки.

--- 30.08 ~16:20Z (Фабл): ACCTAILSIGNER3 = GO («добивка стартует»), но live-preview против прод-БД упёрся: freshness-предикат инструмента требует колонку c.sourceContentRevision — она приезжает ТОЛЬКО с indextail-миграциями (линия кругов 5-10, кандидат l73). Инструмент строился на базе wave/indextail10 — корректно, но значит ПОРЯДОК: посадка l73 (merge уже летит, 803) → prisma-миграции indextail на прод → добивка. Миграция очереди 20260830140000 уже применена live (CREATE TABLE/INDEX, аддитивная). Предварительные грубые цифры прода (моим приближённым SQL): eligible-fresh ~6.7К в 2 доках, остальное — на штатный re-index (точные цифры даст preview после l73). Хотфикс l72d (сохранение облака) собирается на M4 параллельно.

--- 30.08 ~18:30Z (Фабл): ⭐⭐ ДОБИВКА ЗАВЕРШЕНА: job web447-tail-20260830 complete, 6678/6678 processed, 0 skipped, 0 races; DocumentChunk signed 25069→31747. Остаток 64827 без embeddingTextHash = чанки СТАРЫХ source-ревизий (не-fresh snapshot) — по контракту инструмента им нужен штатный re-index источников либо чистка сирот (разбор отдельной волной). Расход: ~745К токенов ≈ центы (в рамках owner-добра $0.26-0.86). Следующее: батарея ролей на l73.
Доказательства

[30.08 00:05Z] INDEXTAIL сдан (8a87bd1f): корень 1 подтверждён (done ставился при chunks=0 в sourceIndexingQueue), корень 2 (предикат content IS NOT NULL), ключевой факт: HMAC считается от текста фрагмента+вектора — batch-подпись БЕЗ re-chunking возможна. → ACCINDEXTAIL (queue/707, матрица мусора обязательна). После GO: координатор исполняет план добивки → повторная батарея ролей.

[30.08 10:20Z] ACCINDEXTAIL NO-GO: матрица мусора ПРОШЛА (подписывается только валидное, chunks=0 честен), но dry-run врёт о будущем статусе, settlement уязвим к гонке нового чанка, CLI не стартуют tsx. → INDEXTAIL2 (queue/720: одна функция решения для dry/apply, share-lock settlement, доказанный запуск CLI). Добивка индекса и батарея ролей ждут этот GO.

[30.08 11:00Z] INDEXTAIL2 GO (29d54d18). → ACCINDEXTAIL2 (queue/724: dry=apply полем к полю, гонки, матрица мусора). GO открывает добивку индекса → батарею ролей.

[30.08 11:40Z] ACCINDEXTAIL2 NO-GO: детерминированная гонка внутри apply даёт ложный done → INDEXTAIL3 (queue/728: атомарный условный UPDATE WHERE NOT EXISTS(unsigned)). Добивка/батарея ролей ждут.

[30.08 12:20Z] INDEXTAIL3 GO (bac1fe02: атомарный settlement) → ACCINDEXTAIL3 (queue/732). Детерминированная проба гонки — ключевой негатив.

[30.08 13:00Z] ACCINDEXTAIL3 NO-GO (доверие по ненулевости колонок без пересчёта HMAC/chunkCount) → INDEXTAIL4 (queue/736).

[30.08 13:40Z] INDEXTAIL4 GO (e84e76b2) → ACCINDEXTAIL4 (queue/740).

[30.08 14:20Z] ACCINDEXTAIL4 NO-GO (старый HMAC-валидный чанк после смены content → ложный done) → INDEXTAIL5 (queue/743: свежесть по sourceContentHash предикатом линии WEB-442).

[30.08 15:00Z] INDEXTAIL5 GO (c192b487: свежесть предикатом WEB-442) → ACCINDEXTAIL5 (queue/746).

[30.08 15:40Z] ACCINDEXTAIL5 NO-GO: исполнимая КОЛЛИЗИЯ FNV-1a/32 (два тела — один hash) → INDEXTAIL6 (queue/748: УСТРОЙСТВО — монотонная Source.contentRevision через расширение pair-guard, chunk.sourceContentRevision, freshness по ревизии; hash остаётся вторым поясом). Затрагивает и линию WEB-442 (32-битность её хеша — известное ограничение, ревизия его закрывает для freshness).

[30.08 16:20Z] INDEXTAIL6 сдан (319ba292: contentRevision устройство) → ACCINDEXTAIL6 (queue/750: replica/restore на ревизию, бэкфилл-политика, коллизионная пара).

[30.08 23:05Z] ACCINDEXTAIL6 NO-GO (7-й круг ставится, детали в отчёте приёмки; verifier c663d798) → INDEXTAIL7 (queue/752).
Лента
2026-09-02T14:40:27.646Z · Fable
[A1 14:40 Фабл] Волна 1229-web447orphans (M4) сдана: 8a52d0d7a29852297e2b4b7296422fa2059688c8 — scripts/chunk-revision-report.ts (dry-run по умолчанию; классы: сирота/только-старые→requeue/удалён-poisoned/referenced-stale; --apply с --operator/--reason, батчи ≤1000, журнал; --requeue штатно). Приёмка 1238-accweb447orphans (M4, одноразовый PG). После GO — dry-run на проде из дерева l92 по 64 827 чанкам → классы → apply с журналом.
2026-09-02T14:49:08.397Z · Fable
[A1 15:02 Фабл] Живые входные данные для инструмента 1229: после unpoison WEB-465 два источника (cmt4e09wp00l…, cmt4e09lo00f…) ушли в skipped/stale-source-content — это ровно класс «чанки старой ревизии при отсутствии/наличии текущих», который классифицирует chunk-revision-report.ts; включить их в dry-run на проде как контрольные примеры (ожидание: класс (б) requeue или (а) сирота — по данным).
2026-09-02T14:57:21.351Z · Fable
[A1 14:58 Фабл] Приёмка 1238-accweb447orphans (M4, real PG): классификация/--apply/--requeue/FK-защита/идемпотентность подтверждены, **NO-GO** по gate-ам: (1) report-vs-diff — integration-тест chunk-revision-report.integration.test.ts не назван в отчёте; (2) check:tsc-no-regression 2663 vs baseline 2136 (в т.ч. свой TS7006 implicit any в тесте; остальное — вероятно дрейф baseline). Корректировка 1244-web447orphans2 (M4): починить свои диагностики, полный список файлов в отчёте, доказательство дрейфа baseline сравнением с чистым l90-деревом.
2026-09-02T15:18:44.825Z · Fable
[A1 15:20 Фабл] Корректировка 1244-web447orphans2 сдана: 45cfb905 (только TS7006 в integration-тесте + полный список файлов в отчёте; логика не менялась). Повторная приёмка 1251-accweb447orphans2 (M4): report-vs-diff, tsc-no-regression с диффом против чистого l90 (дрейф baseline — отдельно), быстрый повтор PG-матрицы. Линия на A2/A1 mirrors → l92 после GO → dry-run на проде.
2026-09-02T15:39:48.702Z · Fable
[A1 15:47 Фабл] Повторная приёмка 1251-accweb447orphans2 (M4): gate-ы и PG-матрица PASS, tsc author-only = 0 (дрейф baseline — отдельно), но **NO-GO** по сохранённому safety-блокеру 1238 §4: JSON-отчёт кладёт ≤5 examples на класс и не содержит полного reference inventory {chunkId, location} — при ≥6 referenced-stale оператор не видит защищённые ID перед --apply. Корректировка 1259-web447orphans3 (M4): полный referenceInventory (ids/locations, без содержимого; --inventory-out для больших), тест с ≥6 ссылками. Урок для брифов: safety-требования приёмки повторять в брифе корректировки явно — автор 1244 закрыл только TS7006.
2026-09-02T16:22:30.537Z · Fable
[A1 16:30 Фабл] Третий круг сдан: волна 1259-web447orphans3 (M4), коммит 426a9416 (ветка web447orphans3-1259, база l91-candidate). Отчёт автора: M4 /Users/milamarty/waves/WEB447ORPHANS3-REPORT.md. Эволюция: 1201? → 1238 NO-GO (gate-ы: dry-run по умолчанию, лимит на --apply) → 1244 → 1251 NO-GO (нет полного reference inventory перед удалением) → 1259. Дальше: приёмка 1262-accweb447orphans3 (M4, реальный PG: инвентарь ссылок по schema.prisma, protected-сценарии, идемпотентность, прерывание батча) → GO → dry-run на проде из enforce-обёртки → --apply с лимитом → l92. Бандл: A1 /home/ubuntu/mirrors/lines/web447orphans3-1259.bundle; A2 ref web447orphans3-1259/head.
2026-09-02T16:43:07.918Z · Fable
[A1 16:50 Фабл] Приёмка 1262-accweb447orphans3 = NO-GO (M4 /Users/milamarty/waves/ACCWEB447ORPHANS3-REPORT.md), четыре блокера: (1) нет явного --limit для --apply (MAX_BATCH_SIZE — внутренний батч, не операторский кап); (2) скан ссылок = один bind на id → P2035 «too many bind variables» при 78К (лимит 32767) — полный dry-run на реальном объёме невозможен; (3) интеграционный тест автора падает на свежей схеме (P3018 от P11-миграций с CONCURRENTLY — чинится в WEB-477) и потому не является гейтом; (4) нет объекта отчёта по батчам и нет фенса гонки: удаление перепроверяет только WikiClaim, JSON/FK-ссылки, добавленные после инвентаря, не видит. Прошло: логика классификации на малой фикстуре, tsc/eslint/boundary/undeclared, check-report-vs-diff. → волна 1265-web447orphans4 (M4, реальный PG): обязательный --limit, скан пачками/temp-table (тест ≥40К id), тест на схеме через db push, batches[] в отчёте + транзакционная перепроверка всех классов ссылок + advisory lock на прогон. Эволюция: 1201 → 1238 NO-GO → 1244 → 1251 NO-GO → 1259 → 1262 NO-GO → 1265.
2026-09-02T17:15:05.597Z · Fable
[A1 17:28 Фабл] Четвёртый круг сдан: волна 1265-web447orphans4 (M4, реальный PG), коммит 3582e6bb (ветка web447orphans4-1265, база l91-candidate). Отчёт автора: M4 /Users/milamarty/waves/WEB447ORPHANS4-REPORT.md. Заявлено: обязательный --limit для --apply (кап удалений за прогон), скан ссылок пачками без P2035 (≥40К в тесте), интеграционный тест на свежей схеме независимо от P11-миграций, batches[] в отчёте + транзакционная перепроверка всех классов ссылок + advisory lock на прогон. Дальше: приёмка 1282-accweb447orphans4 (M4, другой verifier: инвентарь ссылок по schema.prisma, конкурентный writer, kill посреди батча, 40К фикстура) → GO → dry-run на проде из enforce-обёртки → --apply с лимитом → l92. Бандл: A1 /home/ubuntu/mirrors/lines/web447orphans4-1265.bundle; A2 ref web447orphans4-1265/head.
2026-09-02T18:56:44.025Z · Fable
[A1 19:15 Фабл] Приёмка 1282-accweb447orphans4 (M4, реальный PG) = NO-GO: главный блокер — отчётный контракт: после 2500 сирот и --limit 1000 удалено ровно 1000, но ни JSON, ни stdout не сообщают остаток (нет remaining); прошло: лимит/кап, bind-лимит (40К без P2035), инвентарь/protected/гонка/advisory lock/kill/идемпотентность; отдельно worker-suites 66/67 и 4/5 — сверить с базой. → волна 1316-web447orphans5 (M4): поле remaining в JSON и stdout + строка «остаток N — повторите», проверка suites против l91-candidate.
2026-09-02T20:22:03.560Z · Fable
[A1 20:25 Фабл] 1316-web447orphans5 сдана: коммит 4eca04466d (M4), круг 5 по сиротам. Дальше: приёмка 1340 (M4) против ACCWEB447ORPHANS4.
2026-09-02T20:59:16.299Z · Fable
[A1 21:00 Фабл] Приёмка 1340-accweb447orphans5 = GO (M4, отчёт /Users/milamarty/waves/ACCWEB447ORPHANS5-REPORT.md; лог волны оборвался на лимите Codex уже после записи маркера — вердикт и отчёт полные, проверить при сборке l94). Линия web447orphans5 (4eca04466d) → l94.
2026-09-02T23:32:27.283Z · Fable
[A1 03:55 Фабл] В проде l94 (сироты круг 5, GO 1340).
2026-09-03T07:18:44.970Z · Fable
[A1 12:48 Фабл] Триаж 03.09 (owner: «чего они ждут — срочно принимайте, сажайте»): линия в проде l95 (INDEXTAIL10 faca0f11, GO 30.08; сироты круг 5 4eca0446 в l94 — предок l95-candidate f7b9a4fd, посадка через l71/l72/l73); доска не была переключена. Закрываем.
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-447","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-09-03T07:18:44.992Z