Skip to content

feat: add /pace dashboard tracking artifact output vs Year 1 target#411

Merged
coreyja merged 3 commits into
mainfrom
implement/BLOG-84ffd942c4f94ead
Jun 26, 2026
Merged

feat: add /pace dashboard tracking artifact output vs Year 1 target#411
coreyja merged 3 commits into
mainfrom
implement/BLOG-84ffd942c4f94ead

Conversation

@byte-the-bot

@byte-the-bot byte-the-bot commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Review Brief

What changed and why

Adds a public /pace dashboard 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 typed Track enum field on both BlogFrontMatter and notes::FrontMatter, a new server-side handler with pure stat helpers (in_window, compute_track_stats, render_track_section), and backfills track: battlesnake on 4 existing posts. No DB writes, no migrations — everything recomputed on each request from AppState.

Architecture decisions and trade-offs

  • Track is a closed enum with serde(rename_all = "lowercase") — unknown values like track: typo hard-error at parse time rather than silently defaulting. Intentional per spec ("typed, not free-form").
  • Window dates use static LazyLock<NaiveDate> rather than constNaiveDate::from_ymd_opt is not const fn in chrono 0.4.41; const here would be a compile error (E0015).
  • projected_finish is suppressed for the first 28 days (PROJECTION_MIN_DAYS) to avoid misleading projections when the sample is tiny (e.g., one post → ~365 projected).
  • Tailwind classes are full literal strings, never format!-constructed — required so the Tailwind purge scanner includes them in target/tailwind.css.
  • Track iteration uses an array literal [Track::Battlesnake, Track::Other], not vec![…] — the useless_vec pedantic lint forbids the heap-allocated form.

Risk assessment

  • Blast radius: Low. The track field is additive and defaults to Other — no existing frontmatter breaks. Backfill is 4 files, all pre-dating WINDOW_START, so they affect no live count. New route only.
  • Confidence level: High. Pure helper functions have comprehensive unit tests covering all edge cases from the plan.
  • Rollback safety: Clean revert — no schema changes, no migrations, no data mutations.

Spot-check suggestions

  • server/src/http_server/pages/pace.rs:render_track_section — verify the expected-by-now tick (div at left: {tick_pct:.2}%) is a sibling of the fill bar, not a child; both should be children of the outer bg-neutral-700 rounded container.
  • posts/src/blog.rs:Track — confirm #[serde(rename_all = "lowercase")] makes battlesnake (lowercase) the valid value, and all 4 backfilled files use exactly track: battlesnake (not track: Battlesnake).
  • server/src/http_server/pages/pace.rs:collect_artifactsnotes.posts is accessed as a direct field; verify NotePosts.posts is pub (not pub(crate) or private) from the server crate's perspective.
  • server/src/http_server/pages/pace.rs:compute_track_stats — artifacts passed in are already in_window-filtered; confirm the per-track filter(|a| a.track == track) is applied for both the count and the track_artifacts vec, so compute_track_stats_filters_only_requested_track is 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
  • All new pace, track, and nav_includes_pace_link tests ✅
  • Pre-existing #[sqlx::test] DB-backed tests fail locally (Postgres creds mismatch) — unrelated to this change, expected to pass in CI

Summary

Implements the public /pace dashboard (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 (battlesnake or other), and renders server-side stats per track:

  • count vs target (Battlesnake 48, Other 12)
  • expected-by-now marker on the progress bar (elapsed_fraction * target)
  • on/off-pace verdict (green / amber)
  • need-per-week to still hit target (featured prominently)
  • weeks left to the window end
  • projected finish — greyed and suppressed until ~week 4 (PROJECTION_MIN_DAYS = 28), since it lies early
  • chronological artifact list (date + link, newest first) doubling as public proof-of-work

Everything is recomputed on each request from AppState — no DB writes, no migrations, no manual entry.

Changes

  • posts/src/blog.rs — new Track enum + track frontmatter field (defaults to other)
  • posts/src/notes.rs — mirrored track field on note frontmatter
  • server/src/http_server/pages/pace.rs — new dashboard page (handler, pure stat helpers, render, unit tests)
  • route /pace, module registration, and a "Pace" nav link (with regression test)
  • backfilled track: battlesnake on the four existing Battlesnake posts

Testing

  • cargo fmt --check
  • cargo clippy --all-targets --all-features --workspace --tests ✅ (clean)
  • cargo test -p posts
  • New pace, track, and nav_includes_pace_link tests ✅

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

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>
@coreyja coreyja merged commit 5b15d83 into main Jun 26, 2026
6 checks passed
@coreyja coreyja deleted the implement/BLOG-84ffd942c4f94ead branch June 26, 2026 11:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants