promote: workshop/board-viewer brainstorm → concepts + 5 tasks

- concepts/board-viewer.md (design, promoted from .workshop)
- .tasks/STATUS.md + 5 per-task files:
  - board-viewer-pointers (pre-impl, status=ready)
  - board-viewer-gitea-reader (impl, TDD, status=ready)
  - board-viewer-html-render (impl, TDD, blocker=gitea-reader)
  - board-viewer-cron-deploy (impl, infra carve-out, blocker=html-render)
  - board-viewer-review (umbrella, status=blocked)

TDD-mode embedded in impl-tasks per follow tdd-criteria trigger in CLAUDE.md.
MCP write-side workaround per memory reference_projects_meta_resolveTarget_bug.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
vitya
2026-05-22 08:21:59 +03:00
parent d0dc686a65
commit 27465089b5
9 changed files with 414 additions and 15 deletions

View File

@@ -1,21 +1,51 @@
# Task Board
_Updated: 2026-05-22_
## ⚪ [board-viewer-pointers] — pre-impl: fill Domain conventions with design-context pointers
**Status:** ready
**Where I stopped:** только что промочен дизайн из `.workshop/.brainstorm/board-viewer.md` в `concepts/board-viewer.md`; `.wiki/CLAUDE.md` ещё содержит дефолтный setup-wiki stub в Domain conventions, без ссылок на дизайн.
**Next action:** вставить блок «Mandatory: read design context before implementation» в `.wiki/CLAUDE.md` Domain conventions (готовый текст в `board-viewer-pointers.md`), коммит `wiki(claude): add design-context pointers for board-viewer`, push.
**Branch:** master
---
## ⚪ [board-viewer-gitea-reader] — Gitea API reader → normalized task structure
**Status:** ready
**Where I stopped:** дизайн зафиксирован, реализация не начата. Это первая импл-таска, остальные impl-таски зависят от её типов.
**Next action:** прочитать `board-viewer-pointers.md` next-action блок → выполнить `board-viewer-pointers` сначала; затем стартовать с TDD по контракту в `board-viewer-gitea-reader.md`.
**Blocker:** board-viewer-pointers (нет Domain conventions с указанием на спецификацию)
**Branch:** master
---
## ⚪ [board-viewer-html-render] — render 5-col kanban from normalized structure
**Status:** ready
**Where I stopped:** ждёт `board-viewer-gitea-reader` (использует его типы).
**Next action:** после ready-стейта reader'а — TDD по контракту в `board-viewer-html-render.md`.
**Blocker:** board-viewer-gitea-reader
**Branch:** master
---
## ⚪ [board-viewer-cron-deploy] — systemd timer + traefik + DNS on VDS
**Status:** ready
**Where I stopped:** ждёт `board-viewer-html-render` (нужен артефакт для деплоя).
**Next action:** после готового HTML-рендера — добавить traefik label, DNS A-запись `board.kzntsv.site`, systemd timer на ~5 мин, basic-auth через traefik. См. `board-viewer-cron-deploy.md`.
**Blocker:** board-viewer-html-render
**Branch:** master
---
## 🔵 [board-viewer-review] — review checkpoint (umbrella)
**Status:** blocked
**Where I stopped:** дизайн зафиксирован, импл ещё не начата.
**Next action:** дождаться 🟢 у всех blocker-тасок (включая `board-viewer-pointers`). Прочитать спецификацию `.wiki/concepts/board-viewer.md`. Для каждой импл-таски: `git log --oneline`, прогнать тесты, сверить с acceptance criteria. Findings → follow-up tasks через `mcp__projects-meta__tasks_create`.
**Blocker:** board-viewer-pointers; board-viewer-gitea-reader; board-viewer-html-render; board-viewer-cron-deploy
**Branch:** master
---
<!--
Add one block per task, sorted by priority. Use the emoji status legend below.
Per-task deep context lives in .tasks/<task-slug>.md (created on demand by using-tasks).
Block format:
## 🔴 [task-slug] — short description
**Status:** active
**Where I stopped:** one sentence — the exact thought or action interrupted
**Next action:** one concrete step to resume immediately
**Blocker:** (only if blocked) what is preventing progress
**Branch:** git branch name
---
Status legend:
🔴 Active — only one at a time
🟡 Paused — in progress, resumable

