WEB board

всеканоны и докиворкеры↗ iOS↗ Легаси
WEB-679 · Дефект · billing · web

Stripe: покупка КРЕДИТОВ и подписка должны работать до конца в test-режиме (пополнение → кошелёк → кабинет), до Live

Закрыт P1 · важно ведёт: —
Суть
Команда владельца 22.09 19:51 (голос): «по Stripe надо и подписку уметь, и кредиты покупать… у нас есть попап сверху, что денег недостаточно… всё должно работать, пускай даже без Live Stripe: через песочницу пополнили сколько надо, и всё это в кабинете появилось».

Факты по коду (l115q cc278e18) и бою на 22.09:
- Пути «пополнение → кредиты кошелька» НЕТ: `POST /api/billing/payment-webhook/[provider]` (stripe|paypal|revolut → `creditFromPayment`) — заглушка, `isBillingTopupStubEnabled()` требует `BILLING_TOPUP_STUB_ENABLED=true` И не-production → на бою 404 при любых секретах.
- Основной вебхук `/api/billing/webhook`: `payment_intent.*` = только бета-наблюдатель WEB-511 (холд €10 без capture, succeeded = инцидент); `checkout.session.completed` = подписочный grant, закрыт `STRIPE_BETA_FREE_MODE=1` (`betaModeGate.ts`).
- Бой: test-ключи `sk_test`/`pk_test`, `STRIPE_WEBHOOK_SECRET` задан, `NEXT_PUBLIC_STRIPE_PRICE_*` не заданы.
- UX: попап «недостаточно средств» существует (со слов владельца) — куда ведёт и есть ли экран пополнения, выясняет волна 4623.

Нужно:
1) Карта: где в UX пополнение/попап, что вызывает, какой API; чего не хватает до рабочего пути «Stripe Checkout mode:payment → payment_intent.succeeded → creditFromPayment → баланс в кабинете» (4623, чтение).
2) Правка: включить путь пополнения (не заглушку): Checkout с `payment_intent_data.metadata.userId`, обработка в основном вебхуке (или снять «не-production» с приёмника с гейтом по env), идемпотентность по providerRef, отображение баланса; подписка — снять бету по флагу, Price ID.
3) Проверка в test-режиме на стенде: карта 4242, вебхук через `stripe listen`/подписанный POST, SELECT по кошельку, скрин кабинета. Только после лестницы ёмкости (стенд занят).
4) Live (WEB-516) — после того как test-режим показал деньги в кабинете.
Связано: WEB-511 (бета-холд), WEB-516 (Live), WEB-566 (кабинеты), WEB-677 (сервисный кошелёк индексации — другое).
Чем закрывается (приёмка)
На стенде в test-режиме: пополнение на N через Stripe Checkout → баланс кошелька пользователя = N в кабинете (скрин + SELECT); повторная доставка вебхука не удваивает; подписка Voice Pro в test-режиме выдаёт тариф; тесты зелёные; на бою путь закрыт до отдельного решения о Live.
Лента
2026-09-22T20:00:10.764Z · coordinator
[22.09 20:00Z координатор] ## 4622 (A2, GO, 5/5) — цепочка ПОДПИСКИ по коду: пользователь получает ТАРИФ, кошелёк не пополняется

