| 2026-10-10 |

Стек входящих: только задачи без проекта, карточка как в списке, выдача «3 в ряд» (0.95)
...
Набор правок из разбора стека входящих.
Стек — только «сырые» задачи без проекта. Задача, созданная сразу в проекте
(страница проекта, подзадача, агент через MCP), уже получила контекст от владельца,
и разбирать её не за чем: стек — стопка для того, что захвачено на бегу и ещё ни к
чему не отнесено. Такие задачи никуда не пропадают: они видны на странице своего
проекта, автодетализация для них идёт как обычно, а предложение ИИ ждёт на странице
самой задачи. Туда же уходит задача, перенесённая из стека пунктом «В другой
проект»: этот пункт карточка задачи даёт сама, и это ровно тот жест «это к проекту X».
Карточка стека — та же карточка задачи, что в списке и в выдаче (TaskCard), а
своё у неё в новом слоте #extra внизу: предложение автодетализации бейджами и
кнопки «Да, всё верно» и «Детализировать». Прежняя карточка стека была отдельным
набором разметки и разошлась с карточкой задачи: ссылка (классом card-link внутри
китовой шапки card-title) наследовала аперкейс шапки — заголовок кричал; «⋯»-меню
было своё, из двух пунктов, поэтому смена статуса и перенос в проект из стека были
недоступны. Теперь карточка даёт всё то же, что в списке, а стек добавляет к «⋯»
один пункт — «Переспросить ИИ»: он уместен только у сырой задачи, поэтому пункт
передаётся карточке снаружи, через menuExtra. Бейдж «в стеке» и абсолютная дата
создания из карточки ушли — дублировали место карточки и её подвал.
Карточки ряда — одной высоты, а блок предложения с кнопками прижат к низу
содержимого. В 0.59 они выравнивались по верху, и «Да, всё верно» у задач с разным
числом бейджей стояла на разной высоте. Скелетон загрузки повторяет ту же сетку.
Выдача /options — три карточки в ряд (больше и не бывает: pick_options отдаёт
максимум три). Сетка и ломающиеся точки (1080/720px) те же, что у стека: карточка
там та же, и ряд читается одинаково на обеих страницах.
Локали: ушли stack.inStack (за бейджем) и stack.deleteTask (за «Удалить» из общей
карточки).
Проверено живым прогоном в браузере (временный пользователь, данные засеяны SQL,
XP не начислялся): в стеке три карточки из четырёх сырых (четвёртая — с проектом, и
она видна на странице проекта), счётчик шапки совпадает, китовой шапки .card-title
в карточке нет, заголовок без аперкейса и ведёт на задачу, сниппет, бейджи, подвал и
«⋯» на месте, «Переспросить ИИ» в меню рядом со штатными пунктами, три карточки
одной высоты (293px) с кнопками на одной линии, в выдаче на 1440px три карточки в
ряд (461px), на 1000px две, на 700px одна, на 390px горизонтальной прокрутки нет ни
на главной, ни в выдаче. Стенд вычищен.
ТЗ 0.95: §3.1, §3.2, §3.7, §3.9, §3.19, §7.
root
committed
5 hours ago
|