View File

@@ -0,0 +1,44 @@
# board-viewer-cron-deploy
## Goal
Деплой статической HTML-доски на VDS под доменом `board.kzntsv.site`. Periodic regen через systemd timer ~5 мин. Traefik label для роутинга. DNS A-запись. Basic-auth через traefik (не in-app).
**TDD posture:** под carve-out «infrastructure / declarative» из `tdd-criteria`. Acceptance проверяется по факту deploy + curl, не unit-тестами. systemd unit-файлы и traefik labels — декларативные конфиги, не код.
## Key files
- (TBD) `deploy/board-viewer.service` — systemd one-shot unit (runs reader + render → writes to `/var/www/board-viewer/`)
- (TBD) `deploy/board-viewer.timer` — systemd timer (OnUnitActiveSec=5min)
- (TBD) `deploy/docker-compose.yml` фрагмент или `deploy/traefik-labels.yaml` — traefik label для статики
- (TBD) `deploy/README.md` — install steps + DNS record requirements
- (TBD) `deploy/auth/htpasswd` — basic-auth credentials (gitignored, with `.htpasswd.example`)
## Acceptance criteria
- DNS `board.kzntsv.site` → VDS IP (Cloudflare / namecheap / wherever DNS живёт).
- Traefik route: `board.kzntsv.site` → static file serving из `/var/www/board-viewer/`.
- Basic-auth на traefik (middleware), креды из `.htpasswd`.
- systemd timer крутится каждые 5 мин, успешно дёргает reader + render, пишет в `/var/www/board-viewer/index.html`.
- Логи systemd видны через `journalctl -u board-viewer.service`.
- Health check: `curl -u user:pass https://board.kzntsv.site/` → HTML с актуальной доской.
- README.md описывает install steps (для disaster-recovery: «как поднять с нуля на новой машине»).
## Decisions log
- 2026-05-22: task создан промоушеном; carve-out из TDD под инфра (declarative configs).
- 2026-05-22: auth = traefik basic-auth, не in-app (фиксировано в дизайне).
## Open questions
- [ ] Хост: VDS Rusonyx (тот, где уже Gitea + traefik) или отдельная машина? — VDS, рядом с git.kzntsv.site (см. дизайн).
- [ ] Multi-user basic-auth или single creds? — пока single, поскольку single-user проект.
- [ ] Rate-limit на Gitea API: 5-мин тик * N репо * M файлов — оценить, не упрётся ли. Gitea дефолт ~60 req/min anon, токенизированный сильно выше — должно хватить с запасом.
## Completed steps
- [ ] (фиксируется при выполнении)
## Notes
Зависит от `board-viewer-html-render` — нужен артефакт для деплоя (хотя бы placeholder). Можно стартовать параллельно с DNS-записью (она прогревается).

View File

