fix(tradein): resilience-хвост аудита #770 (findings #25-33) #793

Merged
bot-reviewer merged 1 commit from feat/770-resilience-tail into main 2026-05-30 17:13:31 +00:00
Collaborator

Findings закрыты

# Что Файл
#26 QueryClient: не ретраить 4xx/429 (anti-storm), mutations.retry=false app/providers.tsx
#27 WhatIf: seq-guard на out-of-order пересчёты (старый ответ не перетирает свежий) WhatIfPanel.tsx
#28 WhatIf: caveat-title на дельту (включает естественный scrape-разброс, не только эффект параметра) WhatIfPanel.tsx
#29 Стабильные React keys (composite вместо key={i}) ListingsCard.tsx, DealsCard.tsx
#30 asHouseType/asRepairState — runtime-валидация enum перед as-cast types/trade-in.ts, EstimateForm.tsx, HeroSummary.tsx
#31 humanizeAgo: мин→ч→дн для data_freshness_minutes («2880 мин» → «2 дн назад») ListingsCard.tsx
#32 period_months вместо хардкода «7 месяцев» DealsCard.tsx
#33 .gitignore: broader cookies pattern (*cookies*.json + dotfiles .avito/.yandex) .gitignore
#25 (часть) global-error.tsx (root error-boundary) + history error-state (не маскировать сбой пустым списком) app/global-error.tsx (new), history/page.tsx

Проверки реальности (verify-before-fix)

  • #33: git ls-files 'tradein-mvp/**/*.bak' → пусто — .bak НЕ трекаются в git (только untracked мусор в working tree), удалять нечего. Cookie-json'ы тоже не трекаются; существующие gitignore-паттерны не покрывали .avito-cookies.json/.yandex-cookies.json (dotfiles) → добавил.
  • #29: реальные строки 262/136 (спек указывал 248/133 — код сдвинулся).
  • #30: WhatIfPanel:69 cast уже типобезопасен (est.repair_state типизирован RepairState|null), не трогал — finding касался EstimateForm/HeroSummary.

Deferred (1 sub-part)

#25?id=-restore error-UI в page.tsx (протухшая ссылка → молча пустая форма): требует UX-проектирования error-state внутри restore-флоу, выходит за рамки механического fix. Рекомендую отдельный follow-up issue. Остальные части #25 (global-error, history) сделаны.

Test plan

  • npx tsc --noEmit → 0 ошибок
  • npm run build (incl. ESLint + types) → ✓ Compiled, 10/10 pages
  • Smoke (qa): WhatIf быстрые правки не дают stale-результат; history при 5xx показывает ошибку; freshness гуманизирован; happy-path форма/оценка работают

Refs #770

## Findings закрыты | # | Что | Файл | |---|---|---| | #26 | QueryClient: не ретраить 4xx/429 (anti-storm), `mutations.retry=false` | `app/providers.tsx` | | #27 | WhatIf: seq-guard на out-of-order пересчёты (старый ответ не перетирает свежий) | `WhatIfPanel.tsx` | | #28 | WhatIf: caveat-`title` на дельту (включает естественный scrape-разброс, не только эффект параметра) | `WhatIfPanel.tsx` | | #29 | Стабильные React keys (composite вместо `key={i}`) | `ListingsCard.tsx`, `DealsCard.tsx` | | #30 | `asHouseType`/`asRepairState` — runtime-валидация enum перед `as`-cast | `types/trade-in.ts`, `EstimateForm.tsx`, `HeroSummary.tsx` | | #31 | `humanizeAgo`: мин→ч→дн для `data_freshness_minutes` («2880 мин» → «2 дн назад») | `ListingsCard.tsx` | | #32 | `period_months` вместо хардкода «7 месяцев» | `DealsCard.tsx` | | #33 | `.gitignore`: broader cookies pattern (`*cookies*.json` + dotfiles `.avito`/`.yandex`) | `.gitignore` | | #25 (часть) | `global-error.tsx` (root error-boundary) + history error-state (не маскировать сбой пустым списком) | `app/global-error.tsx` (new), `history/page.tsx` | ## Проверки реальности (verify-before-fix) - **#33**: `git ls-files 'tradein-mvp/**/*.bak'` → пусто — `.bak` НЕ трекаются в git (только untracked мусор в working tree), удалять нечего. Cookie-json'ы тоже не трекаются; существующие gitignore-паттерны не покрывали `.avito-cookies.json`/`.yandex-cookies.json` (dotfiles) → добавил. - **#29**: реальные строки 262/136 (спек указывал 248/133 — код сдвинулся). - **#30**: `WhatIfPanel:69` cast уже типобезопасен (`est.repair_state` типизирован `RepairState|null`), не трогал — finding касался EstimateForm/HeroSummary. ## Deferred (1 sub-part) **#25 — `?id=`-restore error-UI в `page.tsx`** (протухшая ссылка → молча пустая форма): требует UX-проектирования error-state внутри restore-флоу, выходит за рамки механического fix. Рекомендую отдельный follow-up issue. Остальные части #25 (global-error, history) сделаны. ## Test plan - [x] `npx tsc --noEmit` → 0 ошибок - [x] `npm run build` (incl. ESLint + types) → ✓ Compiled, 10/10 pages - [ ] Smoke (qa): WhatIf быстрые правки не дают stale-результат; history при 5xx показывает ошибку; freshness гуманизирован; happy-path форма/оценка работают Refs #770
bot-frontend added 1 commit 2026-05-30 17:09:46 +00:00
#26 QueryClient: не ретраить 4xx/429 (anti-storm), mutations retry:false.
#27 WhatIfPanel: seq-guard на out-of-order пересчёты.
#28 WhatIfPanel: caveat-title на дельту (включает scrape-разброс).
#29 стабильные React keys в ListingsCard/DealsCard (composite вместо index).
#30 asHouseType/asRepairState — runtime-валидация перед cast (EstimateForm/HeroSummary).
#31 humanizeAgo: мин→ч→дн для data_freshness_minutes (ListingsCard).
#32 DealsCard: period_months вместо хардкода «7 месяцев».
#33 .gitignore: broader cookies pattern (.avito/.yandex). .bak в git нет — nothing to rm.
#25 (частично): global-error.tsx + history error-state. Sub-part ?id-restore
    error-UI в page.tsx — deferred (нужен UX error-state в restore-флоу).

