feat(task): автозакрытие задачи при выполнении всех помидоров - #21
Merged
Conversation
added 5 commits
August 13, 2026 12:08
PATCH /tasks/:id/pomodoro now decides completion on the backend: it snapshots
the counter, applies the atomic increment, re-reads the authoritative value and
routes through the existing update({ completed: true }) when the task crossed
its pomodoroCount. Recurring tasks therefore behave exactly like a manual
checkbox — no second completion path.
The trigger is a transition (before < target <= after), not "counter is above
target", so unchecking an auto-completed task lets the user keep working on it
without it re-closing.
The endpoint now returns UpdateTaskResponse, the same shape as PATCH /tasks/:id.
Also fixes the e2e harness: AppModule resolves isTelegramEnabled() at import
time, so createTestApp() set ENABLE_TELEGRAM too late and telegraf launched for
real, failing every suite with "401: Bot Token is required" (4/31 passing).
Moved the env setup into jest setupFiles and corrected three stale assertions
that read PATCH /tasks/:id as a flat task. Now 31/31.The timer used to PATCH /tasks/:id/pomodoro itself and drop the response, so
the X/Y badge never moved until a refetch and taskStore.incrementPomodoro was
dead code. It now delegates to the store, which applies the backend response —
including the auto-completion and any recurring nextInstance the backend
produced when the pomodoro target was reached.
Extracts applyUpdateResponse from updateTask so both callers share one parser
for { task, nextInstance?, deletedInstanceId? } instead of duplicating it.
Drops the `!task || !task.isPomodoroTask` guard: the timer can restore its
session before fetchTasks resolves, and swallowing the increment because the
task is not in the store yet would lose it. Only the optimistic bump is now
conditional, and the rollback restores the snapshot instead of subtracting.Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Когда накопленное число помидоров достигает
pomodoroCount, задача автоматически становится выполненной — и в календаре сразу отображается как выполненная.Как устроено
Правило живёт на бэкенде: фронтовый
timer-storeне знает накопленногоpomodoroCompleted, у него только настройки сессии.TaskService.incrementPomodoroснимает счётчик до инкремента, применяет атомарный инкремент, перечитывает авторитетное значение и при пересечении порога вызывает существующийthis.update(..., { completed: true }). За счёт этого повторяющиеся задачи закрываются ровно как от ручной галочки — второй ветки логики завершения не появилось.Триггер — переход через порог (
before < target <= after), а не факт «счётчик выше цели». Это даёт бесплатный escape-hatch: снял галочку вручную → продолжай работать, задача сама не перезакроется.PATCH /tasks/:id/pomodoroтеперь возвращаетUpdateTaskResponse— ту же форму, что иPATCH /tasks/:id.Побочно починилось
X/Yстал живым. Раньшеtimer-storeдёргал API сам и выбрасывал ответ, аtaskStore.incrementPomodoroбыл мёртвым кодом — счётчик не двигался до перезагрузки списка.AppModuleвычисляетisTelegramEnabled()на этапе импорта модуля, аtest/helpers/test-app.tsвыставлялENABLE_TELEGRAM=falseуже внутриcreateTestApp()— то есть после. Telegraf стартовал и валил каждый suite с401: Bot Token is required. Перенёс переменные вsetupFiles. Стало 31 из 31.tasks.e2e-spec.tsчиталиbody.titleвместоbody.task.title— эндпоинт отдаёт обёртку ещё с задачи про recurring, просто тесты никогда не доходили до ассертов.Проверено
vue-tscчисто0.5 + 0.5при цели 1 закрыло; recurring дала инстанс на следующий день со сброшенным счётчиком; задача без конфига не закрыласьОсознанно не сделано
4/4и незакрытые, ретроспективно не закрываются.alfy-mcp/src/tools/tasks.ts:128,142отдают клиенту обёртку{task:{...}}вместо задачи, и уPATCH /tasks/:id/pomodoroнет DTO (incrementне валидируется). Обе находки пре-существуют и лежат в Future work — инвариант задачи запрещал трогатьalfy-mcp.Дизайн, план и разбор — в
docs/tasks/auto-complete-task-on-pomodoros-done.md.