[Fable 5][P3] useSiteAnalysis: общий cancelledRef между конкурентными мутациями — новый analyze воскрешает отменённый poll-цикл #1242

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

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

Confirmed by 2/2 адверсариальных верификатора, severity P3 (concurrency), confidence 0.6.

Файл

frontend/src/hooks/useSiteAnalysis.ts:81

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

    }): Promise<ParcelAnalysis> => {
      cancelledRef.current = false;
      setFetchingState(null);

Why bug

cancel() ставит cancelledRef=true и через mutation.reset() разблокирует CadInput. Если после отмены участка A пользователь тут же запускает участок B, mutationFn B сбрасывает общий ref в false — спящий цикл A на следующем тике видит false и продолжает: поллит fetch-status старого cad, при готовности шлёт лишний POST /analyze для A и вызывает setFetchingState(null), стирая FetchingState-баннер (ETA + кнопка отмены) активного запроса B. Отмена фактически действует только пока не стартовала следующая мутация.

Suggested fix

Сделать токен отмены пер-вызов (локальная переменная/AbortController в замыкании mutationFn; cancel держит ссылку на текущий) и обновлять fetchingState только из актуального цикла.

Verify notes

  • [real/P3] TanStack v5 не абортит mutationFn: после cancel() цикл A жив, спит ≤2с; reset() сразу разблокирует CadInput (значение поля сохранено — ресабмит в один клик). mutationFn B (стр.81) сбрасывает общий cancelledRef=false → проверка на стр.133 у A проходит, A поллит дальше, на terminal шлёт лишний POST /analyze и setFetchingState(null), стирая баннер с кнопкой отмены активного B (fallback page.tsx:395 без cancel). data не портится (observer отвязан), консьюмер — только legacy-роут → P3.
  • [real/P3] Все защиты опровергнуты: CadInput disabled при isPending, но cancel() делает mutation.reset() → isPending=false мгновенно, а mutationFn-промис A не абортируется (TanStack v5). Новый mutate в окне ≤2s (до следующей проверки после sleep) ставит общий cancelledRef=false → цикл A воскресает: лишние поллы fetch-status, лишний POST /analyze (стр.145) и setFetchingState(null) (стр.154) стирает баннер ETA/cancel активного B. data/error защищены TanStack → P3.

Provenance

  • Finders: fe-app-lib
  • Confidence: 0.6
  • Originally batched at: main@d796027
## Из армейского ревью Claude Fable 5 (2026-06-11) Confirmed by 2/2 адверсариальных верификатора, severity **P3** (concurrency), confidence 0.6. ## Файл [`frontend/src/hooks/useSiteAnalysis.ts:81`](frontend/src/hooks/useSiteAnalysis.ts:81) ## Evidence (цитата кода) ``` }): Promise<ParcelAnalysis> => { cancelledRef.current = false; setFetchingState(null); ``` ## Why bug cancel() ставит cancelledRef=true и через mutation.reset() разблокирует CadInput. Если после отмены участка A пользователь тут же запускает участок B, mutationFn B сбрасывает общий ref в false — спящий цикл A на следующем тике видит false и продолжает: поллит fetch-status старого cad, при готовности шлёт лишний POST /analyze для A и вызывает setFetchingState(null), стирая FetchingState-баннер (ETA + кнопка отмены) активного запроса B. Отмена фактически действует только пока не стартовала следующая мутация. ## Suggested fix Сделать токен отмены пер-вызов (локальная переменная/AbortController в замыкании mutationFn; cancel держит ссылку на текущий) и обновлять fetchingState только из актуального цикла. ## Verify notes - [real/P3] TanStack v5 не абортит mutationFn: после cancel() цикл A жив, спит ≤2с; reset() сразу разблокирует CadInput (значение поля сохранено — ресабмит в один клик). mutationFn B (стр.81) сбрасывает общий cancelledRef=false → проверка на стр.133 у A проходит, A поллит дальше, на terminal шлёт лишний POST /analyze и setFetchingState(null), стирая баннер с кнопкой отмены активного B (fallback page.tsx:395 без cancel). data не портится (observer отвязан), консьюмер — только legacy-роут → P3. - [real/P3] Все защиты опровергнуты: CadInput disabled при isPending, но cancel() делает mutation.reset() → isPending=false мгновенно, а mutationFn-промис A не абортируется (TanStack v5). Новый mutate в окне ≤2s (до следующей проверки после sleep) ставит общий cancelledRef=false → цикл A воскресает: лишние поллы fetch-status, лишний POST /analyze (стр.145) и setFetchingState(null) (стр.154) стирает баннер ETA/cancel активного B. data/error защищены TanStack → P3. ## Provenance - Finders: `fe-app-lib` - Confidence: 0.6 - Originally batched at: main@d796027
bot-backend added the
Fable 5 ревью
label 2026-06-13 04:49:49 +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#1242
No description provided.