WEB board

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

QIMPL-1: NON_USER ledger SQL — корректный mapping payer_id/operation_id + repair обоих payer kinds

Закрыт P1 · важно ведёт: —
Суть
Из qreview7 §5.1: NON_USER ledger SQL не вызывался реально; нужен корректный mapping payer_id/operation_id, прогон normal completion и FAILED_ACCOUNTING repair для ОБОИХ payer kinds (USER/NON_USER). Контракт: WEB094-QUEUE-DESIGN-v7.md (ЗАМОРОЖЕН как дизайн-контракт 25.08 по решению owner «v7 последний дизайн-раунд»). Блокер из qreview7-sol-report.md (A1), закрывать КОДОМ с приёмкой: негатив-тест обязателен, приёмщик ≠ автор, исполнимый SQL-прогон в чистой схеме как в ревизии.
Доказательства
[25.08 ~14:55] Взято волной qimpl1 (luna xhigh, A1, wt-qimpl1 от c3fb44e): контракт v7 в репо (docs/billing/QUEUE-CONTRACT-V7.md FROZEN + DDL в prisma/queue-v7/, аддитивно, без применения на прод), точечный фикс NON_USER ledger mapping, доказательство исполнением в чистом PostgreSQL (оба payer kinds, normal+repair), негатив на битый mapping.

[25.08 ~15:20] СДЕЛАНО волной qimpl1: commit cd7b8ea. Контракт v7 байт-в-байт в docs/billing/QUEUE-CONTRACT-V7.md (FROZEN-шапка), DDL аддитивно в prisma/queue-v7/ (НЕ prisma/migrations, на прод не применялось). Правка QIMPL-1 точечно в 080-projection.sql (payer_id='non-user', operation_id из SpendOperation, read-back по operation_id+idem-key, коммент QIMPL-1). Доказано исполнением: PostgreSQL 16.14 чистая схема, create-pass rc=0 (functions=24, tables=44 дословно), smoke normal+FAILED_ACCOUNTING repair для USER и NON_USER. Честный скоуп: §5.2-5.5 НЕ заявлены. → review.

📎 Трейл: отчёт /home/ubuntu/waves/QIMPL1-REPORT.md; ветка qimpl1@cd7b8ea (в проде 54 как файлы prisma/queue-v7/, merge ce4aa0c). Контракт: docs/billing/QUEUE-CONTRACT-V7.md (FROZEN).
[2026-08-25 19:11Z] Независимая приёмка запущена: волна accqimpl на A1 (sol xhigh, приёмщик≠автор), worktree от прод-коммита a453b16; обязательны негативные тесты новых классов; для не-влитых тикетов волна установит факт NOT-LANDED. Бриф /home/ubuntu/waves/accqimpl-brief.md, отчёт будет в ACCQIMPL-REPORT.md.
[2026-08-25 20:05Z] NO-GO приёмки accqimpl: projection read-back NON_USER ledger проверяет только operation_id+idempotency_key, НЕ проверяет payer_id/amount_micros/canonical id; при ON CONFLICT DO NOTHING заранее подсаженная строка с чужим payer_id=attacker-payer и amount=999 проходит как committed truth (позитив работает, дыра в идемпотентном read-back). Фикс-волна web357fix (sol, монетарная зона) запущена.
[2026-08-25 20:36Z] ФИКС ГОТОВ (web357fix GO @92559fe): read-back NON_USER ledger теперь требует ВСЮ каноническую строку (id/payer_id=non-user/operation_id/amount_micros=-terminalMicros/idempotency_key); poison-fixture приёмщика (payer=attacker,amount=999) больше не проходит; позитив NON_USER repair рабочий; GO-evidence 358-361 зелёные. → review, ждёт re-accept независимым (не тем же приёмщиком).
[2026-08-25 21:35Z] Независимый re-accept reacc357 (luna, другой приёмщик) запущен: свежие негативы poison-классов + проверка идемпотентности легального replay.
[2026-08-25 21:42Z] ПРИНЯТ reacc357 (независимый повторный приёмщик, НЕ автор/НЕ accqimpl): GO. 3 poison-класса fail-closed (чужой payer_id / чужая сумма / чужой canonical id), ⭐ легитимный replay остался ИДЕМПОТЕНТНЫМ (фикс не сломал legal replay), NON_USER repair работает, 358-361 GO сохранены. Одноразовый Postgres 16.14. → done, войдёт в посадку 57.
Починено в
a453b16
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-357","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-08-25T21:43:27.535Z