@@ -0,0 +1,59 @@
# board-viewer-gitea-reader
## Goal
Модуль, который через Gitea API читает `.tasks/STATUS.md` + per-task `<slug>.md` файлы из всех релевантных репо (`OpeItcLoc03/agenda` + per-project репо из `~/.config/projects-mcp/auth.toml::gitea_owners`) и нормализует в типизированную структуру для рендера.
Это первая импл-таска. Остальные импл-таски (`board-viewer-html-render`, `board-viewer-cron-deploy`) зависят от типов, которые этот модуль публикует.
**TDD-mode:** обязательно по `follow tdd-criteria` триггеру в `.wiki/CLAUDE.md`. Сначала тесты на нормализацию пар фикстур (STATUS.md + per-task.md) → потом реализация. Не модифицировать assert'ы во время реализации без `[test-modify: ...]` маркера в commit subject.
## Key files
- (TBD) `src/gitea-reader.{ts,py}` — main reader module
- (TBD) `src/types.{ts,py}` — exported task structure
- (TBD) `tests/fixtures/` — sample STATUS.md + per-task.md pairs from real repos
- (TBD) `tests/gitea-reader.test.{ts,py}` — TDD assertions
## Acceptance criteria
- **Output contract:** массив объектов с полями:
```
{
slug: string,
project: string, // repo name (e.g. "board-viewer", "books")
project_owner: string, // gitea owner (e.g. "OpeItcLoc03")
status: "open" | "in_progress" | "paused" | "blocked" | "done",
status_emoji: "⚪" | "🟡" | "🟣" | "🔴" | "🟢",
title: string, // from STATUS.md block H2 after slug
where_stopped: string | null,
next_action: string | null,
blocker: string | null,
branch: string | null,
last_commit_iso: string | null, // from Gitea API, latest commit touching the task file
raw_url: string, // Gitea raw URL to per-task <slug>.md for drawer lazy-fetch
}
```
- **Inputs:** список репо из конфига (env var или TOML).
- **Auth:** Gitea token из `~/.config/projects-mcp/auth.toml::gitea_token` (admin scope, читает любой репо).
- **Robustness:** репо без `.tasks/` → пропустить молча (не ошибка). `.tasks/STATUS.md` без emoji-блоков → пустой массив для этого проекта.
- **Tests:** фикстуры из ≥3 реальных репо (board-viewer, books, .workshop), assertions покрывают: нормальный case, status без per-task.md (только STATUS.md block), per-task без записи в STATUS.md (legacy), blocked-таски с `Blocker:` строкой.
- **Single language:** один из TS / Python. Выбор — за имплементером (TS легче переиспользует с HTML-рендером в одном процессе; Python проще читать конфиг).
## Decisions log
- 2026-05-22: task создан промоушеном; контракт фиксирован — это публичный API для html-render.
## Open questions
- [ ] TS or Python? (повлияет на структуру cron-deploy).
- [ ] Owner-фильтр: брать репо из `gitea_owners` целиком или whitelist? Возможно нужен `board_viewer_repos` отдельным полем в auth.toml.
- [ ] Кэшировать ответы Gitea API локально между cron-тиками? (опт.: rate-limit, hot-reload).
## Completed steps
- [ ] (фиксируется при выполнении)
## Notes
NB: исходный буфер упоминал `mcp__projects-meta__tasks_aggregate` как возможный источник — **это ложная развилка**, MCP недоступен HTTP-сервису. Только прямой Gitea API. См. `.wiki/concepts/board-viewer.md` секцию «Implementation note».

View File

