| 2026-10-10 |

Иконки: углы прозрачны у всех без исключения — расширение, iOS и maskable (0.96)
...
Иконки расширения растривались непрозрачным вариантом (`raster_opaque`), а он
впечатывает фон `--gn-surface-page` — тот же, что заливка холста логотипа. Со
впечатанным фоном скругление сливается с холстом, и знак читается квадратом с
острыми углами: в панели браузера, на странице расширений и в магазине. Это тот же
просчёт, что в 0.90 у иконок «any» (там его заметил владелец в шторке уведомлений),
только иконки расширения тогда остались непрозрачными по недосмотру.
Раз впечатанный фон всё равно съедает скругление, непрозрачными не остаётся никто
(решение владельца 2026-10-10): на прозрачные углы переведены и apple-touch-icon, и
maskable-512, `raster_opaque` удалён. apple-touch-icon (180) — тот же знак во весь
кадр: iOS прозрачность рисует чёрным, но свою маску накладывает поверх и углы
срезает. У maskable-512 скруглён во всю иконку кадр, а знак стоит в safe zone 80%:
при обрезке по кругу Android срезает кадр, но не знак. Там, где иконка показывается
без чужой маски — лаунчер, магазин, панель браузера, — скруглённая читается лучше
квадрата.
Скрипт переставлен так, чтобы просчёт не повторился: по умолчанию `raster_rounded`
(знак во весь кадр, углы прозрачны), исключение теперь одно и с объяснением —
`raster_maskable`. ZIP расширения перепакован (build + pack), иначе страница
«Расширение» отдавала бы старые иконки; сам файл в git не хранится, его собирает
Docker.
Проверено попиксельно (`magick identify`: `%[channels]` → srgba, `%[pixel:p{0,0}]` →
srgba(0,0,0,0); у 16px угол сглаженный, srgba(25,25,33,0.12)) и глазами по сетке всех
девяти иконок в 3x на светлом фоне: углы прозрачные, скругление читается, знак у
maskable — в safe zone. Содержимое ZIP сверено с исходниками побайтово.
ТЗ 0.96: §3.19, §7.
root
committed
3 hours ago
|

Стек входящих: только задачи без проекта, карточка как в списке, выдача «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
3 hours ago
|

Журнал и список задач с телефона: иконка комментария, переключатель «Созданные агентом» (0.94)
...
Набор правок по интерфейсу, в основном про чтение с телефона.
Журнал:
- фильтр по проекту убран: проектов у владельца будут сотни, и селект из них нужный
не находит — разрез по проектам живёт в разделе «Проекты», как и в общем списке
задач (0.73). Со страницы ушла и загрузка активных проектов: справочник имён
по-прежнему собирается из активных и архивных;
- комментарий показывается иконкой и только там, где он есть. Записей без
комментария в журнале большинство, и колонка пустых ячеек съедала половину ширины
таблицы, а текст автора (у закрытия агентом — абзац) в ячейке не влезал и рвался по
строкам. Целиком он открывается модалкой, в ней же вид записи и заголовок задачи:
диалог открывается из строки, под ним остаётся весь журнал, и без этих слов
непонятно, чей это комментарий;
- заголовок колонки — та же иконка: слово «Комментарий» шире колонки, в которой
теперь стоит одна кнопка. Слота заголовка у GnTable нет, но label принимает
готовый узел, поэтому иконке отдан h('i') с aria-label — иначе колонка осталась бы
для скринридера безымянной;
- дата и время разложены на две части: на десктопе читаются как раньше
(«10.10.2026, 22:23»), на телефоне встают в две строки и таблицу не растягивают.
formatDateTime из taskui стал не нужен и удалён;
- телефон (≤767px): отступы ячеек 8px, бейдж события мельче (моноширинный капс 13px
рвал «ДОСТУПНОСТЬ» и задавал таблице нижнюю границу ширины), колонки «Проект» и
«Актор» скрыты. Актор возвращается строкой под бейджем события: своей колонкой он
оставлял половину ширины пустой, а журнал существует ради актора. На 390px таблица
ровно по ширине экрана и не едет по горизонтали.
Список задач:
- «Кто создал» стал переключателем «Созданные агентом» вместо селекта из трёх
пунктов: средний пункт («только мои») ничего не прибавлял к «всем» — своих задач у
владельца подавляющее большинство, — а спрашивалось ровно одно: «показать то, что
завёл агент». Переключатель это и говорит.
Локали: ушли filterProject/filterProjectAll и filterCreated/createdAll/createdMe,
появились showComment и общий common.close — у модалки без своего действия «Отмена»
читалась бы как отказ от чего-то.
Проверено живым прогоном в браузере (временный пользователь, данные засеяны SQL,
XP не начислялся): шапка без фильтра по проекту, кнопка комментария у 5 записей из
10 (3 текста автора и 2 машинных кода, которые интерфейс читает словами), модалка с
полным текстом, «Закрыть» переведена, на 390px нет горизонтальной прокрутки, в списке
три переключателя и created_by_kind=agent в запросе. Стенд вычищен.
ТЗ 0.94: §3.6, §3.20, §7.
root
committed
4 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
4 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
5 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
6 hours ago
|

