Working checklist, not a committed FDP artifact. Check items off as they land.
Each item names the concrete gap between what we have now and the archived
reference (bash.org 2007 for base look, qdb.us 2016 for voting UX).
A. Highest-impact, CSS-only (scoped to :root[data-theme="qdb"])
B. Needs a template/JS change, not just CSS
C. New chrome the original had that we have none of
D. qdb.us refinements not yet carried over
_New threads.vote_count column, accumulated in both
ReadModelBuilder (full rebuild) and IncrementalReadModelUpdater
(incremental path): counts every distinct (identity, tag)
upvote/downvote event, not gated by approval (unlike
score_total) — this is "total votes cast by anyone," a genuinely
different, more honest number than the approval-gated score.
TagScore::isVoteTag() added so upvote/downvote are identified in one
place. ThreadRepository's two SELECTs and ThreadRowSupport updated
to carry it through._
_LocalWriteService::applyThreadTag() now also returns vote_count
(new currentThreadVoteCount() helper mirroring the score one);
TagApiController includes it in the AJAX response text._
_thread_reactions.js gained a third score format, bare-ratio
("(score/total)"), alongside the existing labeled/bare ones —
bindThreadReactions now parses vote_count from the response and
passes it through. quote_card.php sets data-score-format=
"bare-ratio" and renders (score/voteCount) server-side too._
_Verified end-to-end for real, not just structurally: applied a real
upvote via LocalWriteService using the fixture's real identity
(openpgp:0168ff..., which happens to be pre-approved there), got back
score_total=1 vote_count=1, and confirmed the board re-rendered
(1/1). This is more verification depth than Stage 4 managed for plain
upvote/downvote (which only got structural/code-path verification), so
this closes that earlier gap too._
_Schema note: this is a read-model (cache) column, not a canonical-data
migration — any already-running deployment just needs one ./v3
rebuild after this ships; existing canonical thread-label records are
re-aggregated from scratch with full fidelity, nothing is lost. Did
this myself for local dev's state/cache/post_index_qdb.sqlite3._
E. Lower priority / cosmetic
My suggested order: A (all CSS-only, biggest visual payoff, zero risk)
→ the color-coding and button-wording parts of B → C's footer/header →
then D/reopening the vote-ratio decision only if you still want it after
seeing A–C.
Two items need your decision before I touch them: hiding the modern board
chrome (B) and whether to reopen the vote-ratio display (D) — both change
things we deliberately scoped out in Step 2.
Post-checklist fixes
Orange-on-orange text on /tools/: the C2 "flatten the nav" item
added a blanket :root[data-theme="qdb"] a { color: var(--line) } so
plain links read orange. That unintentionally also applied to
anchor-styled buttons elsewhere in the app — .tool-launcher-button
(tools.css) sets its own background: var(--button-bg), which is the
same orange as --line in this theme, so the text became invisible.
Fixed by giving .tool-launcher-button (and .offline-health-open-archive,
same pattern, defensively) an explicit color: var(--active-ink) rather
than relying on the generic link rule. Checked every other
var(--button-bg)/var(--active-bg) background usage site-wide for the
same exposure — this was the only real instance.