@@ -0,0 +1,48 @@
# board-viewer-html-render
## Goal
Рендер 5-колоночного kanban-board из нормализованной структуры (продукт `board-viewer-gitea-reader`). Статический HTML + минимальный клиент-сайд JS для фильтра / drawer / refresh-таймера. Без write-side, без realtime.
**TDD-mode:** обязательно по `follow tdd-criteria`. Снапшот-тесты на HTML-output из фиксированных reader-фикстур. Не модифицировать assert'ы без `[test-modify: ...]` маркера. Визуальная отделка (CSS) — под carve-out «visual CSS», но логика рендера (распределение по колонкам, age-расчёт, drawer-привязка) — TDD.
## Key files
- (TBD) `src/render.{ts,py}` — main renderer
- (TBD) `templates/board.html` — template (Mustache / Jinja / template literals — выбор имплементера)
- (TBD) `static/board.css`, `static/board.js` — styling + filter/drawer client-side
- (TBD) `tests/render.test.{ts,py}` — snapshot tests
## Acceptance criteria
- **5 колонок** по статусам: ⚪ open / 🟡 in-progress / 🟣 paused / 🔴 blocked / 🟢 done. Названия колонок — emoji + слово.
- **Карточка:**
- title (truncate на 80 chars в default-view),
- project badge (e.g. `books`, `board-viewer`),
- owner (если есть) — pill,
- age — относительное (`3d`, `2w`, `5mo`) от `last_commit_iso`,
- last-commit-marker — короткий hash или дата.
- **Группировка по проекту** — toggle вверху: «по статусам» (5 колонок × проекты внутри) vs «по проектам» (per-project колонки × статусы внутри).
- **Поиск/фильтр** — клиент-сайд input: substring по `slug`, `title`, `project`; multi-select по статусам.
- **Per-card drawer** — клик по карточке → side panel с full markdown из per-task `<slug>.md` (lazy-fetch из `raw_url`). Markdown рендерится клиент-сайд (e.g. `marked` для TS, `markdown-it` для Py-side-rendered).
- **Refresh-таймер** видно в header: «refreshed Xm ago, next in Ym» (data attribute из generation timestamp).
- **Output:** один `index.html` файл + sidecar `static/`. Открывается локально без сервера для smoke-test.
- **Tests:** snapshot-тесты на 3 reader-фикстуры (пустая доска / 1-2 таски / N≥10 тасок). Snapshots checked-in.
## Decisions log
- 2026-05-22: task создан промоушеном; зависит от `board-viewer-gitea-reader` контракта.
## Open questions
- [ ] Markdown-renderer: client-side (`marked`) или server-side (Jinja с pre-rendered HTML вшито в data-attr)? Client-side проще — но добавляет ~50KB JS.
- [ ] CSS framework: ничего (raw CSS) / Pico / Tailwind via CDN? Минимализм — за raw. Раздёргать через первый прототип.
- [ ] Архивные таски (🟢 done старше N дней) скрывать по умолчанию? Toggle?
## Completed steps
- [ ] (фиксируется при выполнении)
## Notes
Стиль: минимализм без аватарок/гифок/анимаций. Это инструмент глядеть на доску, не дашборд для презентации. Whitespace + типографика.

View File

@@ -0,0 +1,57 @@
# board-viewer-pointers
## Goal
Pre-impl bootstrap: заполнить `.wiki/CLAUDE.md` секцию «Domain conventions» pointer-блоком на спецификацию. Дизайн не лежит в этом репо целиком — только pointer-stub. Без этой таски следующий агент попадёт в дыру: dense `where_stopped` one-liner + пустой Domain conventions stub = угадывание архитектуры вместо чтения готовых решений.
**Кто делает:** любой следующий агент в этом проекте. Это **первая** по приоритету таска промоушена — все импл-таски ссылаются на pointers через `.wiki/CLAUDE.md`.
## Key files
- `.wiki/CLAUDE.md` — Domain conventions section
- `.wiki/concepts/board-viewer.md` — canonical design (target of pointer)
- `~/projects/.workshop/.archive/2026-05-22-board-viewer.md` — brainstorm rationale (target of pointer)
## Acceptance criteria
- `.wiki/CLAUDE.md` содержит блок «Mandatory: read design context before implementation» в Domain conventions (см. Next action ниже — дословный текст).
- Коммит с message `wiki(claude): add design-context pointers for board-viewer`.
- Push в `origin/master`.
## Next action (готовый блок для копирования в `.wiki/CLAUDE.md` Domain conventions)
Заменить существующий комментарий-stub в секции `## Domain conventions` на следующий блок:
```markdown
### Mandatory: read design context before implementation
Before picking up any task in `.tasks/`, load the full design context. It does **not** live in this repo fully — only pointers do. Sources, in order:
1. **Canonical design:** `.wiki/concepts/board-viewer.md`. Architecture decisions, scope, MVP feature list, anti-patterns (hermes-style, Gitea Projects), trade-offs.
2. **Brainstorm process trace (rationale):** `~/projects/.workshop/.archive/2026-05-22-board-viewer.md`. Why each decision was made, what was rejected and why, clarifying note that `mcp__projects-meta__*` is not callable from HTTP service.
3. **Local `overview.md`** — quick orientation summary; never source of truth.
Do **not** invent thresholds, taxonomies, container topology, or pipeline stages from task `where_stopped` lines alone — those are pointers, not specifications.
### TDD posture
Per `follow tdd-criteria` trigger in this CLAUDE.md: impl-tasks `board-viewer-gitea-reader` and `board-viewer-html-render` are TDD. `board-viewer-cron-deploy` falls under the infrastructure carve-out (declarative systemd / traefik / DNS — verify by deploy + curl, not unit tests). Mark each impl commit accordingly.
```
После вставки: коммит, push.
## Decisions log
- 2026-05-22: pointers task создан промоушеном из `.workshop/.brainstorm/board-viewer.md` (workshop-promote-brainstorm).
## Open questions
- [ ] (нет — pointer-блок самодостаточен)
## Completed steps
- [ ] (фиксируется при выполнении)
## Notes
Pointer-таска намеренно тривиальная — её существование защищает следующего агента от чтения stub'а вместо спецификации. Не объединять с импл-тасками.

