closes board-viewer-polish
- M1 src/parser.ts: comment explaining why **Status:** text field is intentionally
ignored (emoji is canonical, prevents future-reader head-scratch)
- M2 src/render.ts + tests: clamp future ISO to '0m' (server clock drift safety)
+ inline comment + test
- M3 .tasks/board-viewer-gitea-reader.md: decisions-log clarifying orphan
per-task files are out of scope by design (STATUS.md = index of record)
- M4 static/board.{js,css} + src/render.ts: filter rebuilt to query records
(not DOM badge text); 5 status-filter pill buttons in header with active
toggle state; smoke-verified blocked filter
- M5 deploy/Dockerfile.build: HEALTHCHECK (file fresher than 2×tick) and
stderr redirect for failures with timestamp
- M6 src/reader.ts: parallelize getLatestCommitIso per-repo via Promise.all
(preserves block order)
49 tests pass (was 43 pre-polish), typecheck clean, smoke against real Gitea
returns 81 records from 4 repos.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
closes board-viewer-card-meta-fix
Review found two acceptance fields missing from cards:
- project_owner was in JSON but never displayed
- last_commit_iso was only consumed for relative age; no absolute date
Now both rendered (server-side src/render.ts + client-side static/board.js
in sync), with 4 new TDD tests:
- renders owner pill when project_owner is set
- omits owner pill when empty
- renders short YYYY-MM-DD date when last_commit_iso is set
- omits date span when null
CSS: .badge.owner = transparent + border (visually distinct from solid
.badge.project), .date = small muted tabular-nums. Smoke-verified in browser.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- static/board.js: full rerender on toggle-grouping; by-project mode = N cols
per project with status-badge inside cards, sorted by canonical status order
- static/board.css: drop placeholder, add .badge.status
- src/gitea.ts: getLatestCommitIso returns null on 404 (path never existed
remotely; seen on books repo where some per-task slugs in STATUS.md
don't have file in default branch) + new test
- .gitignore: dist/ already present; add .playwright-mcp/, board-*.png
Smoke-verified: dist/index.html, 78 records from 4 repos, both toggle modes
work in browser, drawer lazy-fetches markdown.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- src/cli.ts: load config, fetch board, write dist/index.html, copy static
- static/board.css: minimal raw CSS, system fonts, light/dark via prefers-color-scheme
- static/board.js: client-side filter, drawer (lazy-fetch + marked@14 from esm.sh),
toggle-archived, refresh-info; ESM module
- npm run build → runs cli with --experimental-strip-types
CSS/JS = [skip-tdd: visual]. CLI = plumbing wrapper (~30 LOC of pure FS+orchestration
calling already-tested functions; over 10-line wrapper-skip threshold but no branching
logic — would be tested via smoke / e2e, not unit).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>