Перейти к содержимому

Medusa: кнопка «убрать скидку» выдавала максимальную, потому что ноль в JavaScript ложный

v2 0 звёзд 0 форков 0 наблюдателей 1 ветка 0 прогонов Публичный
patched via APIv2

Medusa, плагин лояльности, август 2026.

В магазине есть накопленные баллы — «складской кредит». Их можно частично потратить на заказ. Шаг, который этим занимается, документирован просто: передайте сумму — он уберёт прежнее списание с корзины и создаст новое на эту сумму.

А чтобы вообще убрать списание, передайте ноль.

Одна строка
ts
1input.amount ? input.amount : storeCreditAccount.balance

Читается как «если сумму передали — берём её, иначе весь остаток». И это верно ровно до того дня, когда кто-то передаст ноль.

Ноль в JavaScript ложный. Проверка решает, что суммы не было, и подставляет balanceвесь остаток покупателя. Из описания исправления #16378:

Clearing the store credit from a cart left it with a credit line for everything the customer had.

Очистка списания с корзины оставляла её со списанием на всё, что у покупателя было.

Кнопка «убрать скидку» выдавала максимальную скидку. Механика, за которую в другом месте платят маркетологам.

Задача, в которой уже лежал ответ

9 августа 2026, 14:24. giladv заводит #16377 — и не просто жалуется, а приносит причину и правку в самой задаче:

Because 0 is falsy, it is treated as an omitted amount and the workflow falls back to storeCreditAccount.balance. … Use an undefined check rather than truthiness, so 0 remains a valid specified amount.

Через три минуты отвечает medusa-os-bot — это Claude, запущенный из GitHub Actions (см. карточку «Medusa: повадки проекта»), — и подтверждает: файл, строка 166, «Fix is straightforward and suitable for a community contribution». Ставит type: bug и good first issue.

Ещё через тринадцать минут fzlzjerry пишет «I would like to work on this» и расписывает план. PR от него так и не пришёл — план исполнили другие.

Починка была уже написана — тремя строками ниже

Самое поучительное здесь не сама ошибка, а то, где нашлось лекарство. Первая правка — #16378, lazerg, 14:58, через 34 минуты после задачи:

Changed the guard to isDefined(input.amount), which is the same check the balance validation a few lines below already uses.

…ровно та проверка, которую проверка остатка несколькими строками ниже уже использует.

И правда: на develop сразу под строкой 166 стоит if (isDefined(input.amount) && …). Верный образец лежал в том же файле, в пределах экрана. Просто в одном месте написали ?, а в другом — isDefined.

Вторая правка — #16379, 15:26, — говорит то же другими словами:

It updates the fallback condition in computeCreditLineActionsStep to strictly check if the value is defined, rather than relying on a truthiness check.

Здесь есть деталь про стиль. Правила проекта для бота начинаются с «No semicolons», а в этом файле на develop тридцать три строки кончаются точкой с запятой. Автор #16379 заодно убрал их все — диф вышел +35/−38 при исправлении в одно слово; бот отметил: «correct per Medusa convention but unrelated to the bug fix», и попросил только changeset.

Автор #16379 закрыл его сам через 51 минуту. Сегодня GitHub показывает вместо его имени ghost — аккаунт удалён.

Три человека, три дня — и ни одного вливания

Ошибку чинили трижды за три дня: #16378 и #16379 — 9 августа, #16401 (ekley) — 11-го. Разные авторы, одна строка.

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

Что стало с очередью — на 02.09.2026:

  • #16378 — бот одобрил через четыре минуты (initial-approval). Дальше тишина до 31 августа, когда бот пометил PR Stale с текстом «open 60 days with no activity» (порог в конфиге — 15 дней). Через полчаса lazerg: «This has been sitting since Aug 9 with a green fix, a test, and no maintainer review yet… Could someone take a look? cc @NicolasGorga».
  • #16379 — закрыт автором в тот же день.
  • #16401 — единственный из трёх, куда пришёл человек. Nicolas Gorga (NicolasGorga), 26 августа: «The update is correct, though would like to understand first what real use case does calling this workflow with amount = 0 cover». Вопрос без ответа.

Строка 166 на develop та же, что 9 августа. #16377 пометили Stale 31 августа вместе с PR.

Рядом в задаче — спор, который стоит прочитать целиком. 10 августа msaikens: «Using Codex to resolve issues is a great way to be more efficient but I strongly dislike AI as a "source of truth"». giladv в ответ: «the ease of AI driven tickets empowers us to document issues that might otherwise be slipping between the cracks. I think that's a fair tradeoff». Задача при этом — с точной причиной и верной правкой, и бот, который её подтвердил, — тоже ИИ.