Автодетализация: теги, приоритет и оценка времени (0.93)
...
ИИ переписывал заголовок и описание — и портил их: контекста задачи у модели нет,
поэтому сжатый текст терял важные детали или менял суть. А карточка предложения
принимает всё или ничего, так что верно угаданные теги приходилось отклонять
из-за кривого описания. Теперь ИИ предлагает только то, где он полезен: теги,
градацию приоритета и оценку длительности.
- теги ДОПОЛНЯЮТ назначенные, а не заменяют их — потому существующие теги задачи
идут в контекст промпта отдельной строкой с правилом «не повторяй» (раньше теги
предлагались, только когда у задачи нет ни одного);
- приоритет просим градацией интерфейса (very_low…urgent), число шкалы 0-10
считаем сами: маленькой модели выбрать слово проще, чем откалибровать число.
Выдуманные ею градации («Very High», «critical», «normal») притягиваются к
ближайшей из пяти, иначе ответ пропадал бы целиком;
- оценка в минутах, границы 5…1440 (как ge=1, le=24*60 в схеме), округление до
5 минут. Она же включает задачу в режим «3 варианта», куда задачи без оценки
попадали лишь фолбэком;
- применение безопасно по определению: теги сливаются, приоритет и оценка пишутся
только в пустое поле. Проверка «пусто» стоит в apply_proposal, а не только в
промпте: владелец мог проставить приоритет, пока модель думала, — перетирать его
нельзя. Заголовок и описание не трогаются вовсе;
- флаг force у detail_task убран: переспрос всё равно ничего не перетирает,
«перепредложить всё» потеряло смысл. «Переспросить ИИ» теперь сбрасывает
предложение и анализирует текущий текст задачи;
- пустой ответ модели («добавить нечего») карточку не показывает — молчим, как и
просил владелец. Сырую задачу в этом случае утверждают из меню шапки (и в стеке
кнопка утверждения остаётся); «Детализация подбирается…» в стеке показывается
только пока ai_proposal === null, то есть пока ИИ думает;
- в интерфейсе из предложения ушли строки заголовка, описания и проекта; сниппет
карточки в списке сводится к тегам («Средний · ≈ 30 мин» текстом задачи не
читается), из промпта внешнего агента описание тоже уходит.
Живой прогон на qwen3.5:2b-q4_K_M (временный пользователь): оценки осмысленны, но
шумны (одна задача давала 15/25/30/45/60 мин от прогона к прогону), приоритет в
основном попадает, но бывает завышен (смазка петель двери → urgent), теги — слабое
место: модель редко переиспользует русский каталог и придумывает английские.
Понижение температуры до 0.1 даёт стабильность, но выравнивает всё (приоритет
почти всегда low, оценка всегда 15), поэтому оставлено как есть.
ТЗ 0.93: §3.2, §3.4, §3.11, §3.22, §5, §7, §8 п.2 (решение 2026-09-22 отменено,
решение 2026-09-19 подтверждено).
root
committed
6 hours ago
|
Импорт задачи из ссылки на багрепорт BugTrail (0.92)
...
Ссылка на отчёт, вставленная в пустой заголовок формы задачи, разворачивается
в заголовок и описание: текст отчёта, записанные шаги со скриншотами инлайном,
страница и ссылка-источник строкой в описании. То же — в попапе расширения.
Бэкенд: services/bugtrail.py достаёт share-токен из ссылки (хост — только из
конфига, поэтому подсунуть чужой адрес нечем) и собирает markdown; GET
/api/bugtrail/draft отдаёт {title, description}, 400 — не наша ссылка, 503 —
импорт выключен, 404/502 — отчёт не отдался. В БД ничего не пишется.
Фронт: @paste на поле заголовка (кит раскрывает attrs на нативный input),
сниппет карточки перестал показывать «!скриншот». Адрес трекера — дефолт в
конфиге, так что на проде фича работает без правки .env.
ТЗ: 3.22, правки в 3.18, 5, 7, 8; версия 0.92.
root
committed
7 hours ago
|

Регулярные задачи: дата следующего раза, метка в списке, фильтр (0.91)
...
Дата следующего экземпляра считалась и выбрасывалась: закрыв ежедневную
задачу, владелец видел её же снова «к выполнению», без даты и неотличимо от
задачи на сегодня. Теперь дата ложится в существующее `deadline_date` (новых
полей и миграций нет) — при нестрогом периоде не ставится, ТЗ 3.4 запрещает
оба поля разом.
- recurrence: `_copy` получает посчитанную дату, `spawn_next` её не теряет;
- reminders: у регулярной повод один — ритм, дедлайновые правила её не
касаются (иначе в день срока два уведомления об одном деле), сводка тоже;
- reminders: ключ дедупа ритма включает id задачи — без него уникальность
push_deliveries съедала второе «Пора», и за день приходило одно
напоминание на пользователя, сколько бы регулярных ни совпало;
- API: параметр `task_type` у GET /api/tasks;
- список: переключатель «Регулярные» рядом с «Приоритетными»;
- карточка: акцентная полоса по левой кромке и кружок с иконкой повтора
перед названием, подсказка словами о правиле; дата читается как
«след. 12.10.2026»;
- ачивка «Дедлайн-босс» регулярные не считает.
ТЗ 0.91: §3.5, §3.6, §3.21, §5, §7.
root
committed
8 hours ago
|