Инлайн-правка длительности и скругление иконок (0.90)
...
Поля «Оценка времени» и «Фактически» были нерабочими: очищенное поле мгновенно
наполнялось прежним числом. Причина — кольцо «поля → значение → поля»: на каждую
цифру компонент отдавал родителю сумму, а родитель возвращал её назад, и раскладка
затирала набираемое («30 мин» → «0 ч 30 мин» в том самом поле, которое человек
только что очистил). Теперь текст полей — источник истины, пока в них печатают:
пришедшее значение применяется, только если не совпадает с уже набранным.
Границы (1…1440 мин, как в API) зажимаются на выходе из полей, а не на вводе —
зажим на каждой цифре переписывал бы число под курсором. Там же минуты
сворачиваются в часы («0 ч 90 мин» → «1 ч 30 мин»), а «Фактически» получило min
как у API (ge=1): «0 мин» сервер отвечал 422-й после нажатия ✓.
Строка параметров: редактор ставит фокус в первое поле (клик по значению =
«начать печатать»), а Esc отменяет независимо от того, где фокус. Обработчик на
самом компоненте ловил событие, только пока фокус оставался внутри, — после
клика по соседней строке Esc молча переставал работать, а сразу после открытия
не работал вовсе.
Иконки: скругление логотипа (rx 10) не было видно — растрирование впечатывало в
PNG фон страницы, тот же, что заливка холста, и углы сливались с ним: и в шторке
уведомлений, и у установленного приложения иконка читалась квадратом с острыми
углами. У иконок «any» (192/512 — они же icon системного уведомления) углы теперь
прозрачные. Где прозрачность недопустима, фон остался впечатан: maskable
обрезается Android по кругу, а для iOS добавлен отдельный apple-touch-icon.png
(180, непрозрачный квадрат — прозрачность iOS рисует чёрным).
ТЗ 0.90: §3.15 — состав иконок и правило скругления, §3 — фокус и Esc в
инлайн-редакторе, ввод длительности часами и минутами.
root
committed
7 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
7 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
7 hours ago
|
Промпт для ИИ-агента: кнопка на странице задачи (0.87)
...
Текст задачи целиком — параметры, описание, предложение ИИ, подзадачи и
ссылка — собирается в aiPrompt.ts и копируется одной кнопкой из меню «⋯».
root
committed
8 hours ago
|
Форма создания задачи закрывается на мобильных (0.86)
...
Порог мобильного вынесен в viewport.ts — один на шторку меню и формы.
root
committed
8 hours ago
|
Интерфейс мандата: бейджи, приёмка, журнал, тосты (0.85)
root
committed
8 hours ago
|
Приёмка агентской работы: ждёт приёмки, принять, вернуть (0.85)
root
committed
8 hours ago
|
Аренда задачи агентом: взятие, heartbeat, возврат (0.85)
root
committed
8 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
9 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
9 hours ago
|

