task(review): independent review done, findings filed

closes board-viewer-review

Subagent code-review against design spec + acceptance criteria found:
- 2 Major: README emoji legend stale (board-viewer-readme-emoji-fix),
  card meta missing owner pill + last-commit-marker (board-viewer-card-meta-fix)
- 6 minor/nitpick bundled as board-viewer-polish

All shipped code: 43/43 tests pass, TDD-immutability clean, secret hygiene OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
vitya
2026-05-22 15:16:36 +03:00
parent e4e1fa1254
commit 9dda6298f0
5 changed files with 169 additions and 7 deletions

View File

@@ -33,11 +33,34 @@ _Updated: 2026-05-22_
---
## 🔵 [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
## 🟢 [board-viewer-review] — review checkpoint (umbrella)
**Status:** done
**Where I stopped:** independent review выполнен subagent'ом (clean context, не имплементер). 43/43 тестов pass, secret hygiene clean, TDD-immutability чисто. 2 Major + 6 minor/nitpick findings зафайлены как follow-up таски ниже.
**Next action:** взять одну из ⚪ fix-тасок (readme-emoji или card-meta).
**Branch:** master
---
## ⚪ [board-viewer-readme-emoji-fix] — fix stale emoji legend in README.md
**Status:** ready
**Where I stopped:** review нашёл; trivial 1-line fix.
**Next action:** обновить `README.md:5` на `⚪ open / 🔴 in-progress / 🟡 paused / 🔵 blocked / 🟢 done` + заполнить TBD "Quick start" блок.
**Branch:** master
---
## ⚪ [board-viewer-card-meta-fix] — add owner pill + last-commit-marker to cards
**Status:** ready
**Where I stopped:** review нашёл acceptance gap — `project_owner` есть в JSON но не выводится; `last_commit_iso` только в relative age, не абсолютной дате.
**Next action:** TDD render + симметрично client-side board.js. См. `board-viewer-card-meta-fix.md`.
**Branch:** master
---
## ⚪ [board-viewer-polish] — bundle of 6 minor/nitpick findings from review
**Status:** ready
**Where I stopped:** detailed list в task-файле; парсер comment, formatAge future-ts test, gitea-reader decisions-log clarification, filter status multi-select, docker healthcheck+stderr, parallel commit-iso.
**Next action:** забирать items по одному. См. `board-viewer-polish.md`.
**Branch:** master
---

View File

@@ -0,0 +1,43 @@
# board-viewer-card-meta-fix
## Goal
Card meta сейчас содержит **title + project + age**. Acceptance criteria `board-viewer-html-render.md` строка 19-24 явно перечисляет 5 полей:
- title (труб 80 chars) ✅
- project badge ✅
- **owner (если есть) — pill** ❌ (поле `project_owner` есть в TaskRecord и JSON, но не выводится)
- age (relative) ✅
- **last-commit-marker — короткий hash или дата** ❌ (есть только relative age от `last_commit_iso`, абсолютной даты/хэша нет)
html-render таска закрылась без этих полей — gap. Не было замечено при self-review (потому и нужен independent reviewer).
## Key files
- `src/render.ts:111-127``renderCard()` (server-side первая рендер-проходка)
- `static/board.js:76-89``renderCard()` (client-side rerender при toggle-grouping)
- `tests/render.test.ts` — добавить assertions для двух новых полей
- `static/board.css` — стиль для `.badge.owner` если нужно отличать визуально
## Acceptance criteria
- Card showing `project_owner` as a pill (CSS class `.badge.owner`), скрыт если поле пустое.
- Card showing last-commit-marker: minimum — ISO date `YYYY-MM-DD` (короткая дата, NOT relative). Hash недоступен из API без extra call → opt for date.
- Возможность размещения: либо как третий meta-span после age, либо как tooltip на age (`title="<iso>"`); решение имплементера.
- 2+ TDD tests:
- card with `project_owner` shows owner pill containing the value
- card with `last_commit_iso` shows date (e.g. `2026-05-22`) — separate from relative age
- Обе ветки (server-side `src/render.ts` + client-side `static/board.js`) синхронны.
## Decisions log
- 2026-05-22: задача из review-чекпойнта; severity=major (acceptance gap, не self-replied).
- Open: owner pill всегда показывать или только при variability (когда есть >1 owner в выборке)? — рекомендация: всегда показывать когда поле не null (нет смысла фильтровать на rendering этапе).
## Completed steps
- [ ] (фиксируется при выполнении)
## Notes
TDD obligatory — render логика, не visual. Тестируется через DOM-property assertions (как в `render.test.ts`).

View File