Напоминания: сроки, ритм, сводка, тихие часы (0.89)
...
Push из 0.88 доставлял события агента, но о сроке и о регулярном деле по-прежнему
напоминала только открытая вкладка. Теперь о них напоминает планировщик: строгий
дедлайн за день, в день и один раз о просрочке; «пора» регулярной задачи; утренняя
сводка. Нестрогий срок («в течение недели/месяца/года») не напоминает вовсе — в
модели у него нет дня отсчёта, а выдумывать якорь не стали. Задачи в done,
cancelled и deferred молчат: «сейчас не в приоритете» — решение владельца.
Правила — чистые функции (app/services/reminders.py): «сегодня» приходит
аргументом, поэтому календарные границы проверяются без часов и без сети, а
планировщик (app/services/scheduler.py) остаётся тонким. День ритма считается тем
же recurrence.next_date, что создаёт следующий экземпляр, — отдельного календаря
напоминаний нет, иначе правила разъехались бы.
Дедуп — журналом push_deliveries (unique(user_id, kind, ref_key)), и запись идёт
до отправки: тик 15-минутный, а поводы дневные, поэтому без журнала одна и та же
просрочка приходила бы сотню раз, а перезапуск контейнера рассылал бы дубли.
Обратный размен (падение между вставкой и отправкой) даёт пропуск, а не дубль —
для напоминания это правильная сторона ошибки.
Тихие часы — настройка quiet_hours («HH:MM-HH:MM», окно 22:00-09:00 в интерфейсе,
пересекает полночь). Попавшее в окно напоминание не отправляется и не
записывается: условие дневное, и следующий тик после окна отправит его сам. Работа
агента тихими часами не глушится — она ждёт решения владельца.
Планировщик живёт задачей в lifespan приложения: итерация уходит в
asyncio.to_thread (внутри синхронные SQLAlchemy и requests), цикл отменяется
вместе с приложением, сбой итерации логируется и не прекращает напоминания.
Сводка отправляется в окне 09:00-14:00 по REMINDER_TIMEZONE и молчит при нуле.
Заодно починен свой service worker: createHandlerBoundToURL('index.html')
бросал исключение при вычислении воркера в dev-режиме, где манифест precache пуст
(VITE_PWA_DEV=1), и регистрация падала целиком — отлаживать push было негде.
Маршрут теперь ставится только если index.html и правда в precache.
ТЗ 0.89: §3.21 дополнен подразделом «Напоминания», §5 — PushDelivery, §7 — строка
этапа, §8 — открытые вопросы про часовой пояс напоминаний и ретенцию журнала.
root
committed
8 hours ago
|

Системные уведомления: транспорт Web Push, события агента, настройки (0.88)
...
О работе агента владелец узнавал только из открытой вкладки: SSE-кадры
task.claimed/task.review живут ровно столько, сколько она открыта, а агент
работает как раз тогда, когда вкладки нет. Web Push — второй транспорт тех же
событий на той же шине; SSE остаётся за мгновенный refetch интерфейса.
Backend: pywebpush и пара ключей VAPID (app/vapid.py печатает готовые строки
для .env; пустые ключи — push тихо выключен, а не падение на старте),
таблица push_subscriptions (endpoint уникален глобально: тот же браузерный
профиль может войти другим аккаунтом — подписка перепривязывается),
app/api/push.py (config/subscribe/unsubscribe/subscriptions), тексты
уведомлений на ru/en/uk собирает сервер — у service worker локалей нет.
Отправка идёт вне HTTP-ответа (BackgroundTasks в REST, поток в MCP) со своей
сессией; мёртвые подписки (404/410, серия ошибок) удаляются лениво.
Frontend: свой src/sw.ts через strategies: 'injectManifest' — в generateSW
обработчик push вставить некуда; precache, SPA-fallback и CacheFirst переехали
из vite.config.ts в воркер, у SW отдельный tsconfig (WebWorker не уживается с
DOM). Подавление при видимой вкладке — по visibilityState клиента, иначе
системное уведомление дублировало бы тост. Разрешение спрашиваем при входе
(жеста нет — браузер вправе проигнорировать), надёжный путь — переключатель
в Настройках.
Тесты: подписка/отписка/перепривязка, отправка с подменённым webpush, связка
событий. Фоновую отправку в тестах глушим: она идёт в чужом потоке по общему
SQLite-соединению и перемешивала транзакции тестов мандата.
ТЗ 0.88: §3.21 «Системные уведомления», правки §3.14, §3.15, §5, §7.
root
committed
9 hours ago
|
Аренда задачи агентом: взятие, heartbeat, возврат (0.85)
root
committed
10 hours ago
|

Мандат агента: флаг доступности и гейт (0.85)
...
- services/claims.py: рамка мандата (ТЗ 3.20) — агент меняет только задачи с флагом
ai_eligible, а сам флаг ставит только владелец: иначе «пометил и закрыл» обходило
бы правило. Ошибка — доменным исключением MandateError: REST отдаёт по нему 403,
MCP — текст с подсказкой следующего шага (стиль 3.10), а правило одно на оба пути.
- REST: PATCH проверяет рамку до правок; create принимает флаг (своя задача — можно),
пишет created_by_kind/created_by_name. Список задач фильтруется по ai_eligible,
created_by_kind и accept_state — признаки лежат в самой задаче, а не в join'е.
- MCP: update_task принимает ai_eligible (только владелец) и отказывает агенту на
задаче без флага с подсказкой «попросите владельца пометить или действуйте с
is_user=true»; create_task принимает флаг; list_tasks фильтруется теми же полями;
_compact отдаёт агенту ai_eligible, created_by_kind/name, accept_state, claimed_by.
- tests/test_agent_mandate.py: 12 тестов — дефолт «нет», гейт REST и MCP, отказ агенту
в выдаче флага себе, Bearer-токен на REST как агент по умолчанию и владелец под
заголовком X-Actor, фильтры списка, поля мандата в ответах тулов.
ТЗ 0.85.
root
committed
10 hours ago
|