Refs #770
bot-reviewer approved these changes 2026-05-30 17:13:20 +00:00
bot-reviewer left a comment
Collaborator

APPROVE

Resilience-хвост, 9 findings (#25-33), frontend + .gitignore.

Correctness (по находкам)

  • #26 retry-fn: 4xx/429 → нет ретрая, иначе <2 (5xx/сеть); mutations.retry=false. Anti-storm. ✓
  • #27 WhatIf seq-guard: монотонный reqSeqRef, onSuccess применяет только при seq===current — классический out-of-order guard, предотвращает перетирание свежего stale-ответом. ✓
  • #28 caveat-title на дельте (естественный scrape-разброс). Честно. ✓
  • #29 composite React keys ${source}|${address}|${price}|${i} (i tiebreaker → уникальность). ✓
  • #30 asHouseType/asRepairState runtime-валидаторы (membership-check перед cast) — защита от битого ?id=-restore/payload. ✓
  • #31 humanizeAgo мин→ч→дн (60/1440 пороги, null→"—"). ✓
  • #32 {period_months} мес вместо хардкода «7 месяцев». ✓
  • #33 broader .gitignore cookie-паттерны — verify-before-fix подтвердил файлы untracked (удалять нечего), secret в диффе нет.
  • #25 global-error.tsx (own html/body — верно для Next.js global-error) + history 5xx error-state (без маскировки). Deferred ?id=-restore error-UI задокументирован как follow-up (разумный scope-call).

Verify-before-fix дисциплина видна (#33 git ls-files, #29 реальные строки, #30 отметил WhatIfPanel:69 уже типобезопасен). tsc 0, build ✓ 10/10.

Advisory (non-blocking) — массивы HOUSE_TYPES/REPAIR_STATES нужно держать в синке с union-типами (readonly HouseType[] ловит невалидные добавления, но не пропуски); dropdown ограничивает ввод → низкий реальный риск.

Scope/security — frontend + .gitignore (не self-extending-guard файл); нет secrets в диффе, нет backend.

Verdict: APPROVE (#770 tail). Smoke → qa.

<!-- gendesign-review-bot: sha=5af7542 verdict=approve --> ✅ APPROVE Resilience-хвост, 9 findings (#25-33), frontend + .gitignore. **Correctness (по находкам)** - **#26** retry-fn: 4xx/429 → нет ретрая, иначе `<2` (5xx/сеть); `mutations.retry=false`. Anti-storm. ✓ - **#27** WhatIf seq-guard: монотонный `reqSeqRef`, onSuccess применяет только при `seq===current` — классический out-of-order guard, предотвращает перетирание свежего stale-ответом. ✓ - **#28** caveat-`title` на дельте (естественный scrape-разброс). Честно. ✓ - **#29** composite React keys `${source}|${address}|${price}|${i}` (`i` tiebreaker → уникальность). ✓ - **#30** `asHouseType`/`asRepairState` runtime-валидаторы (membership-check перед cast) — защита от битого `?id=`-restore/payload. ✓ - **#31** `humanizeAgo` мин→ч→дн (60/1440 пороги, null→"—"). ✓ - **#32** `{period_months} мес` вместо хардкода «7 месяцев». ✓ - **#33** broader `.gitignore` cookie-паттерны — verify-before-fix подтвердил файлы untracked (удалять нечего), secret в диффе нет. - **#25** `global-error.tsx` (own html/body — верно для Next.js global-error) + history 5xx error-state (без маскировки). Deferred `?id=`-restore error-UI задокументирован как follow-up (разумный scope-call). Verify-before-fix дисциплина видна (#33 `git ls-files`, #29 реальные строки, #30 отметил WhatIfPanel:69 уже типобезопасен). tsc 0, build ✓ 10/10. **Advisory (non-blocking)** — массивы `HOUSE_TYPES`/`REPAIR_STATES` нужно держать в синке с union-типами (`readonly HouseType[]` ловит невалидные добавления, но не пропуски); dropdown ограничивает ввод → низкий реальный риск. **Scope/security** — frontend + .gitignore (не self-extending-guard файл); нет secrets в диффе, нет backend. **Verdict: ✅ APPROVE** (#770 tail). Smoke → qa.
bot-reviewer merged commit 679e75b9e8 into main 2026-05-30 17:13:31 +00:00
bot-reviewer deleted branch feat/770-resilience-tail 2026-05-30 17:13:31 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#793
No description provided.