@@ -0,0 +1,60 @@
# board-viewer-polish
## Goal
Bundle of minor / nitpick findings from `board-viewer-review` checkpoint. Each — small, individually below the task-creation threshold; collectively worth doing in one sweep.
## Items
### M1. Document parser's `**Status:**` field neglect
- **Location:** `src/parser.ts` (in `parseOneBlock`)
- **Problem:** `**Status:**` text field is matched by `FIELD_RE` but never stored in `StatusBlock` (emoji is SoT). Future maintainer reads parser and wonders why.
- **Fix:** comment `// **Status:** text field intentionally ignored — emoji is canonical` near where it's matched, OR `delete fields['Status']` after parse to avoid the orphan field in local var. Choose the comment.
### M2. `formatAge` future-timestamp behavior is undocumented
- **Location:** `src/render.ts:31` + `static/board.js:49`
- **Problem:** `Math.max(0, …)` silently clamps future ISO to "0m". With server-clock drift на VDS vs Gitea это маскирует ошибку синхронизации.
- **Fix:** add one test asserting `formatAge(futureIso, now) === '0m'` to lock behavior, and add inline comment "// clamps to '0m' on future iso — see test".
### M3. `gitea-reader` acceptance "per-task без записи в STATUS.md (legacy)" is impossible by design
- **Location:** `.tasks/board-viewer-gitea-reader.md` Decisions log
- **Problem:** Acceptance line 42 promises tests for "per-task без записи в STATUS.md (legacy)" case, but the reader iterates STATUS.md blocks only — orphan per-task files are invisible by design (matches spec: STATUS.md is the index).
- **Fix:** append a decisions-log entry to `board-viewer-gitea-reader.md` clarifying this is intentional; STATUS.md is the entry point, orphan per-task files are out of scope.
### M4. Client filter doesn't match status in by-status mode; "multi-select по статусам" not implemented
- **Location:** `static/board.js:139-147` (`applyFilter`)
- **Problem:** Filter reads `.badge.status` / `.badge.project` text from DOM. In by-status mode there's no status badge → typing "blocked" matches nothing. Также acceptance в `board-viewer-html-render.md:27` обещает multi-select по статусам — не реализован.
- **Fix:** rebuild `applyFilter` to query the underlying `records` array (status always available regardless of view mode). Decide on multi-select: either ship a status-pill filter row, or amend acceptance criteria. Recommendation: add multi-select pills below the filter input — simple `<button data-status="…" class="status-filter">` toggle.
### M5. Build container failures show only `echo` output, no Docker healthcheck
- **Location:** `deploy/Dockerfile.build:14`
- **Problem:** `... || echo 'render failed; will retry next tick'` swallows non-zero exit. Operator must `docker compose logs build` to notice. No Docker healthcheck signals unhealthy.
- **Fix:** emit failure to stderr (`>&2 echo`), and add a `HEALTHCHECK` to Dockerfile that checks `[ -f /output/index.html ]` AND `[ $(date +%s) - $(stat -c %Y /output/index.html) -lt 600 ]` (file exists AND fresher than 10 min). Compose `web` service can `depends_on` health.
### M6. `getLatestCommitIso` called serially, N=80+ per tick
- **Location:** `src/reader.ts:36-37`
- **Problem:** `for...of await` serializes 80+ HTTP calls. Within budget for now but a Gitea hiccup serializes through all of them.
- **Fix:** parallelize per-repo or globally with `Promise.all(blocks.map(...))`. Cap concurrency at ~10 if politeness is a concern.
## Decisions log
- 2026-05-22: bundle создан промоушеном из review-чекпойнта; severity варьируется minor → nitpick; bundled, чтобы не плодить 6 микро-тасок.
## Completed steps
- [ ] M1: parser comment
- [ ] M2: formatAge future-ts test + comment
- [ ] M3: decisions-log entry in gitea-reader.md
- [ ] M4: filter rebuild + status multi-select OR acceptance amendment
- [ ] M5: stderr + Docker HEALTHCHECK
- [ ] M6: parallelize commit-iso fetches
## Notes
Каждый item — TDD по тому, что есть testable. M1/M3 — comment/docs only ([skip-tdd: visual]). M2 — pure test. M4/M5/M6 — TDD logic.

View File

@@ -0,0 +1,28 @@
# board-viewer-readme-emoji-fix
## Goal
Fix stale emoji legend в `README.md`. README — первое, что видит человек открывая repo. Сейчас он указывает старый emoji-набор `⚪ 🟡 🟣 🔴 🟢` (с фиолетовым и красным=blocked), который был исправлен mid-stream на корректный `⚪ 🔴 🟡 🔵 🟢` (everywhere else в код-базе).
Найдено в review-checkpoint (`board-viewer-review`).
## Key files
- `README.md` строка 5 — "About" с emoji-словарём
## Acceptance criteria
- README.md строка 5 содержит корректный emoji-словарь: `⚪ open / 🔴 in-progress / 🟡 paused / 🔵 blocked / 🟢 done`
- Заодно — заполнить TBD блок "Quick start" одной командой: `npx vitest run` + `node --experimental-strip-types src/cli.ts <auth-toml-path>`
## Decisions log
- 2026-05-22: findings зафайлены в review-чекпойнте; severity=major (стартовое впечатление, рассинхрон со всем кодом).
## Completed steps
- [ ] (фиксируется при выполнении)
## Notes
Trivially small fix — `[skip-tdd: visual]` (docs).

View File

@@ -38,14 +38,22 @@ Code-review checkpoint для брейнсторма `board-viewer` (промо
## Decisions log
- 2026-05-22: review-task создан промоушеном; status=blocked, blocker = все 4 импл-таски.
- 2026-05-22: review выполнен subagent'ом (general-purpose, clean context). 43/43 тестов pass, TDD-immutability clean (no `[test-modify:]` markers needed — diffs append-only), secret hygiene OK (`02a14e` grep clean across `deploy/`). 2 Major findings + 6 minor/nitpick — зафайлены: `board-viewer-readme-emoji-fix`, `board-viewer-card-meta-fix`, `board-viewer-polish` (бандл).
- 2026-05-22: findings зафайлены LOCAL в `.tasks/` (project-discipline Rule 1 override), не через `mcp__projects-meta__tasks_create` (это локальные таски, не cross-project).
## Open questions
- [ ] (заполняется ревьюером по ходу)
- [x] (нет — все findings зафайлены)
## Completed steps
- [ ] (фиксируется при выполнении)
- [x] спецификация прочитана subagent'ом
- [x] `git log --oneline` сверен с acceptance criteria каждой импл-таски
- [x] test suite прогнан (43/43 pass)
- [x] design ↔ shipped contract проверен
- [x] TDD-immutability audit (clean)
- [x] secret hygiene grep (clean)
- [x] findings зафайлены follow-up тасками
## Notes