Журнал действий по задачам и API журнала (0.85)
...
- services/tasklog.py — единственная точка записи в task_events, общая для REST и
MCP: иначе «кто закрыл задачу» отвечалось бы по-разному в двух путях. Заголовок
хранится снапшотом, запись об удалении ссылку не оставляет вовсе (FK с ON DELETE
SET NULL обнулил бы её сам, но в SQLite тестов внешние ключи не проверяются, и
журнал ссылался бы на несуществующую задачу). status_transition() пишет переход
кодами («to_do→in_progress») — журнал не хранит переводов, подписи даёт интерфейс.
- api/task_events.py: GET /api/task-events — первый пагинированный список API,
конверт {items, total, page, per_page}, потолок per_page 100, фильтры по типу
события, актору, каналу, проекту и задаче; порядок — свежие первыми, id вторым
ключом (у записей одной секунды иначе нет устойчивого порядка).
- Запись событий из HTTP: создание, закрытие (с комментарием), смена статуса, выдача
и снятие доступности агенту, удаление. Пишутся только настоящие перемены: PATCH,
ничего не изменивший, журнал не засоряет.
- Запись событий из MCP: те же события в тех же местах; у update_task и complete_task
появился параметр comment — что сделано (у агента он станет обязательным вместе с
приёмкой).
- main.py: роутер журнала подключён; conftest: стаб mcp_ctx принимает актора, а
agent_ctx() даёт контекст агента (по умолчанию актора нет — действие владельца,
так вызывают тулы тесты, написанные до мандата).
- tests/test_task_events.py: 9 тестов — события из UI и MCP, фильтры, конверт
пагинации и потолок per_page, изоляция журнала по пользователю, снапшот заголовка
после удаления.
ТЗ 0.85.
root
committed
10 hours ago
|

Мандат ИИ-агента: поле задачи, модель журнала, миграция (0.85)
...
- ТЗ 0.85: новая §3.20 «Мандат ИИ-агента: доступность, взятие, приёмка,
журнал» (четыре части: флаг доступности задачи, аренда, приёмка,
журнал) и правки §3.4 (плоскость приёмки рядом с raw/approved), §3.5
(экземпляр наследует доступность), §3.10 (тулы и флаг is_user), §3.13
(момент начисления XP при агентском закрытии), §3.14 (kind'ы task.claimed
и task.review), §5 (поля Task, таблица TaskEvent, конверт пагинации),
§7 (этап M5+), §8 (открытый вопрос о ретенции журнала). Отдельным
абзацем зафиксировано, что ai_eligible — не граница безопасности.
- models.Task: ai_eligible (по умолчанию нет), created_by_kind/created_by_name
(под фильтр «создано агентом»), done_by_kind, accept_state (плоскость
приёмки, отдельная от статуса — «done = сделано» сохраняется для XP,
статистики, теплокарты и спавна регулярных) и аренда: claimed_by_token_id
(FK mcp_tokens, ON DELETE SET NULL — label токена не уникален, поэтому
сравнивать арендатора можно только по id), claimed_by_name, claimed_at,
claim_expires_at. Свойства claim_is_alive и claimed_by: протухшая аренда
наружу не показывается. Индексы (user_id, ai_eligible/accept_state/
created_by_kind).
- models.TaskEvent — журнал «кто, что и когда»: task_id обнуляется при
удалении задачи (ON DELETE SET NULL), заголовок хранится снапшотом, иначе
записи умирали бы вместе с задачей. Актор (actor_kind/actor_name/via),
комментарий, вид события.
- schemas: TaskCreate.ai_eligible, TaskUpdate.ai_eligible и close_comment
(не поле задачи — уходит в событие закрытия, как earned_via), поля мандата
в TaskOut, новые TaskEventOut/TaskEventPage/AcceptIn.
- services/recurrence._copy: новый экземпляр регулярной задачи наследует
доступность агенту и автора правила.
- Миграция a1b2c3d4e5f7 (после d4e5f6a7b8c9): поля и индексы tasks, таблица
task_events. Существующие задачи бэкфиллятся server_default — «не доступно
агенту, создано пользователем»; accept_state и done_by_kind остаются NULL,
приёмка к старым закрытиям не применяется.
ТЗ 0.85.
root
committed
10 hours ago
|
| 2026-10-09 |