Актор действия: сессия — владелец, токен — агент (0.85)
...
- app/actor.py: Actor (kind, name, via, token_id, owner_name) — кто и через что
сделал. Bearer-токен по умолчанию даёт агентский актор: иначе действие
ИИ-агента в базе неотличимо от действия браузерного расширения, а оба ходят
одним токеном владельца. declares_user() читает заголовок X-Actor: user;
declaring_user() превращает агентский актор во владельческий, сохраняя
канал — в журнале читается «я, но действие пришло от агента». Нет сведений о
токене (тесты подменяют авторизацию) — актор владельца: агентских полномочий
ни у кого нет.
- dependencies.user_from_bearer выбирает строку токена целиком (id, label,
имя владельца — OUTER JOIN, строки users может не быть) и кладёт актора в
request.state. require_user для сессии SSO кладёт актора-владельца с именем
из профиля. Новые actor_of() и ActorDep рядом с UserIdDep: зависимость
объявлена после require_user, поэтому 401 приходит раньше, чем кто-либо
спросит «кто действует».
- main.McpAuthMiddleware: тот же выбор строки токена для /mcp, актор всегда
агентский — объявить действие владельца тул может только своим аргументом
is_user, а не заголовком запроса.
ТЗ 0.85.
root
committed
9 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
9 hours ago
|
Теплокарта проекта всегда за полгода (0.84)
...
- ProjectHeatmap: переключатель периодов убран — период один, полгода
(26 недель, 182 дня). Полугодовая сетка в содержимое карточки влезает
целиком (26 недель по 9px с зазором 2px = 284px), а месячная занимала
пятую часть ширины и читалась недоделкой. Вместе с табами ушла и
раздача размеров ячеек по режимам: ячейки снова 9px, легенда шкалы —
под сеткой, как в статистике.
- Локали: ключи projectPage.heatMonth/heatHalf удалены из ru/en/uk —
после снятия переключателя их никто не использует (stats.heatHalf, табы
периодов на странице статистики, остались на месте).
- ТЗ 0.84.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
10 hours ago
|

Теплокарта проекта, облако тегов, две колонки, иконка подвала (0.83)
...
- ProjectHeatmap.vue: своя карточка «Активность» в сайдбаре страницы
проекта — та же сетка «как в гитхабе», что в статистике, но по задачам
одного проекта: столбец — неделя, строка — день недели, заливка — по
числу закрытых за день (done_at), в подсказке клетки дата и оба числа
(«закрыто N, создано M»). Периодов два: «За месяц» (5 недель, по
умолчанию) и «За полгода» (26 недель). Запросов к серверу нет: страница
проекта и так держит все свои задачи, включая завершённые. В месячном
режиме ячейки 18px, в полугодовом 9px (26 недель — 284px, влезает в
сайдбар без прокрутки): пятью неделями мелкая сетка терялась бы в
широкой карточке. Легенда шкалы встаёт рядом с сеткой, а когда сетка во
всю ширину — переносится под неё.
- ProjectView: карточка «Теги» — облаком, а не списком: размер шрифта по
ранжиру частоты (0.85–1.2em, число задач — в подсказке), клик ставит тег
в фильтр (тот же поиск по подстроке), повторный клик снимает, активный
тег подсвечен; до 960px список подтягивается к верху экрана — там
сайдбар стоит ниже списка.
- ProjectView: карточки задач раскладываются по две в ряд (до 700px — одна
колонка, как и раньше).
- TaskCard: пункт меню «Переместить в проект» сокращён до «В другой
проект» — длинная надпись рвалась на две строки в китовом меню 220px.
- kit-overrides.css: в подвале карточки снята китовская поправка иконки
(.ph.normalize, top:.15em). Она рассчитана на иконку на общей базовой
линии с текстом, а строка подвала — флекс с align-items:center: коробка
иконки центрируется сама (у иконочного шрифта нет нижнего выноса), и
поправка складывалась с этим центрированием — глиф уезжал вниз на 1.3px
(замер по пикселям: центр глифа ниже центра цифры «2» в «2 недели
назад»). Без поправки низ глифа садится на базовую линию, как у буквы.
Правка самого кита заказана отдельной задачей (vertical-align вместо
position:relative; top — на флекс-элемент он не действует).
- ТЗ 0.83.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
11 hours ago
|

