feat: add /pace dashboard tracking artifact output vs Year 1 target#411
Merged
Conversation
Adds a public /pace dashboard that reads blog post and note frontmatter, classifies artifacts into battlesnake/other tracks, and renders server-side pace stats (count vs target, expected-by-now, need-per-week, weeks left) plus a chronological artifact list. Recomputed per request from AppState — no DB, no manual entry. Adds a `track` frontmatter field (defaulting to `other`) to both blog and note frontmatter, backfills the four existing Battlesnake posts, and links the dashboard from the site nav. Task: BLOG-84ffd942c4f94ead Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Replace invalid text-5xl with text-4xl (theme's largest defined size) - Render expected_by_now as a caption near the count (kills dead field) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The /pace dashboard stays public for linkability but is no longer advertised in the main site nav. It's reachable by URL and now linked from the admin dashboard so it's easy to find without typing the URL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Review Brief
What changed and why
Adds a public
/pacedashboard that reads blog post and note frontmatter to track artifact output toward two Year 1 targets (48 Battlesnake, 12 Other posts by June 1 2027). Introduces a typedTrackenum field on bothBlogFrontMatterandnotes::FrontMatter, a new server-side handler with pure stat helpers (in_window,compute_track_stats,render_track_section), and backfillstrack: battlesnakeon 4 existing posts. No DB writes, no migrations — everything recomputed on each request fromAppState.Architecture decisions and trade-offs
Trackis a closed enum withserde(rename_all = "lowercase")— unknown values liketrack: typohard-error at parse time rather than silently defaulting. Intentional per spec ("typed, not free-form").static LazyLock<NaiveDate>rather thanconst—NaiveDate::from_ymd_optis notconst fnin chrono 0.4.41;consthere would be a compile error (E0015).projected_finishis suppressed for the first 28 days (PROJECTION_MIN_DAYS) to avoid misleading projections when the sample is tiny (e.g., one post → ~365 projected).format!-constructed — required so the Tailwind purge scanner includes them intarget/tailwind.css.[Track::Battlesnake, Track::Other], notvec![…]— theuseless_vecpedantic lint forbids the heap-allocated form.Risk assessment
trackfield is additive and defaults toOther— no existing frontmatter breaks. Backfill is 4 files, all pre-datingWINDOW_START, so they affect no live count. New route only.Spot-check suggestions
server/src/http_server/pages/pace.rs:render_track_section— verify the expected-by-now tick (divatleft: {tick_pct:.2}%) is a sibling of the fill bar, not a child; both should be children of the outerbg-neutral-700 roundedcontainer.posts/src/blog.rs:Track— confirm#[serde(rename_all = "lowercase")]makesbattlesnake(lowercase) the valid value, and all 4 backfilled files use exactlytrack: battlesnake(nottrack: Battlesnake).server/src/http_server/pages/pace.rs:collect_artifacts—notes.postsis accessed as a direct field; verifyNotePosts.postsispub(notpub(crate)or private) from the server crate's perspective.server/src/http_server/pages/pace.rs:compute_track_stats— artifacts passed in are alreadyin_window-filtered; confirm the per-trackfilter(|a| a.track == track)is applied for both thecountand thetrack_artifactsvec, socompute_track_stats_filters_only_requested_trackis testing real behavior.What the agent verified
cargo fmt --check✅cargo clippy --all-targets --all-features --workspace --tests✅ (clean under pedantic deny)cargo test -p posts✅nav_includes_pace_linktests ✅#[sqlx::test]DB-backed tests fail locally (Postgres creds mismatch) — unrelated to this change, expected to pass in CISummary
Implements the public
/pacedashboard (task BLOG-84ffd942c4f94ead): a cumulative-target-with-graceful-failure progress tracker for Year 1 artifact output, through June 1 2027.The dashboard reads existing blog post and note frontmatter, classifies each published artifact into one of two tracks (
battlesnakeorother), and renders server-side stats per track:elapsed_fraction * target)PROJECTION_MIN_DAYS = 28), since it lies earlyEverything is recomputed on each request from
AppState— no DB writes, no migrations, no manual entry.Changes
posts/src/blog.rs— newTrackenum +trackfrontmatter field (defaults toother)posts/src/notes.rs— mirroredtrackfield on note frontmatterserver/src/http_server/pages/pace.rs— new dashboard page (handler, pure stat helpers, render, unit tests)/pace, module registration, and a "Pace" nav link (with regression test)track: battlesnakeon the four existing Battlesnake postsTesting
cargo fmt --check✅cargo clippy --all-targets --all-features --workspace --tests✅ (clean)cargo test -p posts✅pace,track, andnav_includes_pace_linktests ✅Pre-existing
#[sqlx::test]DB-backed tests (sponsors, memories, thread_processor, thread builder) fail locally only due to the sandbox's Postgres credentials (password authentication failed for user "coreyja") — unrelated to this change, which touches no SQL. They should pass in CI.🤖 Generated with Claude Code