Проекты: цвет, сайт, фавикон, закрепление, README из репозитория (0.63–0.67)
...
- Цвет-метка проекта (0.63): у проекта может быть цвет-кружок. Селектор
цвета — сырым блоком в стиле кита (в ките компонента нет); кружок рисует
общий компонент ProjectColorDot.
- Сайт проекта (0.63): отдельное поле site_url. Иконку сайта тянет
бэкенд-прокси с кэшем (GET /api/projects/{id}/favicon, том favicons) —
сторонние favicon-сервисы не используются, данные не покидают VPS. В 0.65
иконка встала в метку проекта (кружок + фавикон), а ссылка на сайт —
чипом в шапке страницы проекта.
- Карточка списка кликабельна целиком (0.63; кнопка «Открыть» дублировала
ссылку), тайтл крупнее (0.65), клетки прогресса — поверх полосы кита
(0.64): сегментированной полосы в ките нет.
- Закрепление проектов (0.66): поле pinned, закреплённые — секцией сверху,
сортировка списка по свежести активности, подвал карточки — отсчёт до
ближайшего дедлайна.
- Место булавки переставлено (0.67): на карточке — правый верхний угол
рядом с бейджем актуальности и видна всегда (была приглушена в подвале),
на странице проекта — кнопка-булавка рядом с именем (искали строкой
«Закреплён» в параметрах).
- Репозиторий проекта (0.67): ссылка на git-репозиторий — отдельное поле
repository_url, отдельно от заметки и сайта. README из репозитория
показывается вторым описанием — карточкой «README из репозитория» под
заметкой, со ссылкой на репозиторий и сворачиванием длинного текста;
тянет такой же бэкенд-прокси с кэшем (GET /api/projects/{id}/readme),
не нашли — «README не найден». Распознавание ссылки на репозиторий в
заметке проекта снято (у задачи README-вместо-описания остаётся),
данные в БД не мигрировались.
- Фантомный отступ перед именем у проекта без цвета и фавикона (0.67):
ProjectMark рисовал пустой спан в ряду с gap.
- ТЗ 0.63–0.67; MCP зеркалит поля проекта (color, site_url,
repository_url, pinned). Тесты: 207.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
1 day ago
|