Продолжение: то же место, другая беда

В тот же день, 9 августа в 15:39, giladv дописывает в #16377 второй симптом: повторный вызов с большей суммой не срабатывает, корзина держит старую. Обходной путь — сначала вызвать с «маленькой истинной суммой, например 0.001», потом с нужной. Обход ошибки через ту же самую ошибку.

11 августа приходит #16405 (actualmayank). Удаление старой строки списания и создание новой были обёрнуты в parallelize(...) — выполнялись одновременно. Хотя документация того же шага говорит: «removes any existing store-credit lines and creates a new credit line» — то есть сначала одно, потом другое.

That mismatch is consistent with a cart intermittently keeping a stale credit amount when the workflow is called again with a new amount.

Это расхождение объясняет, почему корзина иногда сохраняла прежнюю сумму списания.

Расхождение между документацией шага и его реализацией прожило до первого плавающего бага. Плавающие баги на то и плавающие, что живут долго.

#16405 прошёл три круга бота за двадцать минут — changeset, формат changeset, отступы — и остался с requires-more из-за отступов: «Running Prettier on the file will correct this». parallelize( на develop на месте, строка 274.

Кто в кадре
  • giladv, без имени в профиле. Завёл #16377 с причиной и правкой внутри, дописал второй симптом, отстоял задачи, написанные с ИИ.
  • lazerg — Lazizbek Ergashev. #16378 через 34 минуты после задачи, с тестом; 31 августа напомнил о себе.
  • Автор #16379 — аккаунт удалён, GitHub показывает ghost. Заодно убрал точки с запятой; закрыл PR сам через 51 минуту.
  • ekley — Hossein Ekramian. #16401 — тот, что получил единственное ревью человека.
  • actualmayank — mayank k. #16405: parallelize там, где документация обещала «сначала удалить, потом создать».
  • fzlzjerry — Morax. «I would like to work on this» — с планом, без PR.
  • msaikens — Mitchell Aikens. «I strongly dislike AI as a "source of truth"».
  • NicolasGorga — Nicolas Gorga, MedusaJS. Единственное ревью человека на три PR: «what real use case…».

Подробнее о проекте и людях — в карточке «Medusa: повадки проекта».

Что с этим делать у себя

1
Найти числовые поля в проверках на истинность

Ищите amount ?, price &&, count ? … : — всякое место, где число проверяется на истинность, а не на существование.

Зачем: Ноль — настоящее значение и обычно означает «не надо». Проверка на истинность читает его как «не передали» — и подставляет запасной вариант, который почти всегда максимальный.
Проверьте
  • Каждое найденное место либо исправлено, либо помечено как намеренное — с объяснением
  • Проверены не только суммы: цена 0 (акция), количество 0, порог 0
2
Посмотреть, нет ли верной проверки тут же, рядом

Прежде чем придумывать свою — прочитайте соседние двадцать строк. В Medusa образец лежал в том же файле, в пределах экрана.

Зачем: Две разные проверки одного и того же в одном файле — это не стиль, а будущая ошибка: одна из них неверна, и непонятно какая.
Проверьте
  • В правленном файле осталась ОДНА форма проверки на существование
3
Сверить документацию шага с тем, что он делаетРекомендуется

Если написано «сначала убрать, потом создать», а в коде оба действия идут параллельно — неправ код.

Зачем: Расхождение между описанием и реализацией не болит до первого плавающего бага, а плавающие баги живут долго и стоят дорого.
Проверьте
  • Либо код приведён к описанию, либо описание исправлено — и сказано, кто из них врал

Medusa: защита от повтора отказывает там, где можно было ответить, и пропускает там, где повтор стоит денег

обновлён 2 сент. 2026 г.

Найти в открытом проекте настоящую историю поломки, восстановить ход мысли по написанному и закончить проверкой своего кода

обновлён 1 сент. 2026 г.

k8sgpt отправляет поломки кластера языковой модели и обещает анонимизацию. Через три месяца пользователь спросил, где её границы, — и получил честный ответ, который держится третий год

обновлён 3 сент. 2026 г.

Как проект живёт и принимает чужие правки, и кто в нём встречался в разборах — по поступкам, датам и ссылкам

обновлён 2 сент. 2026 г.

Better Auth: ограничение частоты считалось от последнего запроса, включая отклонённые, — и разблокировка не наступала никогда

обновлён 2 сент. 2026 г.

Saleor: товар сняли с продажи — и строку из корзины покупателя стирали в фоне. Три с половиной года до теста, поменявшего знак

обновлён 2 сент. 2026 г.