- `checkout.session.completed` (после снятия `STRIPE_BETA_FREE_MODE`) → `Subscription.tier` = `voicePro`/`teamVoice` по Price ID (`pricing.ts:42-77`, `getTierForPriceId`), строки `BillingSubscriptionAggregate/ProviderSnapshot/EventReceipt/NotificationOutbox`, `WebhookDedupe`; тариф открывает лимиты (`features.ts:253-309`). `WALLET_CREDITED=НЕТ`: вебхук подписки не зовёт `creditFromPayment`/`UserBalance`/`LedgerEntry`.
- Предусловия checkout: `ageConfirmed`, `paymentConsentAccepted`, EU/UK-решение (`R14_EU_MODE=paid_self_serve`), тариф не free; тир берётся из цены провайдера, не из metadata.
- **`NEEDS_REBUILD=ДА`**: `NEXT_PUBLIC_STRIPE_PRICE_VOICE_PRO/TEAM_VOICE`, `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`, `NEXT_PUBLIC_APP_URL` вшиваются при сборке → для test-режима на стенде нужна пересборка с тестовыми Price ID; для Live (WEB-516) — пересборка боевого артефакта с live-значениями. 16 env-имён в `env-names.md`; рецепт стенда (подпись вебхука HMAC t.rawBody, карта 4242, SELECT'ы, откат) в `stand-recipe.md`.
- Итог для владельца: подписка в песочнице проверяема без Live, но после пересборки стенда; «кредиты за деньги» — отдельный путь (4623 → правка).
- Сдача: `a2:/home/ubuntu/waves/4622STRIPESUBSCRIPTIONTESTMODERECIPE-evidence/` (`chain.md`, `env-names.md`, `stand-recipe.md`, `tests.md`).
2026-09-22T20:09:05.212Z · coordinator
[22.09 20:09Z координатор] ## 4623 (A2, GO, 6/6) — карта пополнения кредитов: попап есть, экрана пополнения и Checkout `mode:payment` НЕТ; план минимальной правки ~300 строк

- UX: `src/components/layout/LowBalanceBanner.tsx:54` (fixed-баннер «мало средств») и чат-пузырь «Insufficient AI funds» — ведут на `/settings/wallet`, Stripe Checkout не запускают; `TOPUP_SCREEN=НЕТ`.
- Сервер: `create-checkout` только `mode:'subscription'`; кредитная функция `creditFromPayment` (`paymentCredit.ts:93-118`, ledger `cash_topup`, идемпотентность по `providerRef`) есть, но доступна лишь через тёмную заглушку `payment-webhook/[provider]`.
- Выбран путь `main-webhook`: 6 пунктов (G1 Checkout `mode:'payment'` с `payment_intent_data.metadata {userId, billingFlow:'wallet_topup'}` под гейтом `BILLING_TOPUP_CHECKOUT_ENABLED`; G2 ветка non-beta `payment_intent.succeeded` в `/api/billing/webhook` рядом с бета-наблюдателем, только с меткой `wallet_topup`, USD, `amount_received`; G3 CTA в баннере + пакеты сумм; G4 обновление кабинета `/settings/wallet?checkout=success` с опросом `/api/wallet/balance`; G5 тесты; G6 счётчики, `charge.succeeded` не обрабатывать). Риски/митигации: двойное начисление, чужой кошелёк, бета-инцидент, подписочный PI как пополнение.
- Черновик `draft.diff` (116 строк, 5 файлов) и `patch-plan.md` в `a2:/home/ubuntu/waves/4623STRIPETOPUPCREDITSUXMAPANDPATCHPLAN-evidence/`. Тесты 45/46 (один падает на синтетической фикстуре `preauthHold`, не наш путь).
- Дальше: волна-правка на ветке от l115r (`1a5822a8`) → тесты → стенд после лестницы.
2026-09-22T20:58:27.423Z · coordinator
[22.09 20:58Z координатор] ## 4625 (M1, GO, 6/6) — покупка кредитов реализована в коде: коммит `25b6d5be` на ветке `l115s-topup` (от 1a5822a8), 14 файлов, +918/−14

- Checkout `mode:'payment'` под гейтом `BILLING_TOPUP_CHECKOUT_ENABLED` (умолчание выкл.), allow-list пакетов, `payment_intent_data.metadata {userId, billingFlow:'wallet_topup'}`; в `/api/billing/webhook` — ветка `payment_intent.succeeded` только с меткой `wallet_topup` → `creditFromPayment` (идемпотентно, конфликт = ACK без кредита + лог); бета WEB-511 и заглушка `payment-webhook/[provider]` не тронуты; CTA в `LowBalanceBanner`, обновление `/settings/wallet?checkout=success` с опросом баланса.
- Тесты: новые 16/16, существующие billing 103/103; typecheck затронутых файлов 0 новых ошибок (база 329; полный `tsc` на M1 не влез в 8 ГБ). `NEEDS_REBUILD=НЕТ` новых `NEXT_PUBLIC_*` — но сама правка серверная/клиентская → для стенда всё равно новая сборка.
- Патч перенесён на A2: ветка `l115s-topup` в `/home/ubuntu/wt-l115s` (`git am`). Дальше: после лестницы — сборка стенда из этой ветки с тестовыми `NEXT_PUBLIC_STRIPE_*`, проверка картой 4242 по `stand-check.md`.
- Сдача: `m1:~/waves/4625STRIPEWALLETTOPUPIMPLEMENT-evidence/` (`commit.patch`, `tests.md`, `typecheck.md`, `env-names.md`, `stand-check.md`, `worktree.md`).
2026-09-23T11:59:38.290Z · coordinator
[23.09 11:59Z координатор] ## Координатор 23.09 12:1xZ — блокер «нужны ключи/Price ID от владельца» снят: тестовые ключи Stripe (sk_test/pk_test/webhook) уже стоят в окружении боя (`STRIPE_BETA_FREE_MODE=1`), от владельца ничего не требуется (его 11:56: «слал 5 раз»). Две тестовые цены подписок созданы через Stripe API (test mode, USD, месяц): `NEXT_PUBLIC_STRIPE_PRICE_VOICE_PRO=price_1UIop6EBncgzhYfvT4swtcz1` (Voice Pro $19), `NEXT_PUBLIC_STRIPE_PRICE_TEAM_VOICE=price_1UIop7EBncgzhYfvDS4VaDNY` (Team Voice $25); lookup_keys `voice_pro`/`team_voice`. Прописаны в NEXT_PUBLIC-env боя (резерв `.pre-stripe-prices-*`; в l115r не используются) и в parity-снимок сборки A2. Пакеты кредитов — по коду (`TOPUP_PACKAGES` $5/$10/$20/$50, inline price_data). Ветка `l115s-topup` (4625) включается в стендовую сводку all4 → раскладка с тестовыми ключами на стенде → e2e по `4625 stand-check.md` (карта 4242, вебхук, ledger). Независимая приёмка топапа (деньги) — отдельной волной на M1.
2026-09-23T12:15:03.532Z · coordinator
[23.09 12:15Z координатор] ## 4703 (M1, независимая приёмка топапа, 5/5 OK) — **GO, находок 0**: идемпотентность кредитования по PI (повтор/дубль/чужой user или amount → одна запись), обязательная подпись вебхука, сумма только из PI, пакеты из серверного каталога, флаг выключен → отказ, userId ↔ владелец кошелька, валюта/округление, гонка двух вебхуков, UI не считает успех без ledger — 9/9 PASS. `READY_FOR_L115S=ДА`. Остаток: живой sandbox-прогон на стенде all4 (раскладка 4692 с тестовыми ключами) по `4625 stand-check.md` — координатор.
2026-09-23T15:34:23.410Z · coordinator
[23.09 15:34Z координатор] ## 4718 (A2, Stripe sandbox e2e на стенде all5) — **NO-GO**, но не по коду Stripe: на стенде НЕТ маршрутов пополнения — `/api/billing/producer` 404, `/api/billing/payment-webhook/stripe` 404, страница кошелька 404 → реализация топапа (4625, ветка `l115s-topup`) в сводное дерево all5 НЕ вошла (проверяю состав all5 — в all6 включить обязательно); подписка Voice Pro → `Invalid beta authorization checkout` из-за `STRIPE_BETA_FREE_MODE=1` на стенде — для e2e на стенде флаг снять (только стенд). Ключи test-only PASS, секреты не печатались, `/api/billing/webhook` fail-closed без подписи (PASS). Повтор 4718 после all6 с топапом и `STRIPE_BETA_FREE_MODE=0` на стенде.
2026-09-23T15:36:02.973Z · coordinator
[23.09 15:36Z координатор] ## Поправка к 4718 (координатор): топап 4625 (коммит 8b35e3404) В all5 ЕСТЬ; в релизе стенда есть `/api/billing/payment-webhook/[provider]` (POST /stripe → 403 без подписи/origin, не 404) и `/api/billing/create-checkout` (`kind=wallet_topup`); волна дергала `/api/billing/producer` и `/api/billing/topup`, которых нет — «producer» в 4625 stand-check означает create-checkout. NO-GO 4718 = дефект брифа/интерпретации, не билда. Повтор — 4753 с настоящими маршрутами, корректным `Origin` стенда и `STRIPE_BETA_FREE_MODE=0` на стенде (только стенд).
2026-09-23T15:50:39.369Z · coordinator
[23.09 15:50Z координатор] ## 4753 СНЯТА (координатор, 15:5xZ): волна передала Stripe сырой номер тестовой карты через API — владелец получил письмо Stripe «we don't process charges that include full card numbers» (test mode тоже блокирует raw card data). Процесс убит, стенд не менялся (beta-флаг на месте). Повтор — 4756: только `pm_card_visa`/`pm_card_chargeDeclined`/`tok_visa`, зачисление через PI + подписанный webhook, `RAW_PAN_USED=0` в сдаче.
2026-09-23T16:09:20.703Z · coordinator
[23.09 16:09Z координатор] ## 4756 (A2, Stripe sandbox e2e на токенах, стенд all5) — пополнение кредитов работает сквозь: Checkout/PI (`pi_3UIsWhEBncgzhYfv1I5WETls`) с `pm_card_visa` → подписанный `payment_intent.succeeded` на `/api/billing/webhook` → кошелёк +5 USD (ledger `cmuead3sh0007pbk2nin1pzlu`, +5 000 000 micros); повтор того же события — идемпотентен (ДА), конфликт логируется (ДА), UI кошелька показывает успех (ДА), отказ карты (`pm_card_chargeDeclined`, `pi_3UIsckEBncgzhYfv0X050wfV`) честный (ДА); RAW_PAN_USED=0, секреты не печатались. Подписка (Voice Pro tier) — НЕТ (см. следующий комментарий с причиной). Итог для WEB-679: путь «пополнение → кредиты» подтверждён на стенде; общий вердикт волны NO-GO только из-за подписки.
2026-09-23T16:10:18.655Z · coordinator
[23.09 16:10Z координатор] ## Причина отказа подписки в 4756 (координатор): `src/lib/stripe/pricing.ts:42-43` — `getCredential('NEXT_PUBLIC_STRIPE_PRICE_VOICE_PRO', 'price_voice_pro')`: второй аргумент `envValue` (задуман как инлайн `process.env.X` во время сборки) получает ЛИТЕРАЛ-заглушку, поэтому `getCredential` шаг 2 берёт заглушку и до `process.env` (шаг 3) не доходит; в собранном релизе стенда настоящего Price ID нет вовсе (grep по `.next/server` пуст), только `price_voice_pro` → Stripe «No such price», а webhook «Ignoring subscription update without mapped Stripe price» по той же причине. Значения в env стенда/боя верные — код их не читает. Фикс (4759): резолвить цены лениво в момент вызова через `process.env`/`getCredential` без литерала в `envValue`, единый резолвер для checkout и webhook-маппинга price→tier, тест «env задан → Checkout получает настоящий ID; не задан → честная ошибка конфигурации, не заглушка».
2026-09-23T16:25:09.446Z · coordinator
[23.09 16:25Z координатор] ## 4759 (M1, Luna) — GO: дефект подтверждён на base (бой): `pricing.ts` передавал заглушку в `getCredential`; теперь ленивый резолвер читает цены из runtime env (прямое `process.env.NEXT_PUBLIC_STRIPE_PRICE_*` + `getCredential(key)` без литерала), без значения — честная ошибка конфигурации `stripe_price_not_configured`; checkout и webhook-маппинг price→tier на одном резолвере; заглушек `price_voice_pro`/`price_team_voice` в `src` не осталось (0); тесты 5/5, область 88/88, typecheck 0. Патчи (2) наложены на A2, ветка `l115s-web679-price-resolver`. Войдёт в all6; e2e подписки повторить на all6 (4756-путь с `STRIPE_BETA_FREE_MODE=0` на стенде).
2026-09-23T16:42:57.582Z · coordinator
[23.09 16:42Z координатор] ## 4762 (M1, независимое ревью) — WEB-679 резолвер цен (4759): **NO-GO**, 2 блокера: (1) checkout и webhook отдают клиенту `missingKeys` с именами env `NEXT_PUBLIC_STRIPE_PRICE_*` (`create-checkout/route.ts:214-220`, `webhook/route.ts:756-762`) — типизированную ошибку логировать на сервере, клиенту — стабильный код; (2) настроенный, но неизвестный Stripe price ID логируется и ACK-ается 200 без dead-letter/алерта (`webhook/route.ts:661-668, 772-782`) — inbox помечает событие обработанным без сверки entitlement. Сам резолвер ленивый и безопасный для инлайна — это не оспорено. Репродукторы 0005/0006. → раунд 2 (4770).
2026-09-23T16:52:37.788Z · coordinator
[23.09 16:52Z координатор] ## Состав all6 (координатор): all5 (2dc0ee1d) + 4763 (WEB-684 realtime) — резолвер цен 4759 в all6 НЕ вошёл (конфликт с топапом в `webhook/__tests__/route.test.ts` при наложении на all5 + NO-GO ревью 4762); e2e подписки — на all7 после 4770.
2026-09-23T17:07:10.410Z · coordinator
[23.09 17:07Z координатор] ## 4770 (M1, Luna, раунд 2) — GO: имена env в ответах клиенту/Stripe = 0 (типизированная ошибка — в серверный лог, наружу стабильный код); неизвестный Stripe price → карантин в webhook-inbox (`unmapped_price`) + алерт, Stripe получает 500 (ограниченные повторы), путь переобработки `POST /api/admin/billing/webhook-inbox/:id/reprocess` → существующий retry-worker; репродукторы 4762 0005/0006 красные до / зелёные после; область 120/121 (1 pre-existing PostgreSQL skip); typecheck 0. Патчи 0003–0005 наложены на A2 `l115s-web679-price-resolver`. В all7 вместе с 4759; e2e подписки — после посадки all7 на стенд.
2026-09-23T17:31:37.204Z · coordinator
[23.09 17:31Z координатор] ## 4774 (M1, независимое ревью) — WEB-679 р2: **NO-GO** (без критичных): F3 глобальный 5-минутный cooldown алертов гасит повторные `unmapped_price` — при ретраях Stripe провал становится тихим. Положительное: уникальность/идемпотентность события, admin reprocess аутентифицирован и scoped, актуальное состояние подписки берётся из провайдера. → раунд 3 (4784): отдельный ключ алерта на `event.id`/price, счётчик повторов в карантинной записи, эскалация при ≥N.
2026-09-23T17:57:56.232Z · coordinator
[23.09 17:57Z координатор] ## 4784 (M1, Luna, раунд 3) — GO: ключ алерта `unmapped_price:<event.id|priceId>` (вне глобального cooldown), эскалация `unmapped_price_escalated` при ≥3 повторах или 24 ч без переобработки, ежедневная сводка карантина; репродуктор 4774 красный до / зелёный после; область 38/38; typecheck 0. Патчи 0006/0007 наложены на A2 `l115s-web679-price-resolver` (7 коммитов). Набор WEB-679 (резолвер + карантин + алерты) → в ревью (следующее ревью вместе с WEB-683 р6).
2026-09-23T18:17:48.281Z · coordinator
[23.09 18:17Z координатор] ## 4790 (M1, независимое ревью WEB-679 р3) — NO-GO (0 крит., 2 поведенческих): (1) эскалация «unmapped price» после 3 попыток в `billing/webhook/route.ts:806-813` идёт через `notifyErrorAlert` с cooldown по умолчанию 5 мин вместо заявленных 24 ч; (2) «дневная сводка раз в UTC-сутки» хранится в памяти процесса (`webhookInboxRetryWorker.ts:122,162-175`) — рестарт/две копии = дубли. Раунд 4 — следующая волна.
2026-09-23T18:51:35.489Z · coordinator
[23.09 18:51Z координатор] ## 4798 (M1, WEB-679 раунд 4) — GO: эскалация «unmapped price» после ≥3 попыток — cooldown 24 ч явно (общая константа с планировщиком); дневная сводка — долговечная отметка в БД `(kind, utcDay)` вставка-или-отказ, рестарт/две копии не дублируют (тест на PostgreSQL 17.8). Репродукторы 4790 2/2, 41/41, typecheck 0. Патчи 0008–0010 на A2 `l115s-web679-price-resolver` 29bb01aa9b (10). Дальше: ревью р4.
2026-09-23T19:27:30.104Z · coordinator
[23.09 19:27Z координатор] ## 4803 (M1, независимое ревью WEB-679 р4) — NO-GO, 1 крит.: (1) боевая схема миграций НЕ принимает статус `unmapped_price` — старый check `WebhookDedupe_status_check` падает с 23514 (на бою квитанция упала бы); (2) PG-proof копирует claim SQL и гоняет два вызова последовательно — гонка не доказана; (3) без `DATABASE_URL` proof зелёный при `tests 0`; (4) рядом с константой 24 ч остались литералы `24*60*60*1000`. Раунд 5 — следующая волна.
2026-09-23T20:03:31.966Z · coordinator
[23.09 20:03Z координатор] ## СОСТОЯНИЕ НА 23.09 20:2xZ (для нулевого агента)
- Что это: модели без строки в матрице цен молча тарифицировались; резолвер цен + карантин/эскалация «unmapped price» в вебхуке Stripe и повторной доставке.
- Где код: A2 ветка `l115s-web679-price-resolver` = 29bb01aa9b (10 коммитов; worktree `wt-l115s-web679`). Раунд 3 (7) — в all8; р4+ — в all9. (4759 не вошёл в all6 из-за конфликта с топапом — решён в all8 без потери тестов.)
- Раунды: 1–4 (4759/4770/4784/4798), ревью 4790/4803; 4803 (19:08Z) NO-GO 1 крит.: боевая схема миграций не принимает статус `unmapped_price` (check `WebhookDedupe_status_check`, 23514) — на бою упало бы; гонка двух копий не доказана; литералы 24 ч рядом с константой.
- Сейчас: раунд 5 — волна 4808 на M1 (`~/wt-l115s-f`), 19:30Z: миграция check-constraint + тест гонки боевой функцией.
2026-09-23T20:10:03.277Z · coordinator
[23.09 20:10Z координатор] ## 4808 (M1, WEB-679 раунд 5) — GO: миграция расширяет check `WebhookDedupe_status_check` на `unmapped_price` (тест на `migrate deploy`), гонка двух копий доказана боевой функцией (`Promise.all`, уникальный индекс → один успех), литералы 24 ч убраны (одна константа + grep-тест), без DATABASE_URL тест падает явно. Репродукторы 4803 зелёные, typecheck 0. Патчи 0011–0012 на A2 `l115s-web679-price-resolver` = 8645339092 (12). СОСТОЯНИЕ: раунд 5 принят → ревью 4812 (M1).
2026-09-23T20:41:36.246Z · coordinator
[23.09 20:41Z координатор] ## 4812 (M1, независимое ревью WEB-679 р5) — GO: миграция check-constraint, гонка двух копий боевой функцией, единая константа 24 ч, fail-fast без БД — подтверждены на PostgreSQL 17.8 (схема миграций, тесты исполнены). СОСТОЯНИЕ: код WEB-679 = A2 `l115s-web679-price-resolver` 8645339092 (12 коммитов) ПРИНЯТ → в all9 → посадка. Остаётся на тикете: Stripe e2e «до конца» на стенде all8 с `STRIPE_BETA_FREE_MODE=0` (после деплоя all8).
2026-09-23T21:41:04.033Z · coordinator
[23.09 21:41Z координатор] ## 4823/prebuild (сборка all8) — гейт периметра API: маршрут `src/app/api/admin/billing/webhook-inbox/[id]/reprocess/route.ts` (р3) без rate-limit и мониторинга → обёрнут `enforceRateLimit` (10/мин) + `runWithAppsideHttpTrace` на всех выходах; коммит на all8 и cherry-pick в ветку (`l115s-web679-price-resolver`). Правило: новый маршрут = обёртки rate-limit + trace, иначе `check:api-perimeter` красный.
2026-09-25T08:42:21.066Z · coordinator
[25.09 08:42Z координатор] # Ready board comment — WEB-679

2026-09-25 — POST-QA all9 `879094713e`: **CLOSE**.

Что сделано: test-mode путь Stripe теперь покрыт от Checkout mode=payment через signed `payment_intent.succeeded` до `creditFromPayment` и отображения wallet; providerRef/operationId делают повтор webhook идемпотентным. Subscription webhook выдаёт Voice Pro по runtime price mapping. Основные места: `src/app/api/billing/create-checkout/route.ts:44+`, `src/lib/billing/paymentIntentTopup.ts:79+`, `src/lib/billing/paymentCredit.ts:93-128`, `src/app/api/billing/webhook/route.ts:530,623+`.

Коммиты: `8b35e3404`, `9ba95e486`, `26f62f58f`, `d62ae81db`, `864533909`; тесты — checkout/webhook/wallet UI и `web679Round5Contracts`. Локально только включённый component target собрал 4/4 RC=0; остальные файлы отфильтрованы текущим `vitest.config.ts` (полный журнал: `/Users/poolpooly/waves/4982POSTQAALL9BILLINGPRODUCTTICKETS-evidence/tests.md`).

Траблшуты: прежний provider-webhook был stub/non-production; beta observer payment intent не должен быть вторым ingress; unmapped prices quarantine-ятся. Осталось координатору/владельцу на стенде: карта 4242, подписанный replay, SELECT баланса и screenshot; Live остаётся отдельным WEB-516 решением. Новый агент начинает с WEB-449 → этот файл → route/tests; бой, Live и секреты не трогать.


Проверка координатора (09:5xZ, all9 879094713e, M1): перезапуск тестов 4982 правильным раннером (node --import tsx --test, Node 22): тесты тикета на all9 зелёные (4982 гонял vitest, который их не собирает). CLOSE подтверждён.
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-679","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-09-25T08:42:40.900Z