Сад отключён (0.58): страница «Достижения» вместо сада
...
Сад не готов к использованию, а сервис разрастается — убрали его из
интерфейса и сосредоточились на основном функционале. Код сада,
модель GardenItem, coin_events и rarity у xp_events сохранены: сад
вернётся для доработки.
- Выдача растений прекращается сама: GardenItem создавался лениво в
ensure_garden(), которую звал только GET /api/garden, — UI его
больше не запрашивает.
- Прогресс и ачивки переехали на новую страницу /achievements (пункт
меню на месте «Сада»); /garden редиректит туда же.
- Блок ачивок вынесен в общий components/AchievementsPanel.vue —
им пользуются и «Достижения», и сад (для возврата).
- Баланс монет отдаёт GET /api/xp (coins) — единственная правка
бэкенда; паритет с /api/garden покрыт тестом.
- Тост закрытия — «+N XP • +N монет» с похвалой, без слов про
растение; золотое конфетти за xp >= 25 и «не хочется делать».
- Тексты прогресса — в общем неймспейсе progress.*, сад-специфичные
остались под garden.*.
- ТЗ 0.58: пункт «Сад отключён» с причиной, детальное описание сада
оставлено историческим блоком.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
1 day ago
|
| 2026-09-23 |
Ребаланс сада: монеты ×2 за закрытие, микронаграды и бонус уровня ×2
...
- Монеты за закрытие: xp целиком вместо floor(xp/2)
- Микронаграды: создание задачи 1 → 2, проекта 2 → 4
- Бонус за уровень: 25·L → 50·L; ежедневная не менялась (50)
- Заголовки X-Created-* теперь из констант (были захардкожены и
разошлись бы с новыми значениями)
Цены (семена, апгрейды, декор, расширения) не тронуты. ТЗ 0.48 (§3.13).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
18 days ago
|
| 2026-09-22 |
Детализация: выброшенные из заголовка детали не теряются
...
Промпт (ru/en) требует переносить убранное из заголовка (условия, числа,
уточнения) в описание, few-shot пример теперь учит переносу (description
не null). Страховка от нарушения правила маленькими моделями: если заголовок
сокращён, описания нет, а содержательные слова/числа исходника пропали —
исходный заголовок целиком кладётся в описание (при заполненном пользователем
описании не срабатывает). ТЗ 0.46 (§3.2).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
18 days ago
|
README.md вместо описания-ссылки на репозиторий
...
Если описание задачи — одна ссылка на git-репозиторий, на странице
задачи вместо неё показывается README.md оттуда (с подписью-ссылкой
на источник; сырое описание в БД не меняется, чекбоксы README не
патчат описание).
Хост не обязан быть GitHub/GitLab (ТЗ 3.3, 0.41): селф-хостед системы
ищутся эвристикой — API github/gitlab/gitea + raw-пути web-интерфейсов
с дефолтной веткой HEAD (проверено на селф-хостед GitBucket);
не нашли — 404, остаётся ссылка. Кэш прочтений в памяти, 10 минут.
- backend: services/readme.py (эвристика + перебор, кэш),
GET /api/repo-readme?url=, тесты (кандидаты, кэш) — 159 тестов
- frontend: api.getRepoReadme, TaskView показывает README вместо
описания при одиночной ссылке; локали task.readmeFrom ×3
Проверено headless на живом фронте: git.gnexus.space (GitBucket) и
github.com — README рендерится, при неудаче остаётся ссылка.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
18 days ago
|
Двуязычный промпт детализации: язык задачи → язык промпта
...
Маленькие модели мимикрируют под язык промпта, а правило «отвечай по-русски»
внутри английского промпта игнорируют: qwen3.5:2b отвечал английским на русские
задачи. Промпт теперь строится на языке исходника (_is_cyrillic → _prompt_ru /
_prompt_en), few-shot пример тоже на языке задачи.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
18 days ago
|
ИИ-детализация: только оформление (заголовок/описание/теги EN), суммаризация проектов (ТЗ 0.40)
...
- Убрано ИИ-планирование с 0% попадания: LLM больше не предлагает
проект, приоритет и оценку времени — их заполняет пользователь
- Заполненные пользователем поля не угадываются при первичном анализе;
«Переспросить ИИ» (force) — полный анализ
- Ответ LLM — на языке исходного текста; новые теги — только английские
- Улучшено сжатие заголовка: лаконичное название + описание с шагами
- Суммаризация заметок проектов (projects.summary, миграция
a9b8c7d6e5f4): контекст LLM вместо полных заметок; перегенерация
при правке заметки
- Фикс \u-эскейпов в JSON-колонках (ensure_ascii=False): MCP-агенты
видят кириллицу, а не «u0432»
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
18 days ago
|
Сад: компактный старт 24×12, два расширения (ТЗ 0.37)
...
- старт 24×12 (было 32×20): дворик заполняется быстро, расширение
ощущается событием
- расширение I (ур. 3, 100) возвращает прежний старт 32×20; II (ур. 12,
250) — до 40×28; расширения III–V убраны
- предметы существующих садов, оказавшиеся за новой границей,
переставляются по спирали к дому при первом заходе (идемпотентно)
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
18 days ago
|
| 2026-09-21 |
Имя и аватар из SSO: users.name, синк по webhook и при логине (ТЗ 0.36)
...
- users.name (display_name из SSO), миграция f1a2b3c4d5e6
- webhook user.profile_updated: name ← profile.display_name (ключа нет —
не трогаем), avatar_url, locale; username_changed имя не меняет
- /auth/callback: name в сессию и БД (display_name, fallback username)
- /auth/me: профиль из БД, не из cookie-сессии — изменения от webhook
видны сразу, без повторного логина
- фронт: имя в сайдбаре (identity-title), email в meta
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 days ago
|
Вебхуки от gnexus-auth: профиль, статус, отзыв MCP-токенов (ТЗ 0.35, 3.16)
...
- POST /auth/webhook: HMAC-SHA256 через gnexus_gauth (verify_and_parse_webhook),
секрет GAUTH_WEBHOOK_SECRET; не задан — 503 (приём выключен)
- user.email_changed / user.profile_updated — синхронизация User (email,
locale, avatar_url) без повторного логина
- user.blocked / user.archived / user.deleted — отзыв всех MCP-токенов
пользователя (агент теряет доступ вместе с аккаунтом); cookie-сессии
stateless — закрываются на стороне SSO (документировано)
- незнакомые типы (включая webhook.test) — 200 без действий, против retry-шторма
- тесты: подпись, 503 без секрета, битый payload, отзыв токенов, sync профиля
(9 новых; всего 145 passed)
- ТЗ 0.35 (3.16), deploy.md: GAUTH_WEBHOOK_SECRET + настройка webhook (п. 3.1)
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 days ago
|