View File

@@ -0,0 +1,54 @@
# board-viewer-review
## Goal
Code-review checkpoint для брейнсторма `board-viewer` (промоушен 2026-05-22).
**Спецификация:** `.wiki/concepts/board-viewer.md`.
**Pre-impl bootstrap:** `board-viewer-pointers` (заполнил `.wiki/CLAUDE.md` Domain conventions — без него review бы читал stub).
**Импл-таски (review против их acceptance criteria):** `board-viewer-gitea-reader`, `board-viewer-html-render`, `board-viewer-cron-deploy`.
**Кто делает:** **не имплементер.** Следующая сессия в этом проекте (другая модель / другой день / другой агент) поднимает таску с чистым контекстом. «Я только что это написал» bias = главный риск.
## Key files
- `.wiki/concepts/board-viewer.md` — canonical design
- `.tasks/board-viewer-*.md` — acceptance criteria per impl-task
- `~/projects/.workshop/.archive/2026-05-22-board-viewer.md` — brainstorm rationale
## Acceptance criteria (для самого ревью)
- Прочитана спецификация целиком.
- `git log --oneline` shipped-коммитов (по slug или scope в commit-message) сверен с acceptance criteria каждой импл-таски.
- Для каждой импл-таски: прогнан соответствующий тест-suite, проверено что тесты реально доходят до своих веток (не coverage-illusion).
- Сверены дизайн-decisions со shipped-кодом: контракт reader → render, статика + cron на VDS, отсутствие write-side.
- Findings зафайлены как follow-up tasks (`board-viewer-<gap>-fix` или подобное) через `mcp__projects-meta__tasks_create`, либо ревьюер подтвердил «нет findings» в close-note.
## Чек-лист ревью (poll-выполнения)
- [ ] reader: контракт публичных полей сохранён (не сужен, не расширен молча)
- [ ] reader: фикстуры покрывают edge cases (legacy без per-task.md, blocked с blocker-строкой)
- [ ] render: snapshot-тесты обновлены вместе с изменениями (не закоммичен код без апдейта snapshot)
- [ ] render: визуально не AI-generic, минимализм соблюдён
- [ ] cron-deploy: systemd unit + timer проверены `systemctl status`, не только запущены
- [ ] cron-deploy: basic-auth проверена `curl` с верными / неверными кредами
- [ ] auth scope: пушится ли токен в публичный репо случайно? (`grep -r "02a14e" deploy/` должен быть пуст)
- [ ] disaster recovery: README.md в deploy/ достаточен чтобы поднять с нуля?
## Decisions log
- 2026-05-22: review-task создан промоушеном; status=blocked, blocker = все 4 импл-таски.
## Open questions
- [ ] (заполняется ревьюером по ходу)
## Completed steps
- [ ] (фиксируется при выполнении)
## Notes
**Закрытие:** только когда все findings зафайлены ИЛИ ревьюер подтвердил «нет findings» в close-note.
**TDD-immutability:** если ревью обнаружит модифицированные assert'ы без `[test-modify: ...]` маркера в commit subject — это нарушение `follow tdd-criteria`, finding обязателен.