1. Коротко
Что строим. Одно веб-приложение с одной базой, в котором есть проекты, у каждого проекта — задачи с ключами вида TAZ-12, канбан-доска с колонками из состояний задач и дерево статей базы знаний с идентификаторами вида TAZ-A-7. Поверх этого — сквозные разделы: все задачи, все доски, вся база знаний, глобальный поиск, уведомления.
Кто пользуется. Команда 3–10 человек плюс временные участники (например, студенты на хакатоне MAX). Один владелец инстанса, в каждом проекте свои роли. Рабочий язык интерфейса — русский. Заметная часть работы идёт с телефона, как и в панели управления сервером.
Что не строим. Учёт времени, CRM, helpdesk, диаграммы Ганта, спринты, JS-воркфлоу, произвольные пользовательские поля, публичные проекты, интеграции с VCS, отчёты. Список того, что урезано относительно YouTrack, — в разделе 3.3.
Ключевые решения.
- Стратегия C из обзора: своё приложение, потому что YouTrack в РФ не предоставляется, а готовые системы либо не влезают в сервер (Huly, OpenProject), либо не дают одной модели «задача + статья + роли» (GitHub Issues + Docmost). Цена решения честно посчитана в разделе 20: около 20–25 рабочих дней на первую версию.
- Стек тот же, что в
nashpartner: FastAPI + SQLAlchemy + Postgres + Redis на бэкенде, React 19 + Vite + TypeScript + Tailwind + Radix + TipTap на фронтенде. Команда и ассистент уже знают этот код, его части (аутентификация, редактор, docker-раскладка, nginx-край) переносятся почти без изменений. - Markdown — каноническое хранение всех текстов (описаний, комментариев, статей). Редактор визуальный, но на диске и в API — markdown. Это делает базу знаний переносимой (импорт
plibvi/kb, экспорт в git) и открытой для ИИ-ассистента как соавтора. - Права двухуровневые: глобальная роль (Владелец или Пользователь) и роль в проекте (Администратор проекта, Участник, Репортёр, Читатель). Право всегда проверяется на сервере, в одном модуле, по одной матрице (раздел 5).
- Регистрация только по приглашению: персональная одноразовая ссылка или многоразовая ссылка команды. Почтового сервера на хосте нет и в v1 не требуется — ссылку копируют и отправляют в мессенджере.
- Деплой: docker compose (
postgres,redis,api,worker) на loopback-портах 34000–34002, статика фронтенда за хостовым nginx, TLS от Let's Encrypt. DNStracker.salam0nn.ruуже указывает на сервер (проверено 9 сентября). Бюджет памяти — не больше 0,5 ГБ.
2. Контекст и закрытые вопросы
Обзор 8 сентября закончился девятью вопросами первого раунда. Запрос от 9 сентября отвечает на большинство из них; остальное закрыто допущениями, которые помечены явно и вынесены в раздел 22.
| # | Вопрос раунда 1 | Ответ | Источник |
|---|---|---|---|
| 1 | Кто пользователи | Команда до 10 человек и временные участники; один владелец, роли в проектах | запрос («для моей команды», «с определёнными ролями»); размер — допущение |
| 2 | Агент как пользователь | Да. Ассистент — отдельный пользователь-бот с ролью Участник, работает через персональный токен API (v1.1) и через импорт/экспорт markdown | допущение из инвентаризации: ассистент уже соавтор plibvi/kb |
| 3 | Границы системы | Задачи + доски + база знаний + роли + приглашения. Без времени, денег, клиентов, helpdesk | запрос; обзор, раздел «CRM или нет» |
| 4 | Что такое «Проект» | Контейнер с ключом, командой, своими состояниями и типами задач, досками и деревом статей. Один продукт или репозиторий = один проект | запрос («доска канбан для каждого проекта») |
| 5 | Единый беклог | Сквозной список «Все задачи» с фильтрами и группировкой по проекту; ранжирование внутри проектов, а не между ними | допущение |
| 6 | Ритм | Канбан без спринтов. Еженедельный разбор беклога — привычка команды, а не функция системы | запрос («доска канбан») |
| 7 | Иерархия | Два уровня: эпик или задача → подзадача. Третий уровень не строим | обзор, «Остальные привычки» |
| 8 | Хостинг и бюджет | Свой сервер, tracker.salam0nn.ru, до 0,5 ГБ памяти, ноль лицензий |
запрос |
| 9 | Где живёт база знаний | В трекере, в Postgres, в markdown. CONTEXT.md и docs/adr остаются в репозиториях как первоисточник для кода; в базу знаний идёт всё, что читают люди: продукт, процессы, договоры, заказчики, дайджесты. plibvi/kb импортируется скриптом |
запрос («База знаний по всем нашим проектам, которую я смогу самостоятельно заполнять») |
Что изменилось относительно рабочей гипотезы обзора: там в фаворитах были стратегии A и B, а C считалась дорогой. Пользователь выбрал C. Спецификация делает C посильной тем, что вырезает всё, что в YouTrack составляет годы работы (воркфлоу-скрипты, произвольные поля, отчёты), и оставляет ядро, которое команда действительно использует.
3. Что взято из YouTrack
Раздел собран по документации YouTrack 2025–2026 и блогу JetBrains; ссылки в разделе 24. Он нужен, чтобы дизайн не изобретался заново, а копировал проверенные решения там, где они уместны.
3.1. Как выглядит YouTrack сегодня
Навигация. С версии 2025.1 верхнее меню заменено на сворачиваемую вертикальную панель слева. На ней всегда доступны кнопки создания задачи, статьи и проекта, разделы Issues, Dashboards, Agile Boards, Reports, Projects, Knowledge Base, а внизу — уведомления и настройки. В свёрнутом виде остаются иконки, подписи всплывают при наведении. Логотип компании крупный в развёрнутом виде и маленький в свёрнутом.
Проектно-центричная навигация (2025.2). Открыв проект, пользователь видит его страницу-обзор и боковую панель с разделами этого проекта: задачи, статьи, доски, настройки. В шапке — кликабельные «хлебные крошки» с выпадающим списком проектов, чтобы переключаться между проектами, не возвращаясь к списку. Настройки проекта администратор правит прямо из боковой панели; «General Info» вынесена в шапку страницы.
Список задач. Слева узкая панель с разделами Drafts, Projects, Tags, Saved Searches, ширина тянется мышью. Поисковая строка в двух режимах: простой (подсказки атрибутов и значений по мере ввода) и расширенный (свободный запрос с подсветкой синтаксиса: атрибуты обычным текстом, значения синим, текст золотым, ошибки красным подчёркиванием). Два макета списка: Table (колонки с сортировкой) и List с тремя масштабами S/M/L (одна строка; плюс значения полей и автор; плюс первые строки описания). Структура Flat или Tree (родитель–потомок). В строке задачи: иконка приоритета, ключ, заголовок, замок ограниченной видимости, метки, счётчики вложений, комментариев и голосов, время обновления. При выделении появляется тулбар: команда, связи, метка, исполнитель, экспорт, удаление. Предпросмотр задачи — в боковой панели или inline (стрелка вправо или F3).
Страница задачи. Шапка: переключатель боковой панели, «Back to issues», хлебные крошки, ключ и заголовок с кнопкой копирования, строка поиска. Под ней информационная полоса: кто создал и когда, кто обновил, «Visible to», голос, звезда. Тулбар: Edit issue, Add tag, Add links, Show more (копировать ключ, печать, вложения, история версий, клонировать, удалить), переключатель ширины текста. Тело: заголовок, описание, метки, панель связей (сгруппированы по типу), панель вложений (миниатюры или список), лента активности с фильтрами Comments, Spent time, Change history, VCS changes и настройкой порядка. Справа панель полей проекта с inline-редактированием и списком досок, где есть эта задача; панель тянется и по настройке профиля переезжает налево. Есть «distraction-free mode», скрывающий всё, кроме описания и активности.
Agile board. Колонки обычно соответствуют полю State; крайняя левая — не начатая работа, крайняя правая — завершённая. У колонки есть Min WIP и Max WIP: при нарушении в заголовке колонки появляется предупреждение. Колонки можно объединять («Merge with»). Дорожки (swimlanes) выключены, либо по значениям поля (state, enum, user, version, date, project, tag), либо по задачам (эпик как дорожка); отдельная дорожка для неразобранных карточек, сверху или снизу. На карточке: оценка, цветовая схема по проекту или по полю, набор полей (полное имя или первая буква). Беклог доски — сохранённый поиск, в режиме дерева или списка. Доступ: «Can view and use the board» и «Can edit board settings» отдельно, с вариантами «issue readers» и «project updaters». Графики: burndown и cumulative flow.
База знаний. Статья привязана к проекту; дерево статей неограниченной вложенности, папок нет — их роль играют родительские статьи; перетаскивание перестраивает дерево вместе с потомками. Слева — оглавление проектов, у которых есть хотя бы одна статья; ширина панели запоминается. Над статьёй хлебные крошки: список проектов → проект → база знаний → родители → идентификатор статьи с копированием. Метаданные: кто создал и когда, кто последним изменил, «Visible to article readers» либо унаследованные ограничения. Справа — оглавление по заголовкам до трёх уровней. Тулбар: Add to favorites (подписка на уведомления), Edit, Add tag, Show more (Add sub-article, Copy article ID, Export as PDF, Print, Show version history, View statistics, Delete). Внизу — список подстатей с автором и датой, затем лента: комментарии, inline-комментарии, история с диффом (добавленное зелёным, удалённое красным). Черновики сохраняются автоматически, у каждой статьи история ревизий; черновики отмечены в дереве и собраны в отдельном меню.
Роли и права. Роли по умолчанию: System Admin, Project Admin, Contributor (до 2023.1 — Developer), Reporter, Observer. Роль назначается пользователю или группе на проект; члены команды проекта наследуют роль команды. Контрибьютор умеет читать проект, создавать, читать, обновлять и удалять задачи, вложения, комментарии, связи, статьи; Project Admin дополнительно «Update Project»; System Admin — «Delete Project» и «Override Visibility Restrictions». Reporter создаёт задачи и комментарии и следит за ними; Observer видит профили и правит свой. Проект приватный, если роли не выданы группе «All Users» и гостю. У статьи пять прав: Read, Create, Update, Delete Article и Read Article Comment; каждое подразумевает Read Article. Видимость статьи можно сузить до пользователей, групп и команд; подстатьи наследуют ограничения родителя и не могут стать шире; автор всегда видит свою статью; менять видимость может тот, у кого Update Article.
Регистрация. Три пути: администратор создаёт пользователя вручную, приглашает по почте (аккаунт создаётся сразу, письмо содержит ссылку на страницу, где человек задаёт пароль) или включает самостоятельную регистрацию в Hub с reCAPTCHA. Роль и проекты назначаются администратором после.
Поля по умолчанию. Priority: Show-stopper, Critical, Major, Normal, Minor. Type: Bug, Cosmetics, Exception, Feature, Task, Usability Problem, Performance Problem, Epic. State нерешённые: Submitted, Open, In Progress, To be discussed, Reopened; решённые: Can't Reproduce, Duplicate, Fixed, Won't fix, Incomplete, Obsolete, Verified. Kanban-шаблон: Stage — Backlog, Develop, Review, Test, Staging, Done; Kanban State — Blocked, Ready to pull.
3.2. Что переносим как есть
| В YouTrack | В нашей v1 | Зачем |
|---|---|---|
| Вертикальная сворачиваемая панель с кнопками создания | Так же (раздел 14.1) | Проверенная навигация, экономит место на телефоне |
| Проектно-центричная навигация, хлебные крошки с выпадающим списком проектов | Так же | Команда живёт в 4–5 проектах, переключение должно быть в один клик |
Ключ задачи PRJ-123, ключ статьи PRJ-A-12 |
Так же | Главный интеграционный контракт: коммиты, чаты, статьи ссылаются по ключу |
| Панель полей справа от описания, inline-редактирование | Так же, поля фиксированы | Скорость правки без формы |
| Лента активности с фильтрами | Комментарии и история изменений (без времени и VCS) | Ответ на «кто и когда это поменял» |
| Колонки доски = состояния, Min/Max WIP, объединение состояний в колонку | Так же | Лимит незавершённой работы — главная практика канбана |
| Дорожки по исполнителю, приоритету, типу, эпику | Так же, четыре варианта | Достаточно для команды до 10 человек |
| Дерево статей без папок, перетаскивание с потомками | Так же | Простая модель, понятная людям вне разработки |
| Черновики с автосохранением и история ревизий с диффом | Так же | База, которую заполняют самостоятельно, обязана прощать ошибки |
| Избранное со подпиской на уведомления | Так же | Единый механизм «слежу за этим» |
| Роли на проект: администратор, участник, репортёр, читатель | Так же (раздел 5) | Ровно тот набор, что просил пользователь |
| Ограничение видимости статьи с наследованием на подстатьи | Упрощено: флаг «только администраторы проекта», наследуется вниз | Договоры и финансы в базе есть, а групп пользователей — нет |
| Приглашение ссылкой, задаёт пароль сам приглашённый | Так же, плюс роль и проекты зашиты в приглашение | Без почтового сервера письмо заменяет мессенджер |
| Два режима поиска: подсказки и запрос | v1 — фильтры-чипы с подсказками; v1.1 — текстовый запрос с той же грамматикой | Запрос дорог в реализации, чипы закрывают 90 % случаев |
| Markdown в описаниях и статьях с визуальным редактором | Так же | Переносимость и работа ассистента |
| Distraction-free режим страницы | Кнопка «Широкий текст», скрывает панель полей | Дёшево, полезно на ноутбуке |
3.3. Что урезано
| Возможность YouTrack | Решение | Причина |
|---|---|---|
| Произвольные пользовательские поля (custom fields) | v2. В v1 фиксированный набор: тип, состояние, приоритет, исполнитель, срок, оценка, метки, родитель | Главный источник сложности в трекерах; команда обходится фиксированным набором |
| Workflows на JavaScript, state-machine по типам | Нет. Список состояний настраивается, переходы разрешены любые | Автоматизация — через API и ассистента, не через встроенный скриптовый движок |
| Учёт времени, оценки в burndown | Нет. Поле «оценка, ч» хранится, но не суммируется | Граница с PSA проходит здесь (обзор) |
| Спринты, burndown, cumulative flow | Нет | Команда работает по канбану |
| Отчёты и дашборды | Только домашняя страница «Моё» (раздел 14.3) | Отчёты YouTrack — отдельный продукт |
| Диаграмма Ганта, Helpdesk, AI Assistant | Нет | Не в границах |
| Группы пользователей, команды как субъект прав | Нет. Роль выдаётся человеку | До 10 человек группы не нужны; появятся в v2 вместе с видимостью по группам |
| Публичные проекты, гость, голосование | Нет. Все проекты приватные, вход только по сессии | Инстанс внутренний |
| Интеграции с VCS, mailbox, Slack | v1.1: Telegram-бот и токены API; VCS через упоминание ключа в коммитах — по договорённости, без интеграции | Код команды на GitHub под личным аккаунтом, интеграция дороже пользы |
| Сохранённые поиски, теги как сохранённые фильтры | v1.1 | Фильтры в URL закрывают потребность, ссылку можно сохранить в браузере |
| Экспорт в PDF, статистика статьи | Нет; экспорт статьи в markdown — да | Markdown важнее PDF для этой команды |
| Дубликаты и связи всех типов | Три типа связей: «связана», «блокирует / заблокирована», «дублирует» | Достаточно по обзору |
| Несколько значений в полях (multi-enum, versions, builds) | Нет | Нет таких полей |
| Клонирование задач и статей, шаблоны задач | Шаблоны статей — да (раздел 11.6); клонирование — v1.1 | Дёшево добавить позже |
4. Практики, положенные в основу
Каждая практика ниже влияет на конкретные требования, ссылки на них даны в скобках.
- Лимит незавершённой работы. WIP-лимиты — самая сильная практика канбана: колонка с лимитом не принимает новую карточку, пока не уйдёт старая; результат — короче цикл, меньше переключений. Для малой команды стартовое правило — около одной активной карточки на человека в колонке «В работе», лимиты пересматриваются регулярно. Отсюда Min/Max WIP на колонке, подсветка нарушения и рекомендация по умолчанию
max = число участников проекта(BRD-07). - Три–пять колонок. Начинать с «Беклог → В работе → Готово», добавлять колонку только под реальную очередь (ревью, ожидание внешних). Отсюда состояния по умолчанию (WF-02) и запрет создавать доску без хотя бы одной колонки.
- Явные политики. У каждой колонки должно быть определение «что значит, что задача здесь». Отсюда поле «политика колонки» — короткий текст, показываемый по наведению на заголовок (BRD-06).
- DEEP-беклог и задачи вместо user stories. Верх беклога расписан, низ обозначен; задача содержит конкретный результат и критерий готовности. Отсюда карточка задачи с блоком «Критерий готово» в шаблоне описания (ISS-05) и ручное ранжирование в колонке (BRD-10).
- Триаж входящего. Всё внешнее попадает не в беклог, а в состояние «Входящие» (по умолчанию выключено, включается в настройках проекта) с четырьмя действиями: принять, отклонить, дубликат, отложить (WF-05).
- Ответственный и дата пересмотра у статьи. Страница без владельца никогда не обновляется; устаревшая страница вредит больше, чем её отсутствие. Отсюда обязательный ответственный, дата пересмотра и пометка «устарело» с напоминанием (KB-12, KB-13).
- Одна точка входа, прослеживаемость, глоссарий, типы документов (Diátaxis). Отсюда корневая статья-индекс в каждом проекте, шаблоны «Инструкция», «Справочник», «Объяснение», «Решение (ADR)», «Встреча» и блок «Источники» в шаблонах (KB-15).
- Двухуровневые роли и минимальные привилегии. Роль на уровне инстанса и роль на уровне проекта независимы; набор ролей минимален (4–6); проверка идёт от самой конкретной области к общей; матрица «роль × ресурс × действие» — основа и для кода, и для аудита (RBAC-01…RBAC-09).
- Приглашение как транзакция. Токен 256 бит из криптографического генератора, в базе — только хеш, срок 7 дней, одноразовость, роль зашита в приглашение (приглашённый не выбирает её сам), нельзя выдать роль выше своей, лимит на частоту, чёткое сообщение об истёкшей ссылке вместо общей ошибки. Все пункты — требования INV-01…INV-14.
- Postgres достаточно. Полнотекстовый поиск с GIN-индексом и pg_trgm для опечаток закрывает поиск для десятков тысяч документов без Elasticsearch (SRCH-01…SRCH-06).
- SSE вместо WebSocket. Для одностороннего потока «сервер → доска» хватает Server-Sent Events через Redis pub/sub: меньше протокольного шума, проще прокси и тесты (RT-01…RT-05).
5. Роли и права
5.1. Модель
Права проверяются в двух областях, и они независимы друг от друга.
- Глобальная роль есть у каждого пользователя, ровно одна: Владелец или Пользователь. Владелец видит и делает всё; Пользователь сам по себе не видит ни одного проекта.
- Роль в проекте есть у пользователя в каждом проекте, куда его добавили, ровно одна: Администратор проекта, Участник, Репортёр или Читатель. Один человек может быть администратором в одном проекте и читателем в другом.
- Ограничение на объекте сужает круг ещё уже: статья с флагом «только администраторы» невидима Участникам, Репортёрам и Читателям проекта, но видна автору.
tracker/authz.py: сессия, глобальная роль, членство, матрица, ограничение объекта. Отсутствие членства маскируется под несуществующий адрес.RBAC-01 Каждая проверка права ДОЛЖНА выполняться на сервере в одном модуле (tracker/authz.py) функцией can(user, action, obj) -> bool и её вариантом require(...), поднимающим исключение. Роутеры НЕ ДОЛЖНЫ содержать собственных проверок ролей.
RBAC-02 Порядок вычисления: сессия → глобальная роль → членство в проекте объекта → матрица → ограничение объекта. Владелец проходит матрицу целиком, включая права, отсутствующие у Администратора проекта.
RBAC-03 Отсутствие членства в проекте ДОЛЖНО возвращать 404, а не 403, чтобы не раскрывать существование проектов и ключей задач. Отказ внутри проекта, где пользователь состоит, возвращает 403.
RBAC-04 Права *.own относятся к объектам, у которых author_id равен текущему пользователю; для задач «своя» — созданная пользователем, для комментариев и статей — то же.
RBAC-05 Фронтенд ДОЛЖЕН получать в ответе /api/v1/me список проектов с ролью в каждом и скрывать недоступные действия, но это только удобство: единственный источник истины — сервер.
RBAC-06 Матрица 5.3 ДОЛЖНА храниться в коде как данные (словарь ROLE_PERMISSIONS), и по ней ДОЛЖЕН существовать табличный тест, перебирающий все пары «роль × действие» (раздел 20.1).
RBAC-07 Нельзя удалить или понизить последнего Владельца; нельзя понизить последнего Администратора проекта, если в проекте нет Владельца. Попытка возвращает 409 last_admin.
RBAC-08 Пользователь МОЖЕТ покинуть проект сам («Покинуть проект»), кроме случая RBAC-07.
RBAC-09 Деактивированный пользователь (is_active = false) не может войти, его сессии отзываются немедленно, но авторство, комментарии и история сохраняются с его именем.
5.2. Роли словами
Владелец. Хозяин инстанса. Создаёт проекты, управляет всеми пользователями и приглашениями, видит все проекты, статьи и задачи, включая ограниченные; правит глобальные настройки; читает журнал аудита. Владельцев может быть несколько.
Администратор проекта. Отвечает за один проект. Настраивает состояния, типы, метки, доски, участников и их роли (до Администратора включительно), выпускает приглашения в свой проект, переносит и удаляет любые задачи и статьи проекта, ставит и снимает ограничение «только администраторы». Не видит другие проекты, пока его туда не добавят.
Участник. Основная рабочая роль. Создаёт и правит любые задачи проекта, меняет состояния и двигает карточки, комментирует, прикрепляет файлы, создаёт связи; создаёт и правит статьи, переставляет их в дереве; удаляет только своё. Не меняет настройки проекта и доски.
Репортёр. Заказчик или тестировщик. Видит задачи и статьи, создаёт задачи, правит свои, комментирует, прикрепляет файлы к своим задачам и комментариям. Не двигает чужие карточки и не пишет статьи.
Читатель. Только чтение задач, досок и статей. Может добавлять в избранное и получать уведомления.
5.3. Матрица прав
Обозначения: ✓ — разрешено; свои — только для объектов, где пользователь автор; — запрещено. Владелец имеет все права во всех проектах и не показан отдельной колонкой.
| Действие (код) | Администратор проекта | Участник | Репортёр | Читатель |
|---|---|---|---|---|
| Проект | ||||
project.read — видеть проект, его задачи, доски, статьи |
да | да | да | да |
project.update — название, описание, ключ¹, иконка |
да | нет | нет | нет |
project.settings — состояния, типы, метки, «Входящие» |
да | нет | нет | нет |
project.members — добавлять, менять роль, удалять участников |
да | нет | нет | нет |
project.invite — приглашать в проект (роль ≤ своей) |
да | нет | нет | нет |
project.archive — архивировать и разархивировать |
да | нет | нет | нет |
project.delete — удалить проект со всем содержимым |
нет только Владелец | нет | нет | нет |
| Задачи | ||||
issue.read |
да | да | да | да |
issue.create |
да | да | да | нет |
issue.update — все поля любой задачи |
да | да | свои | нет |
issue.state — менять состояние, двигать по доске |
да | да | свои | нет |
issue.assign — назначать исполнителя |
да | да | свои | нет |
issue.link — связи, родитель, подзадачи |
да | да | свои | нет |
issue.move — перенос в другой проект² |
да | нет | нет | нет |
issue.delete |
да | свои | свои | нет |
issue.watch — следить, избранное |
да | да | да | да |
| Комментарии к задачам | ||||
comment.create |
да | да | да | нет |
comment.update |
да | свои | свои | нет |
comment.delete |
да | свои | свои | нет |
| Вложения | ||||
attachment.add — к задаче, комментарию, статье |
да | да | к своим | нет |
attachment.delete |
да | свои | свои | нет |
| Доски | ||||
board.view |
да | да | да | да |
board.use — перетаскивать карточки, менять ранг |
да | да | свои задачи | нет |
board.create |
да | да | нет | нет |
board.manage — колонки, WIP, дорожки, удаление |
да | свои доски | нет | нет |
| База знаний | ||||
article.read |
да | да | да | да |
article.read_restricted — статьи «только администраторы» |
да | автор | автор | нет |
article.create |
да | да | нет | нет |
article.update — текст, заголовок, метки, ответственный |
да | да | нет | нет |
article.move — перенос в дереве, смена родителя |
да | да | нет | нет |
article.restrict — ставить и снимать «только администраторы» |
да | нет | нет | нет |
article.delete |
да | свои | нет | нет |
article.comment.create |
да | да | да | нет |
article.comment.update / delete |
да | свои | свои | нет |
article.history.restore — откат к ревизии |
да | да | нет | нет |
article.favorite |
да | да | да | да |
| Приглашения и участники | ||||
invite.create_global — без привязки к проекту, роль Владелец |
нет только Владелец | нет | нет | нет |
invite.revoke — отзывать приглашения |
своего проекта | нет | нет | нет |
invite.list — видеть приглашения проекта |
да | нет | нет | нет |
| Глобально (только Владелец) | ||||
users.manage — список, деактивация, смена глобальной роли, сброс пароля |
нет | нет | нет | нет |
projects.create |
нет | нет | нет | нет |
settings.manage — название инстанса, логотип, лимиты |
нет | нет | нет | нет |
audit.read — журнал аудита |
нет | нет | нет | нет |
¹ Смена ключа проекта меняет ключи всех задач и статей; старые ключи ДОЛЖНЫ продолжать открываться редиректом (таблица project_key_aliases).
² Перенос задачи в другой проект выдаёт ей новый номер в целевом проекте; старый ключ остаётся алиасом.
5.4. Правила, которые не выражаются матрицей
RBAC-10 Администратор проекта не может выдать роль выше своей: максимум «Администратор проекта». Роль Владельца выдаёт только Владелец через глобальное приглашение или страницу пользователей.
RBAC-11 Автор статьи с ограничением «только администраторы» продолжает видеть и править её, даже если он Участник (правило YouTrack «articles are always visible to their authors»). Право снять ограничение остаётся у Администратора.
RBAC-12 Подстатьи наследуют ограничение родителя и не могут быть открыты шире родителя; при переносе открытой статьи под ограниченного родителя она становится ограниченной, и об этом предупреждают до переноса.
RBAC-13 Ключ проекта участвует в URL, поэтому пользователь без членства получает 404 на любой адрес /p/{key}/... — ровно тот же ответ, что на несуществующий ключ (RBAC-03).
RBAC-14 Владелец, действуя в проекте, где он не участник, отображается в истории как «Владелец», и его действия попадают в журнал аудита с пометкой via_owner.
RBAC-15 Служебный пользователь-бот (ассистент) — обычный пользователь с глобальной ролью Пользователь и ролью Участник в нужных проектах; отдельного типа аккаунта не вводится. Его отличает только флаг is_bot, который скрывает его из подсказок исполнителя по умолчанию.
6. Регистрация по ссылке-приглашению
6.1. Сценарии
- Персональное приглашение. Администратор проекта или Владелец создаёт приглашение: имя (необязательно), почта (необязательно, только как подсказка), роль, список проектов с ролью в каждом. Получает ссылку
https://tracker.salam0nn.ru/invite/<токен>, копирует и отправляет в Telegram или MAX. Ссылка одноразовая, живёт 7 дней. - Ссылка команды. Для группы (например, четырёх студентов на хакатон): одна ссылка на N использований до даты, с ролью по умолчанию Репортёр или Читатель. Владелец может позже поднять роли вручную.
- Первый Владелец. Создаётся из консоли:
docker compose exec api tracker create-owner --email … --name …. Веб-регистрации без приглашения нет вовсе. - Уже вошедший пользователь открыл приглашение. Ему не создают второй аккаунт: показывают «Принять приглашение в проекты X и Y как текущий пользователь», и после подтверждения добавляют членства. Роль, уже более высокая, чем в приглашении, не понижается.
6.2. Требования
INV-01 Токен приглашения ДОЛЖЕН генерироваться secrets.token_urlsafe(32) (256 бит). В базе хранится только sha256(token); сырой токен показывается один раз при создании и при «пересоздать ссылку».
INV-02 Приглашение ДОЛЖНО хранить: кто пригласил, когда, срок действия (по умолчанию 7 дней, максимум 30), глобальную роль (user или owner), список {project_id, role}, тип (personal или team), max_uses (для персонального — 1), uses_count, статус (pending, accepted, revoked, expired), необязательные подсказки name_hint, email_hint, комментарий для себя.
INV-03 Роль в приглашении НЕ ДОЛЖНА превышать роль приглашающего в соответствующем проекте; глобальную роль owner может выдать только Владелец. Нарушение — 403 role_escalation.
INV-04 Приглашение с owner НЕ МОЖЕТ быть командным (max_uses > 1).
INV-05 Страница /invite/<токен> ДОЛЖНА без входа показать: название инстанса, кто приглашает, в какие проекты и с какой ролью; форму «Имя, почта, пароль, повтор пароля»; кнопку «Создать аккаунт». Для истёкшего, отозванного или исчерпанного приглашения — понятное сообщение («Ссылка истекла 12.09.2026. Попросите пригласившего создать новую») без раскрытия прочих деталей.
INV-06 Принятие ДОЛЖНО быть одной транзакцией: проверка токена и статуса FOR UPDATE, создание пользователя, создание членств, uses_count += 1, смена статуса на accepted при достижении max_uses, запись в аудит, создание сессии. Повторный запрос с тем же токеном после этого — 410 invite_used.
INV-07 Почта — уникальный логин (без учёта регистра). Если почта уже занята, страница предлагает войти и принять приглашение существующим аккаунтом (INV-11); второй аккаунт НЕ создаётся.
INV-08 Подтверждение почты в v1 не требуется (почтового сервера нет). Поле email_verified хранится и остаётся false; при появлении SMTP письмо с подтверждением включается настройкой.
INV-09 Лимиты: не более 20 приглашений в час на одного приглашающего; не более 10 попыток открыть /invite/* в минуту с одного IP; не более 5 попыток принять в минуту с одного IP. Превышение — 429 с Retry-After.
INV-10 Сравнение хеша токена ДОЛЖНО быть выборкой по индексу invitations(token_hash), а не перебором; ответ на несуществующий и на истёкший токен занимает одинаковое время с точностью до запроса к базе.
INV-11 Если приглашение открыто в браузере с активной сессией, показывается «Принять как <имя>» и «Это не я, выйти». Принятие добавляет членства к текущему аккаунту и не меняет уже более высокие роли.
INV-12 Приглашающий видит список своих приглашений с состоянием, сроком, счётчиком использований и действиями «Скопировать ссылку», «Пересоздать ссылку», «Отозвать». Владелец видит все приглашения инстанса.
INV-13 Истечение отмечается фоновой задачей раз в час (expire_invitations), но проверка при открытии ссылки сверяет expires_at сама, не полагаясь на задачу.
INV-14 О принятии приглашения приглашающий получает уведомление «Иван Петров принял приглашение в проект TAZ».
INV-15 Пароль: минимум 10 символов, проверка по списку из 10 000 самых частых паролей, без требований к классам символов; хеш argon2id с параметрами по умолчанию argon2-cffi (time_cost 3, memory 64 МБ, parallelism 4).
6.3. Вход, сессии, профиль
AUTH-01 Вход по почте и паролю на /login. Ответ на неверную пару — одинаковый текст «Неверная почта или пароль», без указания, что именно не так. После 10 неудач за 15 минут с одного IP или на одну почту — задержка 30 с и 429.
AUTH-02 Сессия хранится на сервере (Redis, ключ sess:<id>, id = token_urlsafe(32)), в браузере — cookie tracker_session: HttpOnly, Secure, SameSite=Lax, Path=/, срок 30 дней скользящий (продлевается при активности не чаще раза в час). Одновременных сессий у пользователя может быть несколько; в профиле есть список сессий с «Завершить» и «Завершить все, кроме этой».
AUTH-03 Защита от CSRF: SameSite=Lax плюс обязательный заголовок X-Requested-With: tracker на всех изменяющих запросах (POST, PATCH, PUT, DELETE); запрос без него — 403 csrf. Дополнительно сверяется Origin с PUBLIC_URL.
AUTH-04 Выход — POST /api/v1/auth/logout удаляет сессию из Redis и cookie.
AUTH-05 Смена пароля требует текущий пароль и завершает все остальные сессии. Владелец может сбросить пароль пользователю: генерируется одноразовая ссылка /reset/<токен> на 24 часа, показывается Владельцу для передачи (тот же механизм, что у приглашений).
AUTH-06 Профиль: имя, почта, аватар (загрузка, ≤ 2 МБ, ужимается до 256×256), часовой пояс (по умолчанию Europe/Moscow), тема (системная, светлая, тёмная), настройки уведомлений.
AUTH-07 Персональные токены API (v1.1): tracker_pat_…, хранятся хешем, показываются один раз, срок и область (проекты) задаются при создании; передаются заголовком Authorization: Bearer; на них не действует CSRF-проверка AUTH-03; действия попадают в аудит с пометкой via_token.
AUTH-08 Все ответы API ДОЛЖНЫ нести Cache-Control: no-store; nginx добавляет X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, HSTS.
7. Доменная модель
Одна база, один инстанс (без мультитенантности). Все сущности принадлежат проекту, кроме пользователей, сессий, приглашений и глобальных настроек.
7.1. Сущности и поля
Типы указаны по Postgres. id везде bigint generated always as identity, если не сказано иное. У всех таблиц created_at, updated_at (timestamptz, UTC).
Пользователь users
| Поле | Тип | Правила |
|---|---|---|
email |
citext, unique | логин; регистр не учитывается |
name |
text | 1–80 символов, показывается везде |
password_hash |
text | argon2id |
global_role |
enum owner, user |
|
is_active |
bool | деактивация вместо удаления |
is_bot |
bool | скрывает из подсказок исполнителя |
avatar_path |
text null | относительный путь в хранилище |
timezone |
text | IANA, по умолчанию Europe/Moscow |
theme |
enum system, light, dark |
|
notify_prefs |
jsonb | раздел 13.2 |
last_seen_at |
timestamptz | обновляется не чаще раза в 5 минут |
Приглашение invitations — раздел 6.2: token_hash (unique), kind, status, global_role, project_roles jsonb [{project_id, role}], max_uses, uses_count, expires_at, invited_by_id, name_hint, email_hint, note, accepted_user_ids bigint[].
Проект projects
| Поле | Тип | Правила |
|---|---|---|
key |
text unique | 2–8 символов [A-Z][A-Z0-9]*; в URL и ключах задач |
name |
text | 1–80 |
description_md |
text | markdown |
icon |
text | эмодзи или буква; цвет из color |
color |
text | hex, для аватара проекта и карточек |
issue_counter |
int | последний выданный номер задачи |
article_counter |
int | последний выданный номер статьи |
inbox_enabled |
bool | включает состояние «Входящие» (WF-05) |
is_archived |
bool | архивный проект только для чтения, скрыт из списков по умолчанию |
default_board_id |
bigint null | доска, открывающаяся по вкладке «Доска» |
Членство project_members: project_id, user_id, role enum admin, member, reporter, viewer, added_by_id; unique (project_id, user_id).
Состояние issue_states: project_id, name (уникально в проекте), position int, category enum unstarted, started, done, cancelled, color, is_default (ровно одно в проекте). category определяет «решённость»: done и cancelled — решённые.
Тип задачи issue_types: project_id, name, position, icon, color, is_default, is_epic bool (тип, который может быть родителем).
Метка tags: project_id, name (уникально в проекте без учёта регистра), color.
Задача issues
| Поле | Тип | Правила |
|---|---|---|
project_id |
fk | |
number |
int | уникально в проекте; ключ = project.key || '-' || number |
title |
text | 1–200 |
description_md |
text | markdown, до 100 КБ |
type_id |
fk | |
state_id |
fk | |
priority |
enum critical, high, normal, low |
по умолчанию normal |
assignee_id |
fk null | |
author_id |
fk | |
parent_id |
fk null | один уровень: у подзадачи не может быть своих подзадач (ISS-08) |
due_date |
date null | |
estimate_hours |
numeric(6,2) null | хранится, не суммируется |
resolved_at |
timestamptz null | ставится при переходе в done/cancelled, снимается при обратном |
search_vector |
tsvector generated | раздел 12 |
comments_count, attachments_count |
int | денормализованы триггером |
Индексы: unique (project_id, number); (project_id, state_id); (assignee_id) where resolved_at is null; (parent_id); GIN search_vector; GIN title gin_trgm_ops.
Метки задачи issue_tags: (issue_id, tag_id).
Связь issue_links: source_id, target_id, kind enum relates, blocks, duplicates; unique (source_id, target_id, kind); обратная сторона вычисляется (blocks → «заблокирована»). Связь между проектами разрешена, если у пользователя есть issue.read на обе стороны.
Наблюдатель issue_watchers: (issue_id, user_id); автор и исполнитель — наблюдатели по умолчанию.
Комментарий comments: issue_id, author_id, body_md, edited_at. Аналогично article_comments с article_id.
Вложение attachments: id uuid, project_id, владелец — ровно одно из issue_id, comment_id, article_id, article_comment_id; filename, content_type, size, storage_path (<project_key>/<uuid><ext>), sha256, uploaded_by_id, width, height для картинок.
Доска boards: project_id, name, owner_id, is_default, swimlane enum none, assignee, priority, type, parent, card_fields jsonb (что показывать на карточке), done_days int (сколько дней держать решённые на доске, по умолчанию 14), query jsonb (фильтр карточек, тот же формат, что у списка).
Колонка board_columns: board_id, name, position, state_ids bigint[] (одно или несколько состояний; каждое состояние не более чем в одной колонке доски), wip_min int null, wip_max int null, policy_md text (короткое «что значит, что задача здесь»), collapsed_default bool.
Позиция на доске board_positions: board_id, issue_id, rank float8; unique (board_id, issue_id). Ранг задаётся при первом попадании на доску и при ручном перетаскивании (BRD-10).
Статья articles
| Поле | Тип | Правила |
|---|---|---|
project_id |
fk | |
number |
int | ключ TAZ-A-7 |
parent_id |
fk null | дерево без ограничения глубины |
position |
float8 | порядок среди соседей |
title |
text | 1–200 |
slug |
text | транслит заголовка, для красивых ссылок /p/TAZ/kb/7-kak-deployit |
content_md |
text | до 1 МБ; каноническое содержимое |
draft_md |
text null | несохранённые изменения (автосохранение) |
draft_updated_at, draft_by_id |
кто и когда правит черновик | |
is_published |
bool | false — статья-черновик, видна автору и администраторам |
restricted |
bool | «только администраторы»; наследуется вниз (RBAC-12) |
owner_id |
fk | ответственный, обязательный |
author_id, last_editor_id |
fk | |
review_at |
date null | дата пересмотра (KB-12) |
template |
text null | из какого шаблона создана |
search_vector |
tsvector generated | |
version |
int | оптимистическая блокировка (KB-09) |
Индексы: unique (project_id, number); (parent_id, position); GIN search_vector; GIN title gin_trgm_ops.
Ревизия article_revisions: article_id, version, title, content_md, author_id, created_at, summary (необязательная подпись «что изменил»). Хранится каждая публикация; диффы считаются на лету.
Метки статьи article_tags, избранное favorites (user_id, kind enum issue, article, board, project, object_id), ссылки между объектами references (from_kind, from_id, to_kind, to_id; пересчитываются при сохранении из упоминаний ключей и вики-ссылок; дают обратные ссылки KB-11).
Уведомление notifications: user_id, kind, actor_id, object_kind, object_id, payload jsonb, read_at, created_at. Раздел 13.
Событие истории activity: project_id, object_kind, object_id, actor_id, kind (created, field_changed, comment_added, moved, …), field, old_value, new_value (jsonb), created_at. Это и лента активности задачи, и материал для аудита.
Аудит audit_log: только действия безопасности и администрирования: вход, выход, неудачный вход, создание и принятие приглашения, смена роли, деактивация, смена пароля, удаление проекта, действия Владельца в чужих проектах, доступ по токену. Поля: actor_id, action, target, ip, user_agent, meta.
Алиасы ключей project_key_aliases: old_key, project_id; для редиректов после смены ключа (RBAC-13 и сноска ¹ к матрице).
Настройки инстанса settings: одна строка: instance_name, logo_path, invite_ttl_days, max_upload_mb, allowed_upload_types, smtp jsonb null, telegram jsonb null.
7.2. Ключи и нумерация
DM-01 Номер задачи выдаётся в той же транзакции, что и вставка: update projects set issue_counter = issue_counter + 1 where id = :p returning issue_counter. Строка проекта блокируется на время транзакции, дыр в нумерации при откате не бывает, гонок — тоже. Аналогично для статей.
DM-02 Ключ задачи KEY-N и ключ статьи KEY-A-N уникальны навсегда: при переносе или смене ключа проекта старые ключи остаются алиасами и открываются редиректом 301.
DM-03 Ключи распознаются в любом markdown-тексте регулярным выражением \b([A-Z][A-Z0-9]{1,7})-(A-)?(\d+)\b и превращаются в ссылки при рендере; для несуществующих или недоступных ключей ссылка не создаётся.
DM-04 Удаление задачи, статьи, проекта — мягкое: deleted_at, объекты пропадают из выдачи, физически чистятся фоновой задачей через 30 дней. Владелец видит «Корзину» проекта и может восстановить.
8. Рабочий процесс задач
WF-01 Состояния настраиваются в каждом проекте отдельно: название, цвет, категория, порядок. Категорий четыре и они неизменны: не начато, в работе, готово, отменено. Категория даёт смысл, который понимают доски, фильтры и статистика: «нерешённые» — это не начато + в работе.
WF-02 Состояния по умолчанию в новом проекте: Беклог (не начато, по умолчанию), В работе (в работе), На проверке (в работе), Готово (готово), Отклонена (отменено). Их можно переименовать и дополнить; удалить состояние можно только указав, куда перевести его задачи.
WF-03 Переходы между состояниями разрешены любые. Никаких правил «нельзя из Беклога сразу в Готово» в v1 нет — в команде до 10 человек такие правила дороже пользы. Каждый переход пишется в activity с автором и временем.
WF-04 Переход в категорию готово или отменено ставит resolved_at; обратный переход снимает. Закрытие родителя с открытыми подзадачами показывает предупреждение и предлагает закрыть их разом; автоматически ничего не закрывается.
WF-05 «Входящие» — необязательное состояние категории не начато (inbox_enabled). Задачи, созданные Репортёрами и через API-токены, попадают в него; на его карточках и в списке видны четыре кнопки триажа: Принять (→ состояние по умолчанию), Отклонить (→ Отклонена, обязателен комментарий), Дубликат (→ Отклонена + связь duplicates), Отложить до даты (остаётся во Входящих, due_date = дата, скрывается из списка до неё).
WF-06 Типы по умолчанию: Задача (по умолчанию), Ошибка, Улучшение, Эпик (is_epic). Родителем может быть любая задача типа с is_epic, а также любая задача для своих подзадач; глубина — один уровень (ISS-08).
WF-07 Приоритеты глобальные и фиксированные: Критический, Высокий, Обычный, Низкий — с цветами красный, оранжевый, серый, синий. На карточке и в списке приоритет виден полосой слева.
WF-08 Смена состояния возможна четырьмя способами, все они — одна и та же операция PATCH /issues/{key} {state_id}: из панели полей, перетаскиванием на доске, из списка по клавише, кнопкой триажа.
9. Задачи
9.1. Создание
ISS-01 «Создать задачу» доступно с любой страницы: кнопка на боковой панели и клавиша c. Открывается диалог, не уводящий со страницы: проект (запомнен последний), заголовок, описание (редактор), тип, приоритет, исполнитель, срок, метки, родитель. Обязателен только заголовок; проект подставляется из контекста (внутри проекта — этот проект).
ISS-02 Быстрое создание в колонке доски и в списке: поле «+ Задача» принимает заголовок, Enter создаёт задачу в состоянии колонки (или по умолчанию) и оставляет поле открытым для следующей.
ISS-03 Диалог сохраняет незавершённый ввод в localStorage (черновик) и предлагает восстановить его при следующем открытии.
ISS-04 Вставка картинки из буфера обмена в описание или комментарий загружает её как вложение и вставляет .
ISS-05 Шаблон описания по умолчанию для типов Задача и Улучшение — три заголовка: «Зачем», «Что сделать», «Критерий готово»; для Ошибки — «Шаги», «Ожидалось», «Получилось», «Окружение». Шаблон правится в настройках проекта. Карточка эпика повторяет схему беклога auto-parser: «Зачем пользователю, Что уже есть, Что в плане, Критерий готово, Риски».
9.2. Страница задачи
Адрес /p/{KEY}/issues/{KEY}-{N}; короткий адрес /{KEY}-{N} редиректит на полный.
┌──────────────────────────────────────────────────────────────────────────┐ │ < Задачи Проекты > tazzz.ru v > Задачи > TAZ-128 / поиск │ ├──────────────────────────────────────────────────────────────────────────┤ │ Создал Салам · 04.09.2026 14:02 · Изменил Иван · вчера * Следить (3) │ │ │ │ TAZ-128 Фото из Dongchedi не попадают в карусель [Изменить] [:] │ │ ┌────────────────────────────────────────────┐ ┌──────────────────────┐ │ │ │ ## Зачем │ │ Проект tazzz.ru │ │ │ │ Карусель пустая у 12 % объявлений… │ │ Тип Ошибка │ │ │ │ ## Что сделать │ │ Состояние В работе v │ │ │ │ - [x] найти, где теряется ключ S3 │ │ Приоритет Высокий │ │ │ │ - [ ] добавить тест на пустую карусель │ │ Исполн. Иван v │ │ │ │ ## Критерий готово │ │ Срок 12.09.2026 │ │ │ │ Ни одного пустого блока на 500 карточках │ │ Оценка 4 ч │ │ │ ├────────────────────────────────────────────┤ │ Метки фото s3 │ │ │ │ Метки: фото · s3 Родитель: TAZ-90│ │ Родитель TAZ-90 │ │ │ │ Связи: блокирует TAZ-131 · связана TAZ-77 │ ├──────────────────────┤ │ │ │ Подзадачи (1/2): x TAZ-129 _ TAZ-130 │ │ На досках: Канбан │ │ │ │ Вложения (2): shakal.png · log.txt │ └──────────────────────┘ │ │ ├────────────────────────────────────────────┤ │ │ │ Активность [Комментарии] [История] ^ │ │ │ │ Иван · вчера 18:40 │ │ │ │ Ключ теряется в tiered store, см. ADR-32 │ │ │ │ Салам · вчера 18:55 Состояние: Беклог → │ │ │ │ В работе │ │ │ │ ┌────────────────────────────────────────┐ │ │ │ │ │ Написать комментарий… @упомянуть + │ │ │ │ │ └────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────┘
ISS-06 Шапка: кнопка «‹ Задачи» (назад к последнему списку с сохранёнными фильтрами), хлебные крошки с выпадающим списком проектов, ключ с копированием по клику, звезда «Следить» со счётчиком наблюдателей, строка поиска.
ISS-07 Заголовок и описание редактируются по кнопке «Изменить» или по клику в текст; сохранение Ctrl+Enter, отмена Esc. Пока пользователь правит, другим показывается «Редактирует Иван» (через SSE); при сохранении поверх более новой версии — предупреждение с диффом, сохранение с подтверждением.
ISS-08 Подзадачи: блок с прогрессом сделано/всего, галочки прямо в блоке (переводят в состояние категории готово), быстрое добавление. Подзадача не может иметь свои подзадачи; попытка выбрать родителя у подзадачи — ошибка 400 depth.
ISS-09 Панель полей справа, каждое поле правится по клику без формы; на экране уже 900 px панель уезжает под описание, сворачиваемая. Поле «На досках» перечисляет доски проекта, где эта задача видна, со ссылкой на колонку.
ISS-10 Меню «⋯»: копировать ключ, копировать ссылку, перенести в другой проект (Администратор), клонировать (v1.1), скачать все вложения, удалить (с подтверждением, в корзину).
ISS-11 Активность: две вкладки-фильтра «Комментарии» и «История», переключатель порядка (новые сверху или снизу), настройка запоминается. История показывает «поле: было → стало» с автором и временем; переходы состояния выделены цветом состояния.
ISS-12 Комментарий: markdown-редактор с упоминаниями @, вложениями, предпросмотром; «Изменено» с временем после правки; удаление — мягкое, с текстом «Комментарий удалён» в ленте, чтобы не ломать контекст ответов.
ISS-13 Упоминание @имя подписывает упомянутого на задачу и шлёт уведомление (NTF-02). Упоминать можно только участников проекта.
ISS-14 Вложения: до 25 МБ на файл (настраивается), типы из белого списка (картинки, pdf, офисные, txt/md/csv/json, архивы, логи); картинки показываются миниатюрами с просмотром на месте; остальное — списком с размером. Отдача файлов — через сервер с проверкой права (SEC-06).
9.3. Список задач
Адреса: /issues (все проекты), /p/{KEY}/issues (проект). Фильтры сериализуются в query-параметры, так что любой набор фильтров — это ссылка, которой можно поделиться.
┌───────────────────────────────────────────────────────────────────────────┐ │ Задачи · tazzz.ru [Таблица|Список] [+]│ │ [/ текст] [Состояние: нерешённые ×] [Исполнитель: я ×] [+ фильтр] │ │ Группировать: состояние v Сортировать: приоритет v 47 задач │ ├───────────────────────────────────────────────────────────────────────────┤ │ > В работе (5) │ │ ▌TAZ-128 Фото из Dongchedi не попадают в карусель * Иван фото c2 12.09│ │ ▌TAZ-131 Тест на пустую карусель o — ^TAZ-128 │ │ > Беклог (42) │ │ ▌TAZ-133 Спецкаталог Autoru: слить по имени * Салам каталог │ │ … │ └───────────────────────────────────────────────────────────────────────────┘
LST-01 Фильтры v1: текст (по заголовку, описанию и комментариям через FTS), проект (несколько), состояние (несколько; пресеты «нерешённые», «решённые»), категория состояния, тип, приоритет, исполнитель (включая «я» и «не назначен»), автор, метки, родитель, срок (до даты, просрочено, без срока), обновлено (за день, неделю, месяц), наблюдаю, есть вложения. Каждый фильтр — чип с подсказками значений при вводе.
LST-02 Группировка: без группировки, по состоянию, по исполнителю, по приоритету, по типу, по проекту, по родителю. Группы сворачиваются, состояние свёрнутости запоминается в localStorage.
LST-03 Сортировка: по обновлению (по умолчанию), созданию, приоритету, сроку, состоянию, ключу; направление переключается. Внутри группировки по состоянию доступен ручной порядок — тот же rank с доски по умолчанию проекта.
LST-04 Два макета: «Список» (строка: полоса приоритета, ключ, заголовок, исполнитель-аватар, метки, счётчики, срок) и «Таблица» (выбор колонок: все поля, ширины запоминаются). На телефоне только «Список».
LST-05 Клик по строке открывает задачу в боковой панели предпросмотра (на широком экране) или на своей странице (на узком); настройка «Открывать в панели» в профиле. ↑/↓ двигают выделение, Enter открывает, Esc закрывает панель.
LST-06 Массовые действия: выделение чекбоксами (Shift — диапазон), затем «Состояние», «Исполнитель», «Приоритет», «Метки ±», «Перенести», «Удалить»; каждое действие — одна операция POST /issues/bulk с проверкой права на каждую задачу; результат сообщает, сколько изменено и сколько отклонено.
LST-07 Экспорт текущей выборки в CSV (v1) и XLSX (v1.1), колонки — как в таблице.
LST-08 Пагинация курсорная, по 50; общий счётчик показывается всегда.
LST-09 Текстовый запрос (v1.1) с грамматикой, совместимой с чипами: проект: TAZ состояние: {В работе, На проверке} исполнитель: я #нерешённые срок: < 2026-09-15 сортировать: приоритет desc текст: "карусель". Английские синонимы атрибутов (project, state, assignee, sort by) допускаются. Запрос и чипы — два представления одного объекта фильтра IssueQuery, поэтому переключение между ними без потерь.
10. Канбан-доска
10.1. Устройство
BRD-01 При создании проекта автоматически создаётся доска «Канбан» с колонками из состояний по умолчанию: Беклог | В работе | На проверке | Готово; состояние Отклонена в колонки не входит. Эта доска — default_board_id и открывается по вкладке «Доска».
BRD-02 Досок в проекте может быть несколько (например, «Канбан» и «Релиз 2.0» с фильтром по эпику). Доска принадлежит проекту; кросс-проектные доски — v2.
BRD-03 Колонка содержит одно или несколько состояний; каждое состояние — не более чем в одной колонке доски. Перетаскивание карточки в колонку с несколькими состояниями ставит первое из них; на карточке в такой колонке видно фактическое состояние.
BRD-04 Задача попадает на доску, если её состояние входит в одну из колонок и она проходит фильтр доски (query). Задачи в состояниях категории готово/отменено показываются в своей колонке только если resolved_at не старше done_days (по умолчанию 14); в заголовке колонки — «показать все».
BRD-05 Заголовок колонки: название, счётчик карточек, при заданном лимите — 3 / 5; при нарушении Max WIP колонка подсвечивается тёплым фоном, при нарушении Min — пунктирной рамкой; перетаскивание сверх лимита разрешено, но требует подтверждения («В колонке уже 5 карточек, лимит 5. Всё равно перенести?»). Настройка «жёсткий лимит» (запрет) — v1.1.
BRD-06 У колонки есть политика — до 200 символов markdown; показывается по наведению на заголовок и в свёрнутом виде в настройках. По умолчанию для колонок из BRD-01: «Беклог — обсуждено, есть критерий готово», «В работе — есть исполнитель, взято не больше лимита», «На проверке — ждёт ревью или проверки заказчиком», «Готово — критерий готово выполнен».
BRD-07 При создании доски Max WIP колонки «В работе» СЛЕДУЕТ предзаполнять числом участников проекта с ролью Участник или выше; администратор может стереть.
BRD-08 Дорожки: нет; по исполнителю (плюс дорожка «Не назначено»); по приоритету; по типу; по родителю (эпик как дорожка, плюс дорожка «Без эпика»). Дорожки сворачиваются, порядок дорожек по исполнителю — по имени, по приоритету — по убыванию, по родителю — по рангу родителя.
BRD-09 Карточка: полоса приоритета слева, ключ (моноширинный), заголовок (до трёх строк), иконка типа, аватар исполнителя, метки, срок (красный, если просрочен), прогресс подзадач 1/2, счётчики вложений и комментариев; по настройке card_fields любые из них скрываются. Клик открывает задачу в боковой панели, Shift-клик — на отдельной странице.
BRD-10 Порядок карточек в колонке ручной (rank): при перетаскивании ранг = среднее соседей; при разнице рангов меньше 1e-6 колонка перенумеровывается. Новая задача получает ранг ниже всех в колонке (в конец), задача из «Входящих» при принятии — в конец Беклога.
BRD-11 Фильтры на доске: текст, «мои», исполнитель, тип, приоритет, метка — те же чипы, что в списке; сохраняются в URL. Кнопка «Свернуть решённые».
BRD-12 Быстрое создание в любой колонке (ISS-02); контекстное меню карточки: назначить мне, приоритет, состояние, переместить в верх или низ, открыть на странице.
BRD-13 Клавиатура: j/k — карточка ниже и выше, h/l — колонка, Enter — открыть, [ и ] — перенести на колонку влево и вправо, m — назначить мне, Esc — снять выделение.
BRD-14 Телефон: колонки горизонтальной лентой со snap-прокруткой по одной, заголовки колонок — липкая полоса сверху с точками-индикаторами; перетаскивание карточек — долгим нажатием; альтернатива без drag — меню карточки «В колонку…».
10.2. Настройки доски
Вкладки: Общее (название, владелец, по умолчанию для проекта, дни показа решённых, фильтр карточек), Колонки (список с перетаскиванием; у колонки: название, состояния, Min/Max WIP, политика, свернута по умолчанию; кнопки «Добавить колонку», «Добавить из состояния»), Дорожки (выбор из пяти вариантов, показывать пустые дорожки), Карточка (галочки полей, цвет карточки по проекту или по типу).
BRD-15 Состояния, не вошедшие ни в одну колонку, перечислены под списком колонок как «Не на доске» с предупреждением, если среди них есть состояния категории в работе.
BRD-16 Удаление доски не трогает задачи; удаление доски по умолчанию запрещено, пока не назначена другая.
10.3. Обновление в реальном времени
RT-01 Клиент открывает GET /api/v1/events?projects=TAZ,DUSHA (Server-Sent Events) один раз на вкладку; сервер шлёт event: issue, event: article, event: board, event: presence, event: notification с data = {project_key, key, action, actor_id, version, ts} — без содержимого. Клиент инвалидирует кэш TanStack Query по ключу и дозапрашивает нужное.
RT-02 Источник событий — Redis pub/sub канал events:<project_id> (и events:user:<id> для уведомлений); публикует сервис после фиксации транзакции (after_commit), не внутри неё.
RT-03 Сервер шлёт : ping каждые 25 с; nginx для этого пути — proxy_buffering off, proxy_read_timeout 3600s. Клиент переподключается с Last-Event-ID и экспоненциальной паузой до 30 с; при недоступности SSE — опрос GET /api/v1/changes?since= раз в 30 с.
RT-04 Перетаскивание карточки применяется оптимистично; ответ сервера с 409 version (кто-то уже передвинул) откатывает карточку и показывает тост «Иван уже перенёс TAZ-128 в Готово».
RT-05 Присутствие: сервер отмечает в Redis presence:<object> с TTL 60 с, кто открыл задачу или статью; по событию presence в шапке показываются аватары («Смотрят: Иван, Ольга»), а в режиме редактирования — «Редактирует Иван» (ISS-07, KB-09).
11. База знаний
11.1. Устройство
База знаний — это дерево статей в каждом проекте плюс сквозные представления: страница «База знаний» со всеми проектами, глобальный поиск, избранное, «недавно изменённые». Для знаний, не привязанных к продукту (онбординг, процессы, глоссарий команды, шаблоны), при установке создаётся проект Команда с ключом TEAM; в нём нет задач по умолчанию, но технически это обычный проект.
Статья — это markdown-документ с заголовком, ответственным, датой пересмотра, метками, положением в дереве, историей ревизий, комментариями и вложениями. Папок нет: их роль играют родительские статьи, как в YouTrack.
┌────────────┬───────────────────────────────────────────────┬─────────────┐ │ База знаний│ Проекты > По душе v > База знаний > Бэкенд > │ Содержание │ │ / поиск │ DUSHA-A-14 │ Архитектура │ │ │ │ Сервисы │ │ v По душе │ Создала Ольга 29.07 · Изменил Салам вчера │ Очереди │ │ v Проект │ Ответственный: Ольга · Пересмотр: 01.12.2026 │ Окружения │ │ Договор!│ │ Источники │ │ Хроника │ Бэкенд: архитектура * [Изменить] : │ │ │ v Продукт │ ───────────────────────────────────────────── │ │ │ События │ Бэкенд называется nikea-backend, потому что… │ │ │ Прототипы│ ## Сервисы │ │ │ v Бэкенд │ | Сервис | Порт | Зачем | │ │ │ * Архит… │ … │ │ │ API │ См. задачу DUSHA-42 и [[Решение: очереди]] │ │ │ Админка │ │ │ │ > Клиенты │ Подстатьи (3) │ │ │ > Решения │ API · Иван · 02.09 Админка · Ольга · 28.08│ │ │ > tazzz.ru │ Очереди · Салам · вчера │ │ │ > Команда │ ───────────────────────────────────────────── │ │ │ Черновики 2│ Ссылаются сюда: DUSHA-A-3, DUSHA-42, TEAM-A-1 │ │ │ + Статья │ Комментарии (2) · История (7) │ │ └────────────┴───────────────────────────────────────────────┴─────────────┘
11.2. Дерево
KB-01 Левая панель показывает дерево статей текущего проекта и свёрнутые корни остальных проектов, где у пользователя есть article.read. Ширина панели тянется и запоминается; на телефоне панель — выдвижной ящик.
KB-02 Перетаскивание в дереве меняет родителя и позицию вместе со всеми потомками; операция одна — PATCH /articles/{key} {parent_id, position} — и проверяет RBAC-12 и отсутствие циклов (400 cycle).
KB-03 У каждой статьи есть кнопка «Добавить подстатью»; новая статья создаётся в конце списка потомков как черновик с заголовком «Без названия» и сразу открывается в редакторе.
KB-04 В дереве черновики (неопубликованные) показаны курсивом с точкой, ограниченные — замком, статьи с просроченной датой пересмотра — предупреждающим значком. Внизу панели — «Черновики (N)»: все черновики пользователя во всех проектах.
KB-05 Корневая статья проекта: при создании проекта автоматически создаётся статья-индекс «О проекте <название>» из шаблона «Индекс» — одна точка входа. Её нельзя удалить, пока в дереве есть другие статьи.
11.3. Страница статьи
KB-06 Хлебные крошки: проекты → проект (выпадающий список) → база знаний → цепочка родителей → ключ с копированием. Под ними метаданные: кто создал и когда, кто и когда изменил, ответственный, дата пересмотра, замок и текст «Только администраторы проекта», если ограничена.
KB-07 Справа — оглавление по заголовкам h2–h4 статьи с подсветкой текущего раздела при прокрутке; скрывается на узком экране под кнопку.
KB-08 Тулбар: звезда «В избранное» (подписка на изменения и комментарии), «Изменить», «Метка», меню «⋯»: добавить подстатью, копировать ключ, копировать ссылку, копировать как markdown, скачать .md со вложениями (zip), история, переместить, ограничить или снять ограничение (Администратор), удалить.
KB-09 Редактирование: одна статья редактируется одним человеком одновременно. Открытие редактора ставит «Редактирует Иван» через присутствие RT-05; второй пользователь видит предупреждение и может открыть в режиме чтения либо «всё равно редактировать». Сохранение проверяет version (оптимистическая блокировка): при конфликте показывается дифф и выбор «перезаписать» или «открыть свою копию в новой вкладке». Совместное редактирование в реальном времени (Yjs) — v2.
KB-10 Черновик автосохраняется раз в 3 с после изменения в draft_md, не создавая ревизии; кнопка «Опубликовать» переносит черновик в content_md, создаёт ревизию с необязательной подписью и рассылает уведомления; «Отменить изменения» стирает черновик. При закрытии вкладки с несохранённым черновиком он остаётся на сервере, и при следующем открытии редактор предлагает продолжить.
KB-11 Блок «Ссылаются сюда» — обратные ссылки из статей и задач, вычисленные из references; обновляются при каждой публикации ссылающегося документа.
KB-12 Ответственный обязателен (по умолчанию автор). Дата пересмотра необязательна, но шаблоны «Инструкция» и «Справочник» ставят её на +90 дней, «Решение» — не ставят. Просроченная дата помечает статью «Пора пересмотреть» в дереве, в списках и на самой странице; ответственный получает уведомление в день срока и раз в неделю после. Кнопка «Актуально» продлевает дату на тот же интервал и пишет запись в историю без новой ревизии.
KB-13 Страница проекта «База знаний» показывает счётчики: всего статей, черновиков, просроченных пересмотров, без ответственного (после импорта), а также список «Устаревшие» для администратора.
KB-14 Комментарии к статье — как у задач (ISS-12), лента под статьёй; inline-комментарии к фрагментам текста — v2.
11.4. Редактор
ED-01 Один редактор для описаний задач, комментариев и статей: TipTap 2 (ProseMirror) с сериализацией в markdown (tiptap-markdown) и переключателем «Markdown» на сырой текст с предпросмотром. Содержимое всегда сохраняется как markdown; что нельзя выразить в markdown, в редакторе не существует.
ED-02 Поддерживаемый markdown: CommonMark + GFM (таблицы, зачёркивание, чек-листы, автоссылки), заголовки h1–h4, цитаты, код с подсветкой (highlight.js на клиенте), картинки, сноски — нет, HTML — вырезается при рендере (nh3), кроме <details> и <br>.
ED-03 Расширения: упоминания @имя (участники проекта); ссылки на задачи и статьи по ключу (TAZ-128, DUSHA-A-14) с всплывающей карточкой; вики-ссылки [[Заголовок статьи]] внутри проекта с подсказками по заголовкам, при сохранении превращаются в [Заголовок](/p/KEY/kb/N-slug), ключ статьи в квадратных скобках тоже понимается; диаграммы в блоке ```mermaid рендерятся на клиенте; чек-листы в описании задачи кликабельны в режиме чтения.
ED-04 Слэш-меню /: заголовок, список, чек-лист, таблица, код, цитата, картинка, разделитель, диаграмма, вставить шаблон, дата. Панель форматирования появляется над выделением.
ED-05 Вставка картинок и файлов перетаскиванием или из буфера; картинки ужимаются на клиенте до 2000 px по большей стороне, если больше.
ED-06 Вставка HTML из Confluence, Google Docs или Word превращается в markdown на клиенте (turndown); таблицы сохраняются.
ED-07 Горячие клавиши стандартные: Ctrl+B/I/K, Ctrl+Shift+7/8 списки, Ctrl+Enter сохранить, Esc отмена; Tab в списках — уровень.
11.5. Импорт и экспорт
KB-16 Консольная команда tracker import-markdown <dir> --project DUSHA [--parent DUSHA-A-1] [--owner ольга@…] [--restricted-glob "10-project/contract*"] импортирует папку: каждая .md — статья, каждая папка — статья-раздел (из README.md или INDEX.md папки, иначе пустая с названием папки), числовые префиксы 10-project убираются из заголовка, но задают порядок; frontmatter title, updated, owner, tags читается; [[wiki-ссылки]] разрешаются по заголовкам и именам файлов; относительные картинки загружаются как вложения; sources/ в plibvi/kb помечается меткой «источник» и ограничением по маске. Запускается повторно без дублей (сопоставление по относительному пути в import_path).
KB-17 Экспорт: одной статьи с потомками или всего проекта в zip с той же структурой папок и frontmatter; кнопка на странице проекта и команда tracker export-markdown. Экспорт — способ держать копию базы в git, если команда захочет.
KB-18 Импорт auto-parser/docs (HTML-снимки беклога) не автоматизируется: карточки эпиков переносятся руками как задачи типа Эпик по шаблону ISS-05.
11.6. Шаблоны статей
Шаблон — markdown-заготовка с полями, подставляемыми при создании. Хранятся в проекте TEAM как статьи под разделом «Шаблоны» с меткой шаблон; Владелец и администраторы TEAM их правят, проекты ими пользуются. Набор по умолчанию, по Diátaxis и практикам команды:
| Шаблон | Для чего | Разделы |
|---|---|---|
| Индекс | корневая статья проекта | О чём проект · С чего начать · Разделы · Люди и роли · Ссылки (репозиторий, стенд, договор) |
| Инструкция | как сделать X | Когда применять · Шаги · Проверка · Что может пойти не так · Источники |
| Справочник | факты: окружения, порты, доступы | Таблица · Как обновлять · Источники; пересмотр +90 дней |
| Объяснение | почему устроено так | Контекст · Как устроено · Компромиссы · Что почитать |
| Решение (ADR) | зафиксировать решение | Контекст · Решение · Последствия · Триггеры пересмотра · Связанные задачи |
| Встреча | итоги обсуждения | Участники · Решили · Задачи (ключи) · Открытое |
| Заказчик | справочник заказчика | Контакты · Договор и сроки · Что поставляем · Особенности · Связанные проекты |
| Дайджест сессии | итог рабочей сессии с ассистентом | Что сделано · Решения · Что дальше · Источники |
| Глоссарий | термины проекта | Термин · Определение · Не путать с |
KB-15 У каждой статьи, созданной из шаблона, в конце есть раздел «Источники» (кроме Встречи и Глоссария), потому что «любой факт в заметке должен быть прослеживаем до источника» — правило plibvi/kb, которое переносится как есть.
12. Поиск
SRCH-01 Глобальный поиск открывается клавишей / или полем в шапке; ищет по задачам (заголовок, описание, комментарии) и статьям (заголовок, текст, комментарии) во всех проектах, где есть право чтения; результаты — двумя группами с подсветкой совпадений и фильтром по проекту.
SRCH-02 Ввод точного ключа (TAZ-128, dusha-a-14 в любом регистре) открывает объект сразу.
SRCH-03 Реализация — Postgres: search_vector — генерируемая колонка setweight(to_tsvector('russian', title), 'A') || setweight(to_tsvector('russian', content), 'B') || setweight(to_tsvector('english', title || ' ' || content), 'C') с GIN-индексом; запрос — websearch_to_tsquery('russian', :q) в объединении с english; ранжирование ts_rank_cd плюс бонус за свежесть. Комментарии индексируются в собственной таблице и подмешиваются к родителю.
SRCH-04 Опечатки и префиксы: pg_trgm по заголовкам (similarity(title, :q) > 0.3) как второй источник кандидатов, когда FTS дал меньше 5 результатов.
SRCH-05 Подсказки при вводе (GET /search/suggest?q=) — до 8 заголовков по триграммам, ответ не дольше 100 мс на 10 000 документов.
SRCH-06 Права применяются в SQL (join project_members), а не пост-фильтром, чтобы пагинация была честной; ограниченные статьи отбираются условием not restricted or role = 'admin' or author_id = :me.
13. Уведомления и активность
13.1. События
| Код | Когда | Кому |
|---|---|---|
mention |
упомянули в описании, комментарии, статье | упомянутому |
assigned |
назначили исполнителем | исполнителю |
issue_commented |
комментарий к задаче | наблюдателям, кроме автора комментария |
issue_state |
смена состояния | наблюдателям |
issue_changed |
смена приоритета, срока, заголовка, родителя | наблюдателям (сгруппировано, NTF-04) |
article_published |
опубликована ревизия | подписчикам (избранное) и ответственному |
article_commented |
комментарий к статье | подписчикам и ответственному |
article_review_due |
наступила дата пересмотра | ответственному, раз в неделю до продления |
invite_accepted |
приглашение принято | приглашающему |
added_to_project |
добавили в проект или сменили роль | пользователю |
inbox_new |
новая задача во «Входящих» | администраторам проекта |
13.2. Доставка
NTF-01 В приложении: колокольчик с счётчиком непрочитанных, панель со списком (группировка по дню), «прочитать всё», клик открывает объект. Хранятся 90 дней.
NTF-02 Уведомление о упоминании и назначении доставляется немедленно; остальные события по одной задаче или статье схлопываются в одно уведомление, если произошли в течение 5 минут от одного актора (NTF-04).
NTF-03 Каналы: в приложении (всегда), Telegram-бот (v1.1: пользователь привязывает аккаунт кодом из профиля; бот шлёт текст с ссылкой), почта (только если настроен SMTP; в v1 выключено). Браузерные push-уведомления не делаются (пользователь отказался от них для панели).
NTF-04 Схлопывание: события одного вида по одному объекту от одного актора в течение 5 минут дополняют существующее непрочитанное уведомление, а не создают новое.
NTF-05 Настройки в профиле (notify_prefs): для каждого вида — «в приложении», «Telegram»; глобальный «тихий режим» с интервалом по часовому поясу пользователя; «не уведомлять о моих собственных действиях» включено всегда.
13.3. Лента активности
ACT-01 У проекта есть страница «Активность»: хронология событий activity по задачам и статьям с фильтрами по виду, автору, объекту; служит и для еженедельного разбора («что изменилось за неделю»).
ACT-02 Домашняя страница «Моё» показывает: назначенные мне нерешённые задачи (по сроку и приоритету), задачи, где я автор и которые во «Входящих» или без исполнителя, статьи, за которые я ответственен, с ближайшим пересмотром, последние 20 событий в моих проектах, непрочитанные уведомления.
ACT-03 Журнал аудита (SEC-09) — отдельная страница Владельца с фильтрами по действию, пользователю, дате и экспортом в CSV.
14. Интерфейс
14.1. Общая раскладка
┌──────────┬────────────────────────────────────────────────────────────────┐ │ = Трекер │ Проекты > tazzz.ru v > Доска / ! 3 o │ │ ├────────────────────────────────────────────────────────────────┤ │ + Создать│ │ │ │ │ │ ~ Моё │ │ │ _ Задачи │ содержимое страницы │ │ # Доски │ │ │ = База │ │ │ знаний │ │ │ o Проекты│ │ │ ──────── │ │ │ * tazzz │ │ │ * По душе│ │ │ ──────── │ │ │ * Админ. │ │ │ o Салам │ │ └──────────┴────────────────────────────────────────────────────────────────┘
UI-01 Боковая панель слева, сворачивается кнопкой и клавишей [; состояние запоминается. Развёрнутая — 232 px с подписями, свёрнутая — 56 px с иконками и подсказками. Порядок: логотип и название инстанса; «+ Создать» (выпадающее: Задача c, Статья, Проект — последнее только Владельцу); Моё; Задачи; Доски; База знаний; Проекты; разделитель; избранные проекты, доски и статьи (звезда); разделитель; Администрирование (Владелец); аватар и имя (меню: профиль, тема, выйти).
UI-02 Шапка страницы: хлебные крошки с выпадающим списком проектов на уровне проекта (переключение без возврата к списку), поиск /, колокольчик, действия страницы справа. Крошки кликабельны на каждом уровне.
UI-03 Внутри проекта у шапки есть вторая строка-вкладки: Обзор · Задачи · Доска · База знаний · Активность · Участники · Настройки (последние две — по правам). Вкладка помнит последнее состояние (фильтры, выбранная доска).
UI-04 Телефон (уже 720 px): боковая панель превращается в нижнюю навигацию с пятью кнопками (Моё, Задачи, Доски, База, Ещё), вкладки проекта — в горизонтальную прокручиваемую полосу, панель полей задачи — под описание, дерево статей — в выдвижной ящик, таблицы — в карточки. Все касаемые цели не меньше 44 px. Enter в редакторе переносит строку, отправка — кнопкой (правило панели).
UI-05 Тема светлая и тёмная, по системной настройке с ручным переключением в профиле; цвета состояний, приоритетов и меток подобраны парами для обеих тем; контраст текста не ниже 4.5:1.
UI-06 Язык интерфейса — русский; даты в формате дд.мм.гггг и относительные («вчера», «5 мин назад») с полной датой по наведению; часовой пояс из профиля.
UI-07 Пустые состояния объясняют следующий шаг: «В проекте пока нет задач. Создайте первую или включите Входящие»; ошибки говорят, что случилось и что делать («Не удалось сохранить: кто-то изменил задачу. Обновите и повторите»).
UI-08 Загрузка: скелетоны для списков и доски, без спиннера на весь экран; переходы между страницами без перезагрузки (SPA), данные кешируются TanStack Query с фоновым обновлением.
UI-09 Доступность: все действия доступны с клавиатуры, видимый фокус, aria-label у иконок, prefers-reduced-motion отключает анимации перетаскивания.
14.2. Страницы и адреса
| Адрес | Страница | Права |
|---|---|---|
/login |
Вход | нет |
/invite/{token} |
Принятие приглашения | нет |
/reset/{token} |
Сброс пароля | нет |
/ |
Моё | вход |
/issues |
Все задачи с фильтрами | вход |
/boards |
Все доски (карточки по проектам) | вход |
/kb |
Вся база знаний: проекты, недавние, черновики, устаревшие | вход |
/projects |
Список проектов (активные, архив) | вход |
/p/{KEY} |
Обзор проекта: описание, счётчики, участники, последние события | project.read |
/p/{KEY}/issues |
Задачи проекта | project.read |
/p/{KEY}/issues/{KEY}-{N} и /{KEY}-{N} |
Задача | issue.read |
/p/{KEY}/board и /p/{KEY}/boards/{id} |
Доска | board.view |
/p/{KEY}/boards/{id}/settings |
Настройки доски | board.manage |
/p/{KEY}/kb |
База знаний проекта (корневая статья) | article.read |
/p/{KEY}/kb/{N}-{slug} и /{KEY}-A-{N} |
Статья | article.read |
/p/{KEY}/kb/{N}/edit |
Редактор статьи | article.update |
/p/{KEY}/kb/{N}/history |
История ревизий с диффом | article.read |
/p/{KEY}/activity |
Активность проекта | project.read |
/p/{KEY}/members |
Участники и приглашения проекта | project.members |
/p/{KEY}/settings |
Настройки: общее, состояния, типы, метки, шаблоны, доски, корзина | project.settings |
/search?q= |
Результаты поиска | вход |
/notifications |
Все уведомления | вход |
/me |
Профиль, сессии, токены, уведомления | вход |
/admin/users |
Пользователи | Владелец |
/admin/invitations |
Все приглашения | Владелец |
/admin/settings |
Настройки инстанса | Владелец |
/admin/audit |
Журнал аудита | Владелец |
14.3. Горячие клавиши
| Клавиши | Действие |
|---|---|
c |
Создать задачу |
/ |
Поиск |
g m / g i / g b / g k / g p |
Моё / Задачи / Доски / База знаний / Проекты |
[ |
Свернуть или развернуть боковую панель |
e |
Изменить (задача, статья) |
a |
Назначить исполнителя |
s |
Сменить состояние |
l |
Добавить метку |
Ctrl+Enter / Esc |
Сохранить / отменить в редакторе |
j k h l [ ] m |
На доске (BRD-13) |
↑ ↓ Enter |
В списке (LST-05) |
? |
Показать список клавиш |
14.4. Страница приглашения
┌──────────────────────────────────────────────┐ │ Трекер salam0nn │ │ │ │ Салам приглашает вас в проекты: │ │ • tazzz.ru — Участник │ │ • Команда — Читатель │ │ │ │ Имя [ Иван Петров ] │ │ Почта [ ivan@example.ru ] │ │ Пароль [ •••••••••••• ] │ │ Ещё раз [ •••••••••••• ] │ │ │ │ [ Создать аккаунт и войти ] │ │ Уже есть аккаунт? Войти и принять │ │ Ссылка действует до 16.09.2026 │ └──────────────────────────────────────────────┘
15. API
15.1. Общие правила
API-01 REST под префиксом /api/v1, JSON, OpenAPI-схема на /api/v1/openapi.json и Swagger UI на /api/v1/docs (только для вошедших). Один и тот же API используют фронтенд, консольные команды и внешние клиенты по токену.
API-02 Аутентификация: cookie-сессия (AUTH-02) или Authorization: Bearer tracker_pat_… (AUTH-07). Изменяющие запросы с cookie требуют X-Requested-With: tracker (AUTH-03).
API-03 Ошибки — единый формат: {"error": {"code": "permission_denied", "message": "Нет права менять состояние задачи", "details": {...}}}. Коды: validation (422), unauthorized (401), permission_denied (403), csrf (403), not_found (404), conflict (409, с details.current_version), last_admin (409), rate_limited (429), invite_expired, invite_used, invite_revoked (410), too_large (413), unsupported_type (415).
API-04 Списки — курсорная пагинация: ?limit=50&cursor=…, ответ {"items": [...], "next_cursor": "...", "total": 47}. Фильтры — query-параметры с повторением для множеств (state=1&state=2) или объект IssueQuery в POST /issues/query для длинных запросов.
API-05 Обновления — PATCH с частичным телом; для конкурентных объектов (задача, статья, доска) обязателен version в теле или заголовок If-Match; несовпадение — 409 conflict.
API-06 Все объекты отдают key (для задач и статей), url, created_at, updated_at, version; вложенные пользователи — {id, name, avatar_url}; ссылки на другие объекты — по ключам.
API-07 Ограничение частоты: 600 запросов в минуту на сессию или токен; /auth/* и /invite/* — по AUTH-01 и INV-09. Заголовки X-RateLimit-Remaining, Retry-After.
API-08 Идемпотентность создания: заголовок Idempotency-Key (UUID) на POST /issues, POST /articles, POST /attachments; повтор с тем же ключом в течение 24 ч возвращает первый результат.
15.2. Ресурсы
| Метод и путь | Что делает | Право |
|---|---|---|
POST /auth/login, POST /auth/logout |
вход, выход | — / вход |
GET /me, PATCH /me, POST /me/password, GET /me/sessions, DELETE /me/sessions/{id} |
профиль, пароль, сессии | вход |
GET,POST /me/tokens, DELETE /me/tokens/{id} |
персональные токены (v1.1) | вход |
GET /invite/{token}, POST /invite/{token}/accept |
просмотр и принятие приглашения | нет |
GET,POST /invitations, POST /invitations/{id}/resend, POST /invitations/{id}/revoke |
приглашения | project.invite / Владелец |
GET,POST /projects, GET,PATCH,DELETE /projects/{key}, POST /projects/{key}/archive |
проекты | см. матрицу |
GET,POST /projects/{key}/members, PATCH,DELETE /projects/{key}/members/{user_id}, POST /projects/{key}/leave |
участники | project.members |
GET,POST,PATCH,DELETE /projects/{key}/states, …/types, …/tags, POST …/states/reorder |
настройки проекта | project.settings |
GET /issues, POST /issues/query, POST /issues |
список и создание | issue.read / issue.create |
GET,PATCH,DELETE /issues/{key}, POST /issues/{key}/move, POST /issues/{key}/watch, DELETE …/watch |
задача | см. матрицу |
GET,POST /issues/{key}/comments, PATCH,DELETE /comments/{id} |
комментарии | comment.* |
GET,POST /issues/{key}/links, DELETE /links/{id} |
связи | issue.link |
GET /issues/{key}/activity |
история | issue.read |
POST /issues/bulk |
массовые изменения | по каждой задаче |
GET /issues/export.csv |
экспорт выборки | issue.read |
GET,POST /projects/{key}/boards, GET,PATCH,DELETE /boards/{id} |
доски | board.* |
GET /boards/{id}/cards |
карточки с колонками и дорожками одним ответом | board.view |
POST /boards/{id}/move {issue_key, column_id, before_key?, after_key?} |
перенос карточки | board.use |
GET /projects/{key}/articles/tree, GET,POST /projects/{key}/articles |
дерево и создание | article.* |
GET,PATCH,DELETE /articles/{key}, PUT /articles/{key}/draft, POST /articles/{key}/publish, POST /articles/{key}/discard-draft |
статья, черновик, публикация | article.* |
GET /articles/{key}/revisions, GET /articles/{key}/revisions/{v}, POST /articles/{key}/restore/{v} |
история | article.read / article.history.restore |
GET /articles/{key}/backlinks, GET /articles/{key}/export.zip |
обратные ссылки, экспорт | article.read |
GET,POST /articles/{key}/comments |
комментарии | article.comment.* |
POST /attachments (multipart, owner_kind, owner_key), GET /attachments/{uuid}, DELETE /attachments/{uuid} |
вложения | attachment.* |
POST /favorites, DELETE /favorites/{kind}/{id}, GET /favorites |
избранное | вход |
GET /search?q=&project=&kind=, GET /search/suggest?q= |
поиск | вход |
GET /notifications, POST /notifications/read, POST /notifications/read-all |
уведомления | вход |
GET /projects/{key}/activity, GET /changes?since= |
лента, дельта для опроса | project.read |
GET /events?projects= |
SSE | вход |
GET /admin/users, PATCH /admin/users/{id}, POST /admin/users/{id}/reset-link, GET,PATCH /admin/settings, GET /admin/audit |
администрирование | Владелец |
GET /health |
{"status":"ok","db":true,"redis":true,"version":"…"} |
— (только с loopback nginx) |
API-09 Ответ GET /boards/{id}/cards собирается одним SQL-запросом с оконной сортировкой по rank и отдаёт {columns: [{id, name, wip_min, wip_max, count, states}], swimlanes: [...], cards: [{key, title, column_id, swimlane_id, rank, priority, type, assignee, tags, due_date, subtasks: {done, total}, counts}]}; на 500 карточек ответ не дольше 300 мс.
API-10 Консольные команды (tracker … внутри контейнера api): create-owner, create-project KEY "Название", import-markdown, export-markdown, expire-invitations, reindex-search, purge-deleted, backup (обёртка над pg_dump + архив вложений).
API-11 MCP-сервер для Claude Code (v1.1): тонкая обёртка над этим же API с инструментами search, get_issue, create_issue, update_issue, comment, get_article, upsert_article, list_board; авторизуется персональным токеном пользователя-бота. Это закрывает вопрос «агент как пользователь» из раунда 1.
16. Архитектура и стек
16.1. Компоненты
ARCH-01 Бэкенд: Python 3.12, FastAPI, SQLAlchemy 2 (async, asyncpg), Alembic, Pydantic v2, argon2-cffi, redis-py, arq (фоновые задачи), nh3 (санитайзер HTML), markdown-it-py (рендер markdown на сервере для превью и уведомлений), structlog, python-multipart, Pillow (миниатюры и аватары), uvicorn. Зависимости — pyproject.toml + uv.lock, как в nashpartner.
ARCH-02 Python-пакет называется tracker, а точка входа — tracker.main:app. Не app.main:app: соседние проекты перезапускают свои серверы командой pkill -f "uvicorn app.main:app", и она уже дважды роняла панель; процессы контейнеров видны на хосте, так что совпадение имени убило бы и трекер.
ARCH-03 Слои бэкенда: tracker/api/* — роутеры и схемы (тонкие); tracker/services/* — сценарии (создать задачу, перенести карточку, опубликовать статью) с проверкой прав через tracker/authz.py и записью в activity; tracker/models/* — SQLAlchemy; tracker/search.py, tracker/events.py (публикация в Redis, SSE-генератор), tracker/markdown.py (рендер, извлечение упоминаний и ключей), tracker/storage.py (файлы), tracker/cli.py (Typer), worker/ (arq-задачи).
ARCH-04 Фронтенд: React 19, TypeScript, Vite, Tailwind, Radix UI (shadcn-компоненты), TanStack Query и Router, react-hook-form + zod, TipTap 2 + tiptap-markdown, @dnd-kit (перетаскивание), lucide-react, sonner (тосты), date-fns с русской локалью, highlight.js, mermaid (ленивая загрузка), turndown. Сборка bun, вывод — статика.
ARCH-05 Структура репозитория:
tracker/
CONTEXT.md словарь предметной области (раздел 23)
README.md как запустить, как деплоить
docs/SPEC.md этот документ
docs/adr/ журнал решений
backend/
pyproject.toml uv.lock alembic/ alembic.ini Dockerfile
tracker/ worker/ tests/
frontend/
package.json vite.config.ts tailwind.config.js src/ public/
deploy/
docker-compose.yml .env.example nginx-tracker.salam0nn.ru.conf
deploy.sh backup.sh tracker.cron
ARCH-06 Конфигурация — переменные окружения (.env, не в git): DATABASE_URL, REDIS_URL, SECRET_KEY (для подписи служебных ссылок), PUBLIC_URL=https://tracker.salam0nn.ru, UPLOAD_DIR=/data/uploads, MAX_UPLOAD_MB=25, SESSION_TTL_DAYS=30, INVITE_TTL_DAYS=7, SMTP_URL (пусто), TELEGRAM_BOT_TOKEN (пусто), LOG_LEVEL.
ARCH-07 Миграции Alembic применяются при старте контейнера api (alembic upgrade head в entrypoint до запуска uvicorn); откат — alembic downgrade -1 вручную.
ARCH-08 Логи — JSON в stdout (structlog), docker compose logs; в каждой записи request_id, user_id, path, status, duration_ms. Ошибки 5xx дополнительно в audit_log не пишутся, но пишутся с трейсом.
16.2. Бюджет ресурсов
Сервер: 2 ядра, 7,8 ГБ, свободно около 4,8 ГБ (замер 9 сентября), без swap. Уже работают панель, стенд plibvi, nashpartner (postgres, redis, minio), хостовые Postgres и Redis.
| Контейнер | Образ | Память | Примечание |
|---|---|---|---|
tracker_postgres |
postgres:16-alpine |
≤ 200 МБ | shared_buffers=128MB, max_connections=40, work_mem=8MB |
tracker_redis |
redis:7-alpine |
≤ 30 МБ | maxmemory 64mb, allkeys-lru только для лимитов; сессии без вытеснения (noeviction на отдельной базе 1) |
tracker_api |
свой, python:3.12-slim |
≤ 200 МБ | 1 процесс uvicorn, 2 воркера при необходимости |
tracker_worker |
тот же образ | ≤ 100 МБ | 1 процесс arq |
| Итого | ≤ 0,5 ГБ | остаётся запас больше 4 ГБ |
Своя база, а не хостовый Postgres, — потому что бэкап и перенос всего трекера тогда сводятся к docker compose и одной папке; цена — 150 МБ памяти.
17. Безопасность и файлы
SEC-01 Пароли — argon2id (INV-15); секреты — только в .env с правами 600; в git — .env.example.
SEC-02 Весь HTML, отрендеренный из markdown, проходит nh3 с белым списком тегов и атрибутов; javascript: и data: в ссылках вырезаются; картинки с чужих доменов разрешены, но грузятся с referrerpolicy="no-referrer".
SEC-03 Content-Security-Policy для SPA: default-src 'self'; img-src 'self' https: data:; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; frame-ancestors 'none'. Никаких CDN: библиотеки в бандле.
SEC-04 Загрузка файлов: проверка размера до чтения тела (Content-Length и потоковое чтение с обрывом), белый список MIME по сигнатуре (python-magic), а не по расширению; SVG запрещён (XSS); имя файла хранится отдельно от пути на диске; путь — <KEY>/<uuid><ext>.
SEC-05 Хранилище вложений — локальная папка /srv/tracker/uploads (bind-mount в контейнер как /data/uploads). MinIO не используется: одна папка проще бэкапить, а объёмы малые. Переход на S3 — за интерфейсом Storage в tracker/storage.py.
SEC-06 Отдача вложений: GET /api/v1/attachments/{uuid} проверяет право читать владельца вложения и отвечает X-Accel-Redirect: /_uploads/<path> с Content-Disposition и X-Content-Type-Options: nosniff; nginx отдаёт файл сам (location /_uploads/ { internal; alias /srv/tracker/uploads/; }). Картинки в статьях подставляются тем же адресом, поэтому в <img> нет прямых путей.
SEC-07 Лимиты частоты (AUTH-01, INV-09, API-07) хранятся в Redis скользящим окном; ключ — сессия или токен, для анонимных путей — IP из X-Real-IP (доверяем только nginx на loopback).
SEC-08 Сессии и токены отзываются мгновенно (Redis); смена пароля и деактивация вызывают отзыв (AUTH-05, RBAC-09).
SEC-09 Журнал аудита (ACT-03) неизменяем из приложения: только вставка; чистится не раньше чем через 365 дней.
SEC-10 Заголовки: HSTS (nginx), X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: same-origin, Permissions-Policy: camera=(), microphone=(), geolocation=().
SEC-11 Зависимости проверяются pip-audit и bun audit в deploy.sh перед сборкой; результат печатается, сборка не блокируется, но предупреждение попадает в лог деплоя.
SEC-12 Нет внешней телеметрии, аналитики и шрифтов с чужих доменов; всё, что нужно странице, лежит в бандле.
18. Деплой на tracker.salam0nn.ru
18.1. Что уже есть
- DNS:
tracker.salam0nn.ru→2.25.117.83(проверено 9 сентября, тот же адрес, что уsalam0nn.ruиllm.salam0nn.ru). - nginx 1.24 на хосте с vhost-ами
salam0nn.ruиllm.salam0nn.ru; certbot с webroot/var/www/certbotи таймером автопродления. - Docker 29 и Compose 2.40; свободные loopback-порты 34000–34002 (занятые: 8080, 18080–18099, 33000–33005, 5432–5434).
- Bun 1.4 для сборки фронтенда; Node 22 в
/opt/node22для Playwright. - Панель
salam0nn.ru/my-server/показывает контейнеры, порты и сертификаты — мониторинг из коробки.
18.2. Шаги
- Репозиторий
/root/tracker(или клон с GitHub),.envиз.env.exampleс сгенерированными секретами (openssl rand -hex 32). - Compose
deploy/docker-compose.yml: сервисыpostgres,redis,api(127.0.0.1:34000:8000),worker; томаtracker_pg, bind/srv/tracker/uploads:/data/uploads;restart: always; healthcheck у всех; сетьinternal. Postgres и Redis наружу не публикуются вовсе; для отладки —docker compose exec postgres psql. - Фронтенд:
cd frontend && bun install && bun run build, результатdist/→rsync --deleteв/var/www/tracker/. - nginx
deploy/nginx-tracker.salam0nn.ru.conf→/etc/nginx/sites-available/tracker.salam0nn.ru, симлинк вsites-enabled; шаблон — vhostllm.salam0nn.ru(те же TLS-параметры, HSTS,server_tokens off):
server {
listen 80; listen [::]:80;
server_name tracker.salam0nn.ru;
location /.well-known/acme-challenge/ { root /var/www/certbot; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl http2; listen [::]:443 ssl http2;
server_name tracker.salam0nn.ru;
ssl_certificate /etc/letsencrypt/live/tracker.salam0nn.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tracker.salam0nn.ru/privkey.pem;
# ssl_* — как в llm.salam0nn.ru
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
server_tokens off;
client_max_body_size 30M;
root /var/www/tracker;
location / { try_files $uri /index.html; }
location /assets/ { expires 1y; add_header Cache-Control "public, immutable"; }
location /api/v1/events {
proxy_pass http://127.0.0.1:34000;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off; proxy_cache off;
proxy_read_timeout 3600s;
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/ {
proxy_pass http://127.0.0.1:34000;
proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
}
location /_uploads/ { internal; alias /srv/tracker/uploads/; }
}
- Сертификат: сначала включить только HTTP-блок,
nginx -t && systemctl reload nginx, затемcertbot certonly --webroot -w /var/www/certbot -d tracker.salam0nn.ru, включить HTTPS-блок, сноваnginx -t && reload. Продление подхватит существующийcertbot.timer. - Запуск:
docker compose -p tracker -f deploy/docker-compose.yml up -d --build; миграции применятся сами (ARCH-07);docker compose exec api tracker create-owner --email … --name …. - Проверка:
curl -s https://tracker.salam0nn.ru/api/v1/health, вход владельцем, создание проектаTEAM(создаётся автоматически при первом запуске, если его нет), первое приглашение. - Firewall: ничего не меняется — ufw уже пропускает 80 и 443, контейнеры слушают только loopback.
DEP-01 deploy/deploy.sh выполняет шаги 3–6 идемпотентно: git pull (если репозиторий), аудит зависимостей, сборка фронтенда, сборка образа, up -d, ожидание health, nginx -t && reload; при падении здоровья за 60 с — откат на предыдущий образ (docker compose up -d с тегом previous).
DEP-02 Порт 34000 фиксирован в .env (API_PORT), чтобы не столкнуться с соседями при добавлении сервисов; следующий проект берёт 35000.
DEP-03 Страница «Скоро» на tracker.salam0nn.ru СЛЕДУЕТ выложить в первый день (шаги 4–5), до готовности приложения: домен, сертификат и nginx проверяются отдельно от кода.
19. Эксплуатация
OPS-01 Бэкап ежедневно в 03:30 по cron на хосте: pg_dump -Fc из контейнера в /root/backups/tracker/db-<дата>.dump и tar папки вложений (инкрементально, --listed-incremental); хранить 14 ежедневных и 3 месячных; скрипт deploy/backup.sh; после бэкапа — pg_restore --list как проверка целостности. Копия за пределы сервера — вопрос 22.5.
OPS-02 Восстановление задокументировано в README и проверяется раз в квартал: поднять compose с пустым томом, pg_restore, распаковать вложения, reindex-search.
OPS-03 Обновление: deploy.sh; миграции обратимы; перед миграциями со сменой схемы задач или статей — бэкап автоматически.
OPS-04 Мониторинг: панель salam0nn.ru/my-server/ (контейнеры, память, сертификат); GET /api/v1/health в проверку панели; при unhealthy compose перезапускает контейнер сам. Алерты в Telegram — вместе с ботом уведомлений (v1.1).
OPS-05 Логи хранятся docker-драйвером json-file с max-size 20m, max-file 5.
OPS-06 Фоновые задачи (worker): expire_invitations каждый час, review_reminders ежедневно в 09:00 по Москве, purge_deleted еженедельно, cleanup_sessions ежедневно, send_notifications по очереди (Telegram, почта), rebuild_backlinks по событию публикации.
20. Тестирование и приёмка
20.1. Уровни
TST-01 Модульные тесты бэкенда (pytest, pytest-asyncio) на сервисы и authz; интеграционные — через httpx.AsyncClient против тестовой базы (Postgres в docker, как plibvi-test-pg); фронтенд — vitest для утилит фильтров и сериализации markdown; e2e — Playwright на Node 22 (/opt/node22/bin/node) по сценариям 20.3.
TST-02 Табличный тест прав: для каждой роли и каждого действия из матрицы 5.3 — ожидаемый результат; плюс отдельные случаи RBAC-07, RBAC-10…RBAC-14. Изменение матрицы без изменения теста — ошибка сборки.
TST-03 Тесты приглашений: одноразовость, истечение, отзыв, командная ссылка с лимитом, принятие существующим аккаунтом, повышение роли запрещено, лимиты частоты.
TST-04 Тесты доски: перенос между колонками меняет состояние; перенос в объединённую колонку ставит первое состояние; ранги без коллизий при 1000 перестановок; конфликт версий даёт 409.
TST-05 Тесты базы знаний: перенос поддерева, запрет цикла, наследование ограничения, конфликт версий, автосохранение черновика, ревизии и откат, импорт папки plibvi/kb (фикстура из 30 файлов) без дублей при повторном запуске.
TST-06 Тесты поиска: русская морфология («карусели» находит «карусель»), опечатка в заголовке, права в выдаче, точный ключ.
20.2. Целевые показатели
| Показатель | Цель | Как измеряем |
|---|---|---|
| Список 50 задач с фильтрами, сервер | ≤ 150 мс p95 | тест с 5 000 задач |
| Доска на 300 карточек, сервер | ≤ 300 мс p95 | API-09 |
| Доска на 300 карточек, первая отрисовка | ≤ 1 с на ноутбуке, ≤ 2 с на телефоне | Lighthouse, throttling 4G |
| Глобальный поиск | ≤ 200 мс p95 на 10 000 документов | SRCH-05 |
| Открытие статьи 100 КБ | ≤ 500 мс до текста | e2e |
| SSE: от изменения до обновления у соседа | ≤ 1 с | e2e с двумя контекстами |
| Память стека | ≤ 0,5 ГБ | docker stats |
20.3. Сценарии приёмки
Каждый сценарий проходится вручную на ноутбуке и телефоне перед выпуском версии и повторяется e2e-тестом.
- Владелец создан из консоли, входит, создаёт проект
TAZс иконкой; проект получил состояния, типы, доску «Канбан» и корневую статью. - Владелец создаёт персональное приглашение «Участник в TAZ, Читатель в TEAM», копирует ссылку; в приватном окне по ссылке создаётся аккаунт Ивана; Иван видит TAZ и TEAM, не видит DUSHA; вторая попытка по той же ссылке — «ссылка уже использована».
- Владелец создаёт командную ссылку на 4 использования с ролью Репортёр; три студента регистрируются; четвёртый — после отзыва ссылки — получает «ссылка отозвана».
- Иван создаёт задачу клавишей
c, вставляет скриншот из буфера, упоминает@Салам; Салам получает уведомление, открывает задачу из колокольчика. - На доске Салам тянет карточку в «В работе» при лимите 2/2 — видит подтверждение; у Ивана в другом браузере карточка переезжает сама в течение секунды.
- Репортёр Ольга создаёт задачу — она попадает во «Входящие»; администратор принимает её кнопкой, задача оказывается в конце Беклога.
- Иван пишет статью из шаблона «Инструкция», вставляет таблицу и
[[Бэкенд: архитектура]], публикует; в родительской статье появилась подстатья, в целевой — обратная ссылка; черновик, брошенный при закрытии вкладки, восстанавливается. - Администратор ставит на статью «Договор» ограничение; Иван перестаёт видеть её и подстатьи в дереве и в поиске; автор статьи видит по-прежнему.
- Импорт
plibvi/kbдаёт дерево из разделов00–90с сохранёнными ссылками и картинками; повторный импорт ничего не дублирует. - Поиск «карусели» находит
TAZ-128; вводtaz-128открывает задачу; поиск в проекте, где пользователя нет, ничего не показывает. - Владелец деактивирует пользователя — тот вылетает из сессии немедленно; его комментарии остались с именем.
- Всё то же с телефона: нижняя навигация, доска лентой, перенос карточки долгим нажатием и через меню, редактор с кнопкой отправки.
- Бэкап и восстановление на чистом compose: все задачи, статьи, вложения и история на месте, поиск работает после
reindex-search.
21. План разработки
Оценка — рабочие дни одного разработчика с Claude Code, при том что стек и части кода переносятся из nashpartner. Очереди выпускаются по одной; каждая заканчивается выкаткой на tracker.salam0nn.ru и проходом соответствующих сценариев 20.3.
| Очередь | Содержание | Требования | Дни |
|---|---|---|---|
| M0 Каркас и вход | репозиторий, compose, nginx и сертификат, страница «Скоро», модели пользователей, вход и сессии, владелец из консоли, приглашения обоих видов, страница приглашения, админка пользователей, аудит | AUTH, INV, RBAC-01…09, DEP, SEC | 4 |
| M1 Проекты и задачи | проекты, участники и роли, состояния, типы, метки, задачи, список с фильтрами и группировкой, страница задачи, комментарии, вложения, связи, подзадачи, история, упоминания, уведомления в приложении, «Моё» | DM, WF, ISS, LST, NTF, ACT | 6 |
| M2 Канбан | доски, колонки, WIP, политики, дорожки, перетаскивание, ранги, быстрое создание, SSE и присутствие, телефон | BRD, RT | 4 |
| M3 База знаний | дерево, статья, редактор, черновики, ревизии и диффы, ограничение, шаблоны, избранное, обратные ссылки, ответственный и пересмотр, импорт и экспорт markdown, комментарии | KB, ED | 6 |
| M4 Поиск и полировка | глобальный поиск и подсказки, массовые действия, экспорт CSV, корзина, горячие клавиши, тёмная тема, пустые состояния, e2e, бэкап и восстановление, README | SRCH, UI, TST, OPS | 3 |
| Итого v1 | 23 | ||
| v1.1 | текстовый запрос, персональные токены и MCP, Telegram-бот, жёсткие WIP-лимиты, клонирование, XLSX, сохранённые фильтры | 6 | |
| v2 | пользовательские поля (JSONB со схемой на проект), группы и видимость по группам, совместное редактирование, inline-комментарии, кросс-проектные доски | по решению |
Первый пригодный к работе результат — после M1 (10 дней): задачи и роли уже есть, база знаний временно остаётся в markdown. Если нужно раньше показать команде именно базу знаний, M3 можно поменять местами с M2: они независимы.
22. Открытые вопросы и допущения
Ни один вопрос не блокирует M0. Ответы уточняют M1–M3.
- Ключи и состав проектов. Предлагаются
TAZ(tazzz.ru, auto-parser),DUSHA(«По душе» / «По любви», plibvi),HORECA(nashpartner),MAX(хакатон),TEAM(команда). Подтвердить названия и кто в каждом администратор. - Импорт
plibvi/kbпри запуске. База лежит вне git из-за договора и финансов. Предлагается импортировать целиком, а10-project/(договор, заказчик) иsources/пометить «только администраторы». Подтвердить, что это достаточная защита, или оставить эти разделы вне трекера. - Хакатонная команда. Нужен ли студентам доступ в
MAXкак Участникам (двигают карточки) или как Репортёрам; и удалять ли их аккаунты после финала 29 октября (предлагается деактивация). - Мессенджер для уведомлений. Telegram-бот (у plibvi уже есть опыт) или MAX-бот (по мотивам хакатона). Влияет только на v1.1.
- Копия бэкапов вне сервера. Куда: Яндекс Object Storage (уже используется в auto-parser для фото) или на макмини по Tailscale. Без ответа бэкапы остаются на том же диске.
- Почтовый сервер. Если появится SMTP (например, для сброса пароля без участия Владельца), включаются письма-приглашения и подтверждение почты; в v1 не нужен.
- Ассистент как пользователь. Подтвердить, что бот-пользователь
claudeс ролью Участник во всех проектах и персональным токеном (v1.1) — приемлемо, и не нужен ли ему более узкий доступ.
Допущения, принятые без вопроса: команда до 10 человек; один инстанс без мультитенантности; русский язык интерфейса; все проекты приватные; фиксированный набор полей задачи; переходы состояний без ограничений; один редактор статьи за раз; ограничение видимости только «администраторы проекта», без групп.
23. Глоссарий
Словарь ляжет в CONTEXT.md репозитория в формате команды («термин, определение, чего избегать»).
| Термин | Определение | Избегать |
|---|---|---|
| Инстанс | Одна установка трекера со своими пользователями и проектами | воркспейс, тенант |
| Проект | Контейнер с ключом, участниками, состояниями, типами, метками, досками и деревом статей | очередь, пространство |
| Ключ проекта | 2–8 заглавных латинских букв и цифр, например TAZ |
код, префикс |
| Задача | Единица работы в проекте с ключом TAZ-128 |
тикет, issue, таск |
| Подзадача | Задача с родителем; своих подзадач не имеет | сабтаск |
| Эпик | Тип задачи, который объединяет другие задачи как родитель | инициатива, фича |
| Состояние | Положение задачи в рабочем процессе; у состояния есть категория | статус, стадия |
| Категория состояния | Одна из четырёх: не начато, в работе, готово, отменено | резолюция |
| Нерешённая задача | Задача в состоянии категории «не начато» или «в работе» | открытая |
| Входящие | Необязательное состояние для триажа внешних задач | инбокс, интейк |
| Тип | Задача, Ошибка, Улучшение, Эпик и добавленные в проекте | вид, класс |
| Приоритет | Критический, Высокий, Обычный, Низкий | важность, severity |
| Исполнитель | Пользователь, отвечающий за задачу сейчас | ассайни, ответственный (это термин статьи) |
| Автор | Кто создал задачу, комментарий или статью | репортёр (это роль) |
| Наблюдатель | Кто получает уведомления по задаче | подписчик, вотчер |
| Метка | Цветной ярлык проекта на задаче или статье | тег, лейбл |
| Связь | Направленное отношение между задачами: связана, блокирует, дублирует | линк |
| Доска | Представление задач проекта колонками по состояниям | борд, канбан (это метод) |
| Колонка | Одно или несколько состояний на доске с лимитом и политикой | статус доски |
| Лимит WIP | Минимум и максимум карточек в колонке | ограничение |
| Политика колонки | Короткое правило «что значит, что задача здесь» | definition of done (шире) |
| Дорожка | Горизонтальное деление доски: по исполнителю, приоритету, типу, эпику | свимлейн |
| Карточка | Задача на доске | тикет |
| Ранг | Ручной порядок карточек в колонке | позиция, вес |
| Статья | Документ базы знаний с ключом TAZ-A-7 |
страница, документ, заметка |
| Подстатья | Статья с родителем; дерево без ограничения глубины | дочерняя страница, папка |
| Корневая статья | Статья-индекс проекта, одна точка входа | главная |
| Черновик | Несохранённые правки статьи или неопубликованная статья | драфт |
| Ревизия | Сохранённая версия статьи | версия (у задач — version для блокировки) |
| Ответственный | Человек, отвечающий за актуальность статьи | владелец (это глобальная роль) |
| Дата пересмотра | Срок, после которого статья помечается «пора пересмотреть» | дедлайн |
| Ограниченная статья | Статья «только для администраторов проекта» и автора | приватная, скрытая |
| Шаблон | Заготовка статьи из проекта TEAM |
темплейт |
| Роль | Владелец, Пользователь (глобально); Администратор проекта, Участник, Репортёр, Читатель (в проекте) | группа, уровень доступа |
| Право | Код действия из матрицы 5.3, например issue.state |
пермишен |
| Приглашение | Ссылка с токеном, ролью и проектами; персональная или командная | инвайт, регистрация |
| Сессия | Вход пользователя в браузере, cookie плюс запись в Redis | токен (это про API) |
| Токен API | Персональный ключ для скриптов и ассистента (v1.1) | API key |
| Активность | История изменений объекта и лента проекта | лог (это про аудит) |
| Аудит | Журнал действий безопасности Владельца | активность |
24. Источники
Документация YouTrack (JetBrains), проверено 9 сентября 2026:
- YouTrack's New Design (2025), YouTrack Introduces a New Design, More Design Improvements (2025.2)
- The Issues List, Single Issue View, Sample Search Queries
- Agile Board Settings, Limit Work in Progress
- Knowledge Base, Viewing Articles, Organize Articles, Manage Article Access
- Default Roles, Permission Comparison for Default Roles, Private and Public Projects, Manage Project Members and Access
- Create User Accounts, Default Custom Fields
Практики:
- Plane: Roles and permissions, Plane: Permissions matrix
- Codify: SaaS team invitation system, DEV: identity verification gates invite acceptance
- Atlassian: WIP limits, Businessmap: What is WIP, ProKanban: does every column need a WIP limit
- Productlane: internal knowledge base, Knowledge base information architecture, Diátaxis
- Frontegg: access control matrix, NocoBase: how to design an RBAC system
- PostgreSQL: pg_trgm, OneUptime: full-text search in PostgreSQL
- Real-time notifications with FastAPI, Redis and Postgres, FastAPI streaming APIs for real-time dashboards
- Replacing EAV with JSONB in PostgreSQL, Atlassian: issue key counter
Свои материалы: Анатомия трекера и базы знаний (8 сентября), plibvi/kb/README.md, nashpartner (стек и деплой), /etc/nginx/sites-available/llm.salam0nn.ru (шаблон vhost), память о падениях панели от чужого pkill.