Мультиюзерность: изоляция данных, per-user MCP-токены, claim при первом логине (ТЗ 0.34, бэкенд)
...
- Модели: User (профиль из SSO), McpToken (только sha256-хэш),
user_id на tasks/projects/tags/documents/xp_events/coin_events/garden_items;
уникальности имени проекта/тега — составные (per-user); app_settings →
per-user (PK user_id+key), старая таблица → app_settings_global (источник claim)
- Миграция f9a0b1c2d3e4 (Postgres): новые таблицы, user_id на 7 таблиц,
составные уникальности, частичные индексы дейли/уровня per-user
- Claim-on-first-login (ТЗ 1.2): первый вошедший забирает строки с user_id NULL
и копирует глобальные настройки; upsert профиля в callback
- Scoping всех API и сервисов: tasks/projects/tags/attachments/settings/xp/
garden + xp/garden/closing/options/recurrence/detailing; чужое — 404
- MCP (ТЗ 3.10): per-user bearer-токены вместо env MCP_TOKEN (middleware →
sha256 → request.state.gntodo_user_id), 8 тулов с Context; SSE publish(uid)
- API /api/mcp-tokens: список/генерация (plaintext один раз)/отзыв
- Тесты: test_multiuser (изоляция двух пользователей, claim, per-user дейли/
настройки), test_mcp_tokens; 135 passed, ruff, mypy strict
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 days ago
|
MCP-эргономика для небольших моделей и проектный контекст (ТЗ 0.30)
...
- Серверные instructions FastMCP: порядок работы, привязка задач к проекту,
восстановление контекста через get_project, запрет придумывать id.
- Новые тулы: list_projects (заметка + открытые задачи), get_project (по id
или имени, полная заметка + открытые задачи), list_tags.
- Адресация проекта по имени: project_name (регистронезависимо) в
create_task/update_task/list_tasks; сравнение в Python — lower() в SQLite
не понимает кириллицу.
- Ошибки с подсказкой следующего шага; create_task без проекта отдаёт hint.
- Описания всех параметров через Annotated/Field (enum, форматы, диапазоны).
- _compact отдаёт project_id; обновлённые докстринги тулов.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 days ago
|
Пачка 4 ревью: расхождения с ТЗ
...
- Фильтр по приоритету в списке задач (ТЗ 3.6): параметр priority в
GET /api/tasks — градации шкалы 0-10, без приоритета = very_low;
селект в ListView, ключи i18n в ru/en/uk, тест.
- Статистика (ТЗ 3.13): метрики «закрыто», XP, график и теплокарта считают
только закрытия (kind='task') — микронаграды за создание больше не
попадают в «закрыто».
- apply_proposal привязывает задачу только к неархивному проекту; каталог
проектов для LLM-детализации тоже без архивных (ТЗ 3.8.1).
- i18n: «ч» часов в достижениях сада — через словарь (taskui.time.h).
- Докстринги: актуальные номера разделов ТЗ (MCP 3.10, архив 3.8.1).
ТЗ 0.29 (семантика фильтра приоритета в 3.6).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 days ago
|

