[Fable 5][P2] RecommendBucket молча отбрасывает per-bucket elasticity/velocity_source — live what-if на фронте считает по неверной эластичности #1204

Closed
opened 2026-06-13 04:49:42 +00:00 by bot-backend · 0 comments
Collaborator

Из армейского ревью Claude Fable 5 (2026-06-11)

Confirmed by 2/2 адверсариальных верификатора, severity P2 (api-contract), confidence 0.85.

Файл

backend/app/schemas/recommend.py:42

Evidence (цитата кода)

    # Velocity baseline (units/month for THIS project allocated to this bucket
    # at price_factor=1.0). Frontend scales by price_factor^elasticity for live
    # what-if recompute.
    velocity_per_month: float | None = None
    months_to_sellout: float | None = None
    # Success-driven mix flag (issue #25): bucket has top success_score in district
    is_top_success: bool = False

Why bug

recommend_mix кладёт в bucket ключи elasticity/elasticity_r2/elasticity_n/elasticity_source/velocity_source (analytics_queries.py:2592-2606), но в RecommendBucket их нет и нет extra='allow' — response_model их молча выкидывает. Фронт (RecommendVelocityPanel.tsx:68 r.elasticity ?? elasticity) при движении price-слайдера всегда падает на глобальную эластичность: расчёт темпа/срока расходится и с UI-таблицей «Эластичность по сегментам», и с серверным months_to_sellout (тот считается per-bucket).

Suggested fix

Добавить в RecommendBucket поля elasticity: float | None, elasticity_r2: float | None, elasticity_n: int | None, elasticity_source: str | None, velocity_source: str | None (или model_config = ConfigDict(extra='allow') как backstop в AnalyzeResponse).

Verify notes

  • [real/P2] recommend_mix кладёт в каждый bucket elasticity/r2/n/source и velocity_source (analytics_queries.py:2592–2606), endpoint analytics.py:238 отдаёт dict через response_model=RecommendMixOutput; RecommendBucket полей не объявляет, extra='allow' нет → Pydantic v2 молча отбрасывает. Фронт ждёт их (analytics.ts:333), RecommendVelocityPanel.tsx:68 всегда падает на глобальную эластичность: при pf≠1 live-KPI расходятся с per-bucket таблицей (scope) и серверным months_to_sellout (2618–2620).
  • [real/P2] Цепочка подтверждена: producer кладёт b["elasticity"]/velocity_source и считает months_to_sellout по per-bucket pf^e; endpoint с response_model=RecommendMixOutput, в RecommendBucket полей нет, model_config нет — Pydantic v2 (extra=ignore) молча режет. Фронт ожидает их (elasticity?: в типе), derivedRows не обогащает из scope.elasticity_per_bucket → r.elasticity??elasticity всегда глобальная, расходится с таблицей сегментов и сервером. При pf=1 расхождения нет → P2.

Provenance

  • Finders: schemas-models
  • Confidence: 0.85
  • Originally batched at: main@d796027
## Из армейского ревью Claude Fable 5 (2026-06-11) Confirmed by 2/2 адверсариальных верификатора, severity **P2** (api-contract), confidence 0.85. ## Файл [`backend/app/schemas/recommend.py:42`](backend/app/schemas/recommend.py:42) ## Evidence (цитата кода) ``` # Velocity baseline (units/month for THIS project allocated to this bucket # at price_factor=1.0). Frontend scales by price_factor^elasticity for live # what-if recompute. velocity_per_month: float | None = None months_to_sellout: float | None = None # Success-driven mix flag (issue #25): bucket has top success_score in district is_top_success: bool = False ``` ## Why bug recommend_mix кладёт в bucket ключи elasticity/elasticity_r2/elasticity_n/elasticity_source/velocity_source (analytics_queries.py:2592-2606), но в RecommendBucket их нет и нет extra='allow' — response_model их молча выкидывает. Фронт (RecommendVelocityPanel.tsx:68 `r.elasticity ?? elasticity`) при движении price-слайдера всегда падает на глобальную эластичность: расчёт темпа/срока расходится и с UI-таблицей «Эластичность по сегментам», и с серверным months_to_sellout (тот считается per-bucket). ## Suggested fix Добавить в RecommendBucket поля elasticity: float | None, elasticity_r2: float | None, elasticity_n: int | None, elasticity_source: str | None, velocity_source: str | None (или model_config = ConfigDict(extra='allow') как backstop в AnalyzeResponse). ## Verify notes - [real/P2] recommend_mix кладёт в каждый bucket elasticity/r2/n/source и velocity_source (analytics_queries.py:2592–2606), endpoint analytics.py:238 отдаёт dict через response_model=RecommendMixOutput; RecommendBucket полей не объявляет, extra='allow' нет → Pydantic v2 молча отбрасывает. Фронт ждёт их (analytics.ts:333), RecommendVelocityPanel.tsx:68 всегда падает на глобальную эластичность: при pf≠1 live-KPI расходятся с per-bucket таблицей (scope) и серверным months_to_sellout (2618–2620). - [real/P2] Цепочка подтверждена: producer кладёт b["elasticity"]/velocity_source и считает months_to_sellout по per-bucket pf^e; endpoint с response_model=RecommendMixOutput, в RecommendBucket полей нет, model_config нет — Pydantic v2 (extra=ignore) молча режет. Фронт ожидает их (elasticity?: в типе), derivedRows не обогащает из scope.elasticity_per_bucket → r.elasticity??elasticity всегда глобальная, расходится с таблицей сегментов и сервером. При pf=1 расхождения нет → P2. ## Provenance - Finders: `schemas-models` - Confidence: 0.85 - Originally batched at: main@d796027
bot-backend added the
Fable 5 ревью
label 2026-06-13 04:49:42 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#1204
No description provided.