Корешок у карточки проекта, «без активности», перенос задачи в проект (0.82)
...
- ProjectsView/ArchiveView: у карточки проекта левая грань 6px (китовый
--gn-border-width-accent, тот же шаг, что у полосы шапки страницы) и цвета
проекта — того же, что метка у имени и заливка прогресса; у проекта без
цвета грань берёт акцент кита. У карточки задачи рамка осталась ровной 2px:
по одной этой грани видно, что перед тобой папка с задачами, а не сама
задача. Тот же корешок у карточки проекта в архиве и у заготовок списков,
иначе при загрузке карточка дёрнула бы ширину содержимого. Наведение
подкрашивает рамку, но не корешок — цвет грани принадлежит проекту, а не
состоянию карточки.
- ProjectsView: у проекта без задач подвал больше не пустует — вместо
свежести стоит «без активности» (ключ projects.noActivity, три локали).
- kit-overrides.css: полоса прокрутки спрятана у всех табов — правило было
только у фильтра «Списка задач», и на странице проекта табы статуса
оставались с полосой. Прокрутка сохранена.
- TaskCard/TaskMoveDialog: в «⋯»-меню карточки задачи появились «Перейти к
проекту» (нет у задачи без проекта и на странице самого проекта) и
«Переместить в проект». Перенос — модалка с выбором проекта (GnModal +
GnSelect кита, первым пунктом «без проекта», текущий проект выбран по
умолчанию) → PATCH /api/tasks/{id} с project_id: XP и монеты за перенос не
начисляются, успех подтверждается тостом, а списки перечитываются по
событию moved (в «Выдаче» и на странице задачи подписки на task.changed
нет — без события карточка осталась бы в старом проекте).
- ТЗ 0.82.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
12 hours ago
|
Подвал карточки проекта как у задачи, строка подвала по центру (0.81)
...
- ProjectsView: подвал карточки проекта переехал в китовый слот #footer —
раньше это была строка внутри содержимого, без черты; теперь подвал прижат
к низу карточки и отделён чертой во всю ширину, как в карточке задачи.
Скелетон списка повторяет ту же полосу.
- kit-overrides.css: полоса подвала стала общей для карточек задач и проектов
(правила были одни и те же в двух местах). Отступы выровнены: у кита 8px
сверху против 15px снизу — строка висела на 3.5px выше середины полосы;
пустой подвал (проект без задач и без срока) держит высоту строки, иначе
черта у соседей по ряду встала бы на разной высоте.
- ProjectsView: иконки свежести и срока — с китовым классом normalize (глиф
иконочного шрифта садился выше середины строки и читался подвешенным).
- ТЗ 0.81.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
12 hours ago
|
Единая иконка «⋯», действия шапки справа на узком экране, полоса у табов (0.80)
...
- TaskView: иконка меню действий — ph-dots-three-outline, как в карточках
задач, стека и на странице проекта (был залитый ph-dots-three).
- App.vue: до 640px строка действий шапки растягивается на всю ширину и
прижимается вправо — у кита перенесённые действия встают у левого края,
и «⋯» на странице задачи оказывался под бейджами слева.
- ListView: у табов статуса спрятана полоса прокрутки (прокрутка осталась);
переключателю «Приоритетные» дан сдвиг 3px влево-вправо — бегунок кита
выступает за коробку контрола на 5px и вылезал левее соседей по ряду.
- ТЗ 0.80.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
12 hours ago
|
Шторка дропдауна из шапки, полосы прокрутки, меню у правого края (0.79)
...
- App.vue: position:relative + z-index у .page-header — только с 768px.
На мобильных они запирали fixed-шторку в контексте наложения шапки, и
затемнение (вне #app, z 1090) ложилось поверх неё, съедая тапы: меню «⋯»
на странице задачи открывалось затемнённым и не нажималось.
- kit-overrides.css: полосы прокрутки снова китовые (10px, дорожка #16161e,
бегунок #414868) — добавлено только скругление бегунка; scrollbar-width /
scrollbar-color больше не задаются, иначе Chromium игнорирует
::-webkit-scrollbar-* и скругление пропадает.
- dropdownClamp.ts: при развороте меню влево выставляем ещё и left: auto —
китовый left:0 продолжал действовать, и у правой колонки карточек меню
выезжало за вьюпорт (right 1593 при ширине 1440).
- ТЗ 0.79.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
13 hours ago
|
Страница задачи не едет по горизонтали, отступ табов статуса (0.78)
...
- TaskView: мобильный трек сетки — minmax(0, 1fr) вместо голого 1fr
(тот не сжимается ниже min-content: 517px в коробке 358px), колонки
получили min-width: 0 — на 390px страница уезжала вбок (scrollWidth 533).
- TaskView: overflow-wrap: break-word у md-view — длинный неразрывный токен
в описании (URL, инлайновый code) заставлял абзац скроллиться по горизонтали.
- ListView: отступ снизу у табов статуса 0.6rem → 1.25rem, табы липли
к первой карточке списка.
- ТЗ 0.78.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
15 hours ago
|

Список задач, карточки и шторка дропдауна на мобильных (0.68–0.77)
...
- Цвета проектов (0.68–0.72): палитра пересобрана с объективным порогом
различимости — 8 базовых тонов × ступени светлоты + 5 нейтральных = 27
клеток, минимальная попарная ΔE76 в CIELAB 12.5 (приглушённые варианты
сливались, чёрный не виден на тёмной панели). Метка проекта — кружок, у
проекта с сайтом — фавикон внутри метки.
- Список задач (0.70–0.74): фильтр по проекту и табы приоритета убраны,
приоритет — переключателем «Приоритетные» с серверной отсечкой
priority_min (7–10; задача без приоритета в отсечку не попадает). Тег
ищется подстрокой имени (параметр tag, icontains с autoescape) с debounce,
ввод — GnCombobox; статус — табы кита с горизонтальной прокруткой. То же
на странице проекта. Фильтры-поля переехали в слот действий шапки.
- Выравнивание фильтров (0.76): кит держит 8px margin-top у input и 15px
margin-bottom у form-group — внутри flex-обёртки они не схлопываются, и
поле стояло на 3.5px выше центра ряда, а крестик сброса (отсчёт от низа
обёртки) уезжал к нижнему краю поля. Отступы внутри фильтра обнулены:
обёртка стала ровно в коробку поля, центры сходятся до пикселя.
- Карточка задачи (0.75–0.76): кликабельна целиком (клик мимо внутренних
ссылок/кнопок и по невыделенному тексту), бейдж проекта — ссылкой.
Свежесть задачи переехала из ряда бейджей в подвал карточки (слот footer,
черта 1px, прижат к низу — в сетке все карточки на одной линии): height
100% содержимого у кита выталкивало подвал, карточка стала flex-колонкой.
Иконка рядом с текстом — с китовым классом normalize.
- Полосы прокрутки (0.76): тонкие, бегунок акцентом, скругление в пилюлю,
прозрачная дорожка — как у полосы меню-дровера кита. Заданы и
scrollbar-width/color, и ::-webkit-scrollbar-*; у дровера приоритетнее
правило кита.
- Шторка дропдауна на мобильных (0.77): до 767px меню — лист снизу во всю
ширину экрана с затемнением и «ручкой», пункты 44px, выезд снизу 0.22s
вместо китового overlay_reveal. Слой затемнения — div вне #app (index.html)
и body:has(.dropdown.is-open): для китового обработчика клик по нему «вне
меню», поэтому тап по затемнению закрывает меню без JS, а JS-кламп
координат на мобильных выключен. Китовый panel_boot оставляет у шапки
transform (содержащий блок для position:fixed) — на мобильных анимация
шапки снята, правило «меню в шапке прижато вправо» тоже оставлено
десктопу. Задача на перенос в кит оформлена отдельно:
gnexus-ui-kit/docs/tasks/gn-dropdown-mobile-sheet.md.
- Кнопка «назад» над шапкой страницы (0.70): показывается, когда в истории
приложения есть предыдущий экран; возврат на экран входа не предлагается.
ТЗ 0.68–0.77; тесты бэкенда 209, ruff/mypy чисто, сборка фронта зелёная.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
15 hours ago
|
| 2026-10-09 |
Булавка на карточке проекта — по центру бейджа актуальности (0.67)
...
Кнопка стояла на top 0.75rem (центр 26px), а бейдж — по центру шапки кита
(центр 29.8px): расхождение 3.8px, а на перенёсшемся на вторую строку имени
оно доходило до 16.6px — шапка растёт, бейдж едет за её центром, абсолютная
кнопка нет.
Общая точка отсчёта — центр первой строки имени (29.8px от верха .card-wrap,
от переноса не зависит): булавка top 1rem (центр 28px-кнопки 30px), бейдж
align-self flex-start + 1px (центр 24px-бейджа 30px). В шапку булавку не
положить — кнопка внутри RouterLink невалидна, поэтому под кнопку
подстраивается бейдж.
Замеры 1440/1024/768/480/390/360 — центры совпадают до пикселя в обоих
случаях, горизонтального скролла нет.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
1 day ago
|

Проекты: цвет, сайт, фавикон, закрепление, 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.62)
...
Заметка убрана с карточки списка (целиком живёт на странице проекта): она
делала карточки разной высоты и мешала сравнивать. Вместо неё — актуальность
в шапке справа, ряд метрик (приоритет, «N в работе», «просрочено N» — только
ненулевые), полоса прогресса «Закрыто N из M» и подвал с ближайшим дедлайном
(просроченный красным) и кнопкой «Открыть»; у проекта без задач — подсказка.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
1 day ago
|
Проекты карточками по две в ряд (0.61)
...
Карточка проекта несёт бейджи и заметку — в две колонки список читается
компактнее; на узких экранах одна колонка (860px). Заголовок секции
занимает всю ширину строки, скелетон повторяет ту же сетку.
Co-Authored-By: Claude Code <noreply@anthropic.com>
root
committed
1 day ago
|