Change Log Explorer
Browse the history of completed work on this site. Expandable cards with PR links, plan files, checklists, and timeline of each item's journey.
restore prerendered deploys for indexed routes
restore prerendered deploys for indexed routes
migrate to lucide-react 1.x
* deps(deps): bump lucide-react from 0.563.0 to 1.8.0 Bumps [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) from 0.563.0 to 1.8.0. - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/1.8.0/packages/lucide-react) --- updated-dependencies: - dependency-name: lucide-react dependency-version: 1.8.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> * fix(deps): migrate to lucide-react 1.x Lucide 1.x dropped brand icons (no Linkedin) and made LucideIcon type-only. This bundles the major bump with the source-side fixes so the dependabot PR #292 can be replaced cleanly. - Add src/components/icons/LinkedinIcon.tsx as a small inline-SVG component matching the GitHubMark pattern. Uses currentColor so it inherits text color the same way the lucide icon did. - Switch HeroSection and ContactSection to import LinkedinIcon instead of Linkedin from lucide-react. - Fix the value-import of LucideIcon in MetricCard.tsx — it's a type now, not a runtime export. All 215 tests pass; typecheck clean. Closes #292 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Blog: Two Supply Chain Attacks in One Day (and a Setting I Used to Argue Against)
Lightning on PyPI and intercom-client on npm got compromised the same morning by what looks like the same attacker. We weren't exposed, but the threat shape changed enough that I walked back a position I took a month ago.
refactor Search Console fetch and add summary-only mode
- Split Search Console query into two calls: an authoritative no-dimension summary and a dimensional rows fetch. Previously the summary was derived by summing rows, which under-counted clicks/ impressions because the row response is capped and aggregated by (query, page) — a single page can split across many rows. - Replace ad-hoc reductions with summarizeRows / aggregateRows / weightedAveragePosition helpers; impression-weighted positions replace simple averages. - Make the daily history write idempotent (update existing date entry instead of appending a duplicate). - Add --update-summary-only mode that re-reads the latest history entry and rewrites docs/metrics/latest.json, preserving the original timestamp. The daily workflow now invokes this after fetch-ga4-data.js so the GA4 step's latest.json overwrite doesn't drop fresh Search Console numbers. - updateMetricsSummary now takes a lastCheck override so the summary- only path doesn't bump the timestamp to "now". Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
make Blog the first and default tab
Reorders ANALYTICS_TABS so Blog is first and sets it as the initial activeTab. Also fixes the Search CTR display (averageCTR is already a percentage; the extra *100 was double-scaling it). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
add preinstall guard section
Documents the npm install blocker that forces npm ci usage, closing the gap between having a lockfile and actually using it. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
fix quarantine section — min-release-age not enforced in npm 11
npm warns "Unknown user config" for min-release-age, so it's not actually protecting anything. Kept uv exclude-newer (which works) and noted npm doesn't have an equivalent yet. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
add 7-day quarantine hardening section
Added min-release-age=7 (npm) and exclude-newer (uv) as a third hardening measure alongside save-exact and version pinning. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
consolidate tags — remove 8 singletons
Merged low-use tags into existing ones: - Observability, Automation → SRE - CMS, SEO, Node.js → Web Dev (or removed) - Spotify → Projects (removed) - Supply Chain → Security (removed) 21 tags → 13. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Blog: Anatomy of the axios Supply Chain Attack (and How We Checked Our Machines in 10 Minutes)
A compromised npm maintainer account pushed malware into axios. Here's how the attack worked, what it installed, and how we checked our machines in 10 minutes.
eliminate AI slop patterns across 24 posts
* blog: eliminate AI slop patterns across 24 posts Run slop-guard (https://github.com/eric-tramel/slop-guard) across all blog posts and rewrite flagged patterns. All 30 posts now score >= 81/100 (up from a low of 8/100). Changes across all posts: - Replaced slop words (reflecting, narrative, leverage, significantly, surprisingly, navigate, collaborative, comprehensive, notable) - Rewrote "X, not Y" contrast pairs as direct claims - Removed "This is not X. It is Y" setup-resolution patterns - Expanded or cut pithy one-liner fragments - Converted bold-bullet listicle blocks to prose paragraphs - Reduced excessive bullet runs and triadic list cadences - Varied repeated phrases - Cut slop phrases ("the key insight", "deliberate choice", "feel free to") All frontmatter, code blocks, React components, internal links, and ASCII diagrams preserved unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * blog: fix writing quality violations from proposed slop-guard rules Address violations from proposed writing quality rules (eric-tramel/slop-guard#9): - "tip of the iceberg" cliche → "symptoms of deeper structural issues" - "deep dive" cliche → "full technical breakdown" - "best practices" cliche → "web standards" - "very" qualifier → cut - "wonderful" ecstatic adjective → "disarming" - "naturally tried to optimize" (over-explanation + pretentious) → "tried to shrink" Intentionally kept: - "obviously" in echonest-sync (intentional humor about airhorns) - "## The Journey" headings (inside code blocks, referencing template name) - "subsequent" in theme-persistence (technical term: "subsequent navigation") - "rather" in "rather than" comparisons (conjunction, not hedging qualifier) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
inline diff in codex review prompt to avoid sandbox errors
The Codex CLI read-only sandbox (bwrap) blocks file reads with "RTM_NEWADDR: Operation not permitted". Fix by passing the diff directly in the prompt instead of writing to pr_diff.txt and asking Codex to read it. Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
add 60-day rolling window and remove redundant legend
* fix(analytics): add 60-day rolling window and remove redundant legend - Filter Sessions Over Time, Search Performance, and Blog Traffic charts to show only the last 60 days instead of all historical data - Remove redundant legend from Device Breakdown donut chart (labels already display device name and percentage) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: bundle mermaid+dagre together and add missing react-is dep - Add manualChunks rule to bundle mermaid and dagre into a single chunk, preventing stale hash errors when cached mermaid chunks reference old dagre chunk filenames after redeployment - Add react-is as a direct dependency (required by recharts at build time but only present as a transitive dev dependency) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
deduplicate anomaly alerts for sustained trends
Instead of creating a new issue every day during a prolonged traffic decline, append updates as comments on the most recent open anomaly issue (within 3 days). New issues are still created for fresh incidents. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
tweak wording in watchdogs post
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Blog: Watchdogs and LaunchAgents: Managing Systems That Want to Break
What we learned building a watchdog for BlueBubbles and OpenClaw on a headless Mac Mini. Health monitors that cause the instability they're designed to detect, and how to fix them.
use 7-day rolling average for analytics anomaly detection
The previous day-over-day comparison triggered false positives when traffic returned to baseline after a spike (e.g. 573→318 = -45%, but 318 is normal). Now compares against the 7-day rolling average with a -40% threshold, which is more resilient to natural traffic fluctuations. Closes #256 Co-authored-by: Claude Opus 4.6 <[email protected]>
Blog: EchoNest Sync and the Spotify API Shakeup
We built a desktop sync app for EchoNest. Then Spotify changed its API in ways that made us glad we did.
Streamline blog post build/deploy pipeline
Added a `detect-changes` job to `deploy.yml` that classifies pushes as content-only or full-build. Content-only deploys skip Playwright tests, unit tests, Sentry source maps, security audit, and bundle size checks. Deployed in PR #249. Full pipeline: ~10min → Content-only: ~2min.
harden OpenClaw post security posture
* blog: harden OpenClaw post security posture - Remove specific locations, chat counts, plist names, exact schedules - Generalize infrastructure to "Mac-class device" / "system service" - Describe guardrails as principles (least privilege, separate service accounts, out-of-band approval) rather than exact mechanisms - Reframe hard parts as lessons learned, not precise failure modes - Remove personal schedule details; add consent language for shared data - Add monitoring/detection section (weekly activity report, anomaly alerts) - Remove GOG_KEYRING_PASSWORD and other implementation specifics Co-Authored-By: Claude Opus 4.6 <[email protected]> * blog: humanizer pass on OpenClaw post - Remove formulaic openers ("The lesson:", "The takeaway:", "The goal") - Break up rule-of-three/five feature lists into shorter sentences - Rewrite security paragraph from brochure tone to plain language - Cut negative parallelism ("not just X, but Y") - Vary sentence structure and rhythm throughout Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
improve OpenClaw post intro and link
* blog: improve OpenClaw post intro with context and link - Link OpenClaw to https://openclaw.ai/ - Frame as exploration of an open-source AI agent framework - Add context on what OpenClaw is and why it's notable - Fix location: Mac Mini is in the cabin, not apartment Co-Authored-By: Claude Opus 4.6 <[email protected]> * blog: reframe OpenClaw post and run humanizer pass - Reframe as experimentation with safety-first approach - Remove camera snapshot section - Link OpenClaw to https://openclaw.ai/ - Fix Mac Mini location (cabin not apartment) - Humanizer pass: remove bolded inline headers, reduce AI vocabulary, vary sentence structure, cut promotional tone Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
rework blog analytics dashboard
* feat(analytics): rework blog analytics dashboard - Add all-time leaderboard aggregating GA4 history across all entries - Fix slug matching for renamed posts via alias map + year normalization - Replace single-line blog traffic chart with stacked area (top 5 posts) - Replace avg reading time metric with all-time views; add WoW trend - Switch tag breakdown to all-time data; remove sparse category pie chart - Trim 7d table to top 5, streamline columns Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix(analytics): address Codex review feedback - Remove risky partial substring matching in slug resolver to prevent silent data misattribution; add min key length guard (>=10 chars) - Use Date.getTime() for firstSeen/lastSeen comparisons instead of string comparison to handle non-ISO date formats safely Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
make security audit non-blocking in deploy workflow
The deploy workflow's npm audit step was failing the entire build due to transitive dependency vulnerabilities in dev tools (eslint, minimatch, glob). These are not exploitable in production. Aligns with pr-checks.yml which already has continue-on-error: true and --omit=dev for the audit step. Co-authored-by: Claude Opus 4.6 <[email protected]>
Blog: OpenClaw: Experimenting with a personal AI agent
What I learned running an open-source AI agent on a self-hosted Mac Mini. Least privilege, version-controlled config, and the boring plumbing that makes it work.
Rework TabList Mobile Selector
Feb 2: INP Performance Optimization
Reduced Interaction to Next Paint from 493ms (poor) to <50ms (good). Throttled Kanban drag handlers, deferred analytics to idle time, debounced K8s form inputs, and memoized heatmap computations.
K8s Resource Right-Sizer
K8s CPU/memory rightsizing calculator with percentile-based recommendations, risk slider, savings estimates, and YAML export. Includes combobox inputs with tooltips and dropdown presets for easy exploration.
Jan 28: Hero GitHub Button
Added GitHub button to hero section alongside the Email button: - Solid variant for visual hierarchy - Renamed "Get In Touch" to "Email" for clarity - Updated tests for new button layout Commits: b98efed, 0e53f51, acf7603, bd55d64
GitHub Actions Billing Dashboard
Track Actions minutes/spend in analytics dashboard. Phase 0 (API validation) complete - uses new Enhanced Billing endpoint. See docs/plans/60-github-actions-billing-dashboard.md
Jan 28: SEO Schema Improvements
Enhanced structured data for better search visibility: - Added ProfilePage JSON-LD schema to homepage - Added BreadcrumbList schema to blog posts - Fixed LinkedIn URL in social links - Added author.sameAs to BlogPosting schema Plan: [docs/plans/61-seo-profilepage-schema.md](/docs/plans/61-seo-profilepage-schema.md)
Jan 28: Blog Post Date Rename
Renamed all blog post files and frontmatter dates from 2025 to 2026 to reflect correct publication year.
Jan 27: The 404s Came Back
Published sequel to the original 404 debugging post, covering: - Mysterious traffic from CI/CD pipelines - HeadlessChrome user agent detection - Analytics filtering for bot traffic Also improved Top Pages table readability on mobile (PR #210). Related: [Blog post](/blog/2026-01-27-the-404s-came-back)
Jan 27: SEO Pre-render All Routes
Pre-rendered all routes at build time to ensure Googlebot can crawl them without JavaScript execution. SPAs on GitHub Pages return 404 for direct URL access without pre-rendering. Commit: 4eee9a8
Jan 25: Dotfiles for AI-Assisted Development
Published blog post covering dotfiles patterns for AI-assisted development: - CLAUDE.md project instructions - Global vs project-specific rules - Session notes for context persistence - Commit message conventions with flags Related: [Blog post](/blog/2026-01-25-dotfiles-for-ai-assisted-development)
Jan 25: Employment Update
Updated employment information to reflect new role as Technical Incident Manager at Nvidia.
Kanban System Durability Improvements
New Board Creation
Add ability to create new kanban boards from the UI. Board selector dropdown to switch between boards, plus 'New Board' button to create fresh boards. **Architecture Decision**: Dynamic board discovery (Option A from plan). Worker scans `content/kanban/` to discover boards, no hardcoded allowlist. **Codex Review Complete** (2026-01-23): - Race condition handling with retry logic on 409 - Markdown fallback for new boards before precompile - Column ID validation with `SAFE_ID` - Optimistic UI with `precompiled: false` indicator See `docs/plans/58-new-board-creation.md` for full implementation plan.
Migrate Kanban to Markdown Files
Replace monolithic roadmap-board.json with individual markdown files per card. Adopt Backlog.md pattern or Astro Content Collections approach. Problem: 1665-line JSON file is error-prone for manual editing (trailing commas, bracket mismatches). Options evaluated: - Backlog.md (ready-made, Claude Code compatible) - Astro Content Collections pattern (gray-matter + Zod) - YAML files - TypeScript data files
Phase 2: Markdown-Only Saves
Eliminate dual maintenance by making markdown the single source of truth. Current state: JSON files are edited by save workflow, markdown files power ChangelogExplorer. Target state: Save workflow writes .md files directly via GitHub API, precompile runs on every push. Benefits: - Single source of truth (no sync issues) - Better git diffs for card changes - Easier manual editing when needed - CLI tools work with same format
Jan 23: Kanban Phase 2 Complete
## Phase 2: Markdown-Only Saves Completed the migration to make markdown the single source of truth for kanban boards. ### Key Changes - **Eliminated JSON files**: Removed `roadmap-board.json`, `house-board.json`, and `roadmap-archive.json` - **Worker writes directly to markdown**: Save flow now commits `.md` files via GitHub Trees API - **Commit SHA conflict detection**: Replaced timestamp-based detection with atomic commit SHA comparison - **Precompiled JS fallback**: Board loads from worker API (primary) or generated JS (offline fallback) ### Bug Fixes - Fixed UTF-8 encoding corruption for non-ASCII characters (arrows, emojis) - Fixed duplicate history entries during drag operations (moved tracking from `handleDragOver` to `handleDragEnd`) - Fixed Cloudflare Workers 50 subrequest limit by using inline content in tree items - Fixed open redirect vulnerability in OAuth return_to validation ### Architecture ``` Save: UI → Worker → GitHub Trees API → content/kanban/*.md ↓ repository_dispatch → precompile-content.yml ↓ src/generated/kanban/*.js Load: UI → Worker API (primary) → precompiled JS (fallback) ``` Related PRs: #195, #198
Analytics Bot & CI Traffic Tagging
Tag suspicious traffic with custom dimension instead of filtering. Enables analysis of bot patterns while keeping data. Implemented in `src/lib/analytics/clientTrafficClassifier.ts`: - Known bot user agents (Googlebot, Bingbot, HeadlessChrome, etc.) - CI/automation user agents (GitHub Actions runners, Playwright) - Known probe paths (wp-admin, .env, xmlrpc.php, etc.) - Sessions tagged on first pageview with GA4 custom dimension
Analytics Event Tracking
Add GA4 events for interactive tool usage. Track calculator submissions, tab switches, copy-to-clipboard actions. Implemented in `src/lib/trackToolEvent.ts` with events firing in: - SLO Calculator: tab_switch, calculate, period_change - CLI Playground: command_run, tool_select, mode_switch, preset_select, share_copy - On-Call Coverage: model_select
Lighthouse: A11y & SEO Focus
Lab Lighthouse provides unique value for accessibility audits and SEO checks that field CWV data can't capture. Reframed the workflow to focus on these strengths. Implemented in `.github/workflows/lighthouse.yml`: - Path-based triggers (only runs on UI changes to src/pages, components, CSS) - Multi-page testing with thresholds: A11y ≥95, SEO ≥90, Best Practices ≥90 - Results stored in lighthouse-metrics branch for historical tracking
Tailwind CSS v4 Upgrade
Migrate to v4: CSS-based config, Vite plugin, updated utilities. ~116 class renames across 59 files. Completed Jan 21. See blog post: "Tailwind v4 Upgrade: The Performance Tradeoff"
react-hook-form & @hookform/resolvers Update
Updated react-hook-form from 7.53.0 to 7.71.1 and @hookform/resolvers from 3.10.0 to 5.2.2. The resolvers package had a peer dependency requiring react-hook-form >=7.55.0.
Animated Mermaid Diagrams
Added animated walkthrough mode to incident-command-diagrams with: - Step-by-step animation with play/pause/skip/back controls - Decision nodes that pause for user to choose a path (branching logic) - Link nodes for cross-diagram navigation and external tool links - Side-by-side layout with sticky context panel - Auto-scroll to center active node in viewport - Progress bar and step counter - Lazy-loaded Mermaid rendering with theme support
CLI Tool Playground
Interactive in-browser demos for CLI tools. Pure JS implementations (no WASM). Features: - Tool selector: kubectl, jq, grep, sed, awk - Learn mode with goals, hints, and command chips - Playground mode for freeform experimentation - Command explainer with flag breakdowns and 'Try next' suggestions - Shareable URL state (tool, mode, input, command) - 5 presets per tool covering common use cases kubectl simulator: - 5 triage scenarios (CrashLoopBackOff, ImagePullBackOff, Service Mismatch, Rollout Regression, Node Pressure) - Full K8s resource schema (pods, deployments, services, nodes, events) - Supports: get, describe, logs, rollout, top, events - Session state mutation for rollback commands Security fixes: - Replaced Function() with safe regex parser (XSS) - URL param validation with fallbacks - Race condition guards for async results
Error Budget Burndown
Visualize how quickly you're consuming error budget. Input SLO target + incident history to see burn rate and projected exhaustion date.
Programmatic OG Image Generation
Generate Open Graph images at build time for project pages using satori + @resvg/resvg-js. Implemented: - Build-time generation via scripts/generate-og-images.mjs - Build-time validation via scripts/validate-projects.mjs - Dark theme template with icon, title, description, tags - Inter font (regular + bold) for typography - Added to build pipeline in package.json - Extracted project metadata to projects-meta.json - Simplified projects.ts by removing inline metadata - Fail-fast guards for CI (exits 1 on generation or validation failures)
SLO Calculator SEO
Added JSON-LD structured data and keyword optimization for SLO Calculator project page. Implemented: - WebApplication JSON-LD schema for active projects - BreadcrumbList JSON-LD schema for navigation - Keywords and ogImage fields in ProjectMeta type - SEO-optimized keywords for SLO Calculator - resolveOgImage helper for absolute URL handling OG image generation moved to separate task (Programmatic OG Image Generation).
SLO Tool UX Improvements
Polished the SLO Calculator with better defaults and new burn rate simulator. Changes: - Realistic default response times (26m MTTR) - Collapsible Response Times section (shows Alert latency by default) - Removed redundant burndown charts from Achievable/Target tabs - Added interactive Burn Rate Simulator slider (0.1x-5x) - Explanatory text for the 'ideal' line concept - Fixed slider resync when inputs change (Codex finding) - Fixed edge case when zero incidents (Codex finding)
Grafana Dashboard JSON Builder
Visual editor for Grafana dashboards. Drag-and-drop panel layout, panel type picker, export schema v38+ JSON.
Log Pattern Extractor
Detect recurring log patterns from raw lines. Auto-group by template, field type inference, export to regex/grok/logstash format.
Retention Cost Estimator
Estimate observability costs across providers (Datadog, Grafana Cloud, New Relic). Side-by-side comparison, retention tradeoffs.
Unified SLO Calculator
Consolidated SLO Calculator and Error Budget Burndown into one project with three tabs: - What can I achieve? (response time inputs directly visible) - Can I meet this SLO? (collapsible config) - Budget Burndown (full chart view) Removed 2,700+ lines of redundant code. Collapsible configuration for target/burndown tabs, direct inputs for achievable tab.
SLO Calculator Burndown Integration
Add 'SLO Burndown' tab to SLO Uptime Calculator. Reuse BurndownChart from Error Budget Burndown, generate simulated incidents from 'incidents per month' input.
Pre-commit Hooks (Husky + lint-staged)
Add Husky + lint-staged for pre-commit hooks. Runs ESLint with auto-fix on staged TS/JS files before every commit.
Kanban External Change Detection
Detect when board is updated externally (e.g., by Claude commits). Check on tab focus + every 15s while visible. Show toast with reload button to prevent save conflicts.
Change Log Explorer Project
Visualize completed work from kanban changelog and archive. Rich expandable cards with PR links, plan file content, and completion history.
Kanban URL Simplification
Remove URL state persistence, keep just `?board=roadmap`. Add deep linking with `?card=id` to open specific cards on load.
Preview Deployments
Deploy PRs to unique preview URLs via Cloudflare Pages. Enables visual review before merge. Preview URL: https://personal-website-adg.pages.dev Branch pattern: <branch>.personal-website-adg.pages.dev
Kanban Card Comments
Add giscus discussion threads to kanban cards. Reuse existing blog Comments pattern with theme matching. Also fixed blog Comments to use ThemeContext.
Sentry Error Tracking Enhancement
Source maps for readable stack traces, React error boundaries, release tracking, and improved debugging context. Codex Review of PR #130: - High (fixed): Sentry lazy loading inconsistency - ErrorBoundary imported but Sentry.init deferred. Fixed: now initializes synchronously before render. - Medium (fixed): Source maps publicly served exposing source code. Fixed: using hidden sourcemaps + delete after Sentry upload.
Analytics Data Deduplication
Deduplicate time-series data in useAnalyticsData hook. ga4-history.json has multiple entries per date from automated collection.
Kanban Board Save Feature
GitHub OAuth login, Cloudflare Worker proxy, save to GitHub via Actions. Includes conflict detection, unsaved changes warning.
React Performance Optimizations
Analytics CLS 0.71→0.10, scroll throttling, React.memo, lazy-loaded charts, skeleton loaders
Dynamic PR Status Indicator
Live CI status for In Review cards via GitHub API, merged icon for changelog PRs
Framer Motion Animations
Stagger animations for grids, scroll reveals, tab transitions, mobile nav stagger. Respects prefers-reduced-motion.
Jan 14: Kanban & UX
Kanban board with drag-and-drop, card colors, House Projects board, performance budgets, ARIA live regions, deploy notifications
Renovate Automation
Decided against: overhead not justified for actively-maintained personal project. Manual npm update works fine.
Jan 12: Projects Page Launch
SLO Calculator, Status Page Generator, registry pattern
Jan 11: MDX Precompilation
Blog LCP 5.6s → 3.1s (45% faster)
Jan 8: Blog Phase 4 & 5
Comments, syntax highlighting, RSS, structured data
Jan 6-7: Node.js v24 + Perf
55 → 98 Lighthouse, system fonts, Radix cleanup