Пачка 3 ревью: гонки наград и остатки фронтенда
...
- Гонка дейли: уникальный частичный индекс (source, day) + колонка
coin_events.day; параллельные POST /api/xp/daily отдают granted=False
вместо 500 (IntegrityError ловится молча).
- Гонка ensure_garden (параллельные GET /api/garden): работа в savepoint,
при нарушении уникальности (uq_coin_level, kind+ref_id) — откат и выход;
дубли бонусов уровня и растений исключены.
- Параллельное закрытие: PATCH и MCP update_task берут задачу с FOR UPDATE
OF tasks (Postgres; SQLite игнорирует) — двойных наград/спавнов нет.
- redetail утверждённой задачи — no-op: ai_proposal больше не стирается,
фоновая LLM не будится (worker и так работает только с raw).
- Фронт: ProjectView подписан на SSE (task/project changed/deleted);
SSE-refetch на странице задачи не затирает начатый ввод тега;
просрочка дедлайна считается по локальной дате, а не UTC; бросок
предмета мимо сада игнорируется (не прилипает к краю); пауза refetch'а
сада держится до конца своего move-запроса, а не фиксированные 300 мс.
- Тест: redetail no-op для approved. 110 passed.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 days ago
|
Пачка 2 ревью: общий путь закрытия — MCP-награды и анти-дюп спавна
...
- Новый services/closing.py: handle_task_closed — спавн регулярной + XP +
монеты + бонус уровня; используют и HTTP-API, и MCP-инструменты. Закрытие
агентом теперь даёт те же награды, что и UI (ТЗ 3.5, 3.13, 3.10).
- Задача.spawned_at (+миграция): спавн — один раз на цепочку; раньше MCP
спавнил следующий экземпляр при каждом обновлении закрытой задачи.
- Выход из done сбрасывает done_at (HTTP и MCP): повторное закрытие снова
проходит через ветку закрытия без дублей наград (открытый вопрос ТЗ 7).
- MCP create_task: микронаграда за создание, как в HTTP-API; broadcast
xp.changed (celebrate:true при закрытии, false при создании).
- Тесты: MCP-награды при закрытии и создании, спавн ровно один раз,
un-done/повторное закрытие без дублей (HTTP и MCP). 109 passed.
ТЗ 0.28.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 days ago
|
Пачка UI: полоска таймера тостов, мобильный «+», ширина md-редактора, мета-бейджи
...
- Тосты: у .toast-progress-bar кита анимация без duration мгновенно схлопывала
полоску таймера (forwards до scaleX(0) поверх inline transform адаптера);
отключаем селектором той же специфичности (.toast .toast-progress ...).
- Кнопка «Новая задача» на мобильном — компактная кнопка-иконка вместо
растянутого .btn с пустым хвостом после скрытия лейбла.
- Инлайн-редактор описания задачи на всю ширину контейнера (max-width .form-group).
- Ссылка на проект в мете страницы задачи — inline-flex, бейджи на одном базлайне.
- Detailing: зачистка HTML-тегов из markdown-ответов LLM (_clean_markdown).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 days ago
|
ИИ-детализация: короткий тайтл, шаги в описании, контекст проектов, валидация (ТЗ 0.25)
...
- LLM предлагает короткий заголовок для перегруженных тайтлов и описание
с нумерованными шагами, если они очевидны
- в контекст добавлены открытые проекты с описаниями — назначение по смыслу
- теги: разрешены новые (1-2 при отсутствии подходящих), до 3, не обязательно один
- валидация ответа: заглушки «NULL»/пробелы отбрасываются, дубли схлопываются,
new_project без валидного имени не протекает; несуществующие теги создаются
при утверждении
- фронт: строки «Новый заголовок»/«Описание с шагами» в карточке предложения
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 days ago
|
Пачка UI + Ollama + SSE-реактивность (ТЗ 3.14)
...
- Бейдж приоритета с иконкой (PriorityBadge) во всех видах
- Фильтры страницы проекта запоминаются в localStorage (глубокий watch)
- DurationInput (ч/мин, шаг 5), ConfirmDialog, пагинация, иконки удаления
- Диалоги: «В архив» warning, кнопки поменяны местами
- Автодетализация на Ollama 192.168.1.130 (qwen3.5:2b, think:false)
- SSE /api/events: шина realtime, публикации после коммита, coarse-события + refetch
- Фронт: realtime.ts, удаление поллинга StackView, refetch по видам,
подавление во время drag сада, дедуп XP-тостов
- Тесты 93 passed, e2e двух вкладок; ТЗ 0.24
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
19 days ago
|
| 2026-09-20 |
Сад: инвентарь декораций — покупка кладёт на склад, drag на карту и обратно
...
- Бэк: garden_items.x/y nullable; покупка декорации не размещает её,
а кладёт в инвентарь; PATCH с x/y null возвращает декорацию в инвентарь
(растениям — 400); авторазмещение учитывает только размещённые элементы.
- Фронт: полоса инвентаря под сценой с чипами, drag из инвентаря на карту
с призраком у курсора, drag размещённой декорации обратно в инвентарь,
GnAlert «Куплено…» в маркете. Фикс: убран pointer-events:none у декораций —
из-за него декорации нельзя было перетаскивать по карте.
- Миграция f2a3b4c5d6e7, локали ru/en/uk, ТЗ 0.15.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
20 days ago
|
Сад-сцена: монеты, растения на карте, магазин декораций и расширений
...
Контент открывается за опыт, покупается за монеты: XP — ранг садовника
(только растёт), монеты — валюта сада. Монеты за закрытие = floor(xp/2)
(заголовок X-Earned-Coins) и бонус 25×L за уровень; журнал — coin_events.
- Бэкенд: CoinEvent/GardenItem + миграция с backfill монет; сервис сада
(баланс, авторазмещение спиралью вокруг домика, виды по уровням,
premium только у rare/epic); API /api/garden — сцена, перемещение,
апгрейд растений, магазин декораций и расширений карты.
- Фронт: SVG-сцена GardenScene (сетка 20px, домик, drag&drop, поповер
роста), кошелёк в шапке, маркет (расширения, декорации, каталог видов
с замками), тост «+XP • +N монет», локали ru/en/uk.
- Тесты сада (10), ТЗ 0.14 (3.13 + модель данных).
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
20 days ago
|