Changelog

All changes and updates to this site.

Projects: FragHub hosting updated to Hetzner

Updated the [Projects](/projects) page to reflect that FragHub is now hosted on Hetzner rather than Laravel Cloud.

Tightened Accept-header content negotiation for agents

The markdown-for-agents edge function now parses the `Accept` header with q-values, so a client sending `text/markdown;q=0` correctly receives HTML, and a client that prefers `text/html` over `text/markdown` is also respected. If the client explicitly requests a type the site cannot serve, the function returns 406 Not Acceptable per RFC 9110. All negotiated routes now set `Vary: Accept, Accept-Encoding` so shared caches key on the Accept header and don't cross-serve HTML to agents or markdown to browsers.

Queue safety post: updated

The [Improving Queue Safety in Laravel](/articles/improving-queue-safety-in-laravel) post was updated. See the Revisions section at the end of the post for the full list of changes.

Resume page, content fixes, and SEO improvements

  • Added `/resume` page with work history, education, skills, and recent writing
  • Fixed duplicate list numbering in the SessionManager post-mortem article
  • Fixed dark mode tutorial: removed redundant `x-init`, corrected heading level
  • Added `retryUntil` vs `$tries` caveat and corrected Finding #11 in the queue safety article
  • Added `/sitemap.xml` alias redirecting to `/sitemap-index.xml`
  • Added `/index.md` markdown fallback for AI agents and crawlers

Updated: Windows vs Mac Claude Code post with fff-mcp benchmark

  • Added an update section to the Windows vs Mac benchmark article covering fff-mcp, a Rust MCP server that gives Claude Code a warm file-search index
  • Re-benchmarked on the same Windows machine, Filament framework repo (7,794 files), five grep queries, median of three runs
  • fff-mcp came in 14.0x faster per warm query and 5.5x faster over a five-query session, including its one-time startup and scan
  • Included the install one-liner and a note that the speedup stacks with the WSL finding in the original post

Added YouTube video thumbnail cards for articles

  • Articles can now embed YouTube video thumbnails as styled cards with play button overlay
  • Thumbnails link out to YouTube (no iframe, keeps pages lightweight)
  • First usage in the Windows vs Mac benchmark article for the fff-mcp video walkthrough

Backfilled missing article excerpts

  • Nine older articles never had an `excerpt` field in frontmatter, so the `/articles` listing rendered only the title, date, and tags with no hook
  • Added an excerpt to each, written from the article body rather than the `tldr`, so the listing reads as a proper lede
  • Affects the tutorials (Laravel constants, Laravel enums, Alpine.js dark mode), the project write-ups (Barcoder, FragHub, Poker Timer), the 19 bug fixes post, and the two earlier reflections (KPIs, own apps vs job)

Strip trailing # from article TOC entries

  • Article table of contents no longer shows a trailing `#` after each entry
  • The `#` anchor link still appears beside each heading in the article body, used for deep-linking

Clean up redundant Alpine.js attribute in dark mode tutorial

  • Removed `x-init="init"` from the dropdown example in `/articles/how-to-add-a-dark-mode-selector-to-your-alpinejs-and-tailwind-css-app`
  • Alpine.js 3.x auto-runs an `init()` method defined inside `x-data`, so the explicit attribute was a no-op
  • Also dropped a stray trailing colon from the `## 5. Test Theme Toggling:` heading to match the rest of the article

Person JSON-LD structured data on every page

  • Every page now emits a schema.org Person JSON-LD block with name, URL, image, jobTitle (Web App Developer), description, address, and sameAs links to GitHub, LinkedIn, and X
  • Agents and search engines can parse identity programmatically and disambiguate across platforms via sameAs
  • Not visible on the page; consumed by knowledge-graph crawlers and rich-result generators

Site made machine-readable for AI agents

Cloudflare published an agent-readiness scan that checks sites against emerging standards like Link headers, markdown content negotiation, Agent Skills, MCP discovery, and the rest of the .well-known zoo. joshsalway.com scored 25/100 on the first run (Level 1: Basic Web Presence), with zero of six checks passing on the protocol-discovery axis. Shipped the fixes over an afternoon:

  • Declared content-usage intent in robots.txt via a Content-Signal directive (search, ai-train, and ai-input all yes)
  • Link response headers now advertise the sitemap, RSS feed, llms.txt, agent-skills index, and API catalog
  • Published an agent-skills discovery index with a read-articles skill enumerating every way to ingest site content
  • Accept: text/markdown on the homepage and any article now returns raw markdown; browsers still get HTML
  • Published an MCP server card backed by a JSON-RPC endpoint at /mcp with search-articles and list-articles tools
  • Same two tools registered as WebMCP tools on every page for in-browser agents
  • Published an API catalog (RFC 9727) advertising the machine-readable endpoints, plus a /status.json health endpoint

OAuth/OIDC discovery stays intentionally absent. The site has no authenticated endpoints, so publishing fake metadata would point agents at auth flows that don't exist. Better to fail those two checks than to lie.

After the changes, the Content category re-scans at 100 out of 100, which is the part that actually matters for a content site. Overall lands around 80/100 (Level 3); the two intentional OAuth fails above account for the remaining gap.

Correct fork PR count in '19 Laravel Bug Fixes' post

  • The Current Totals section in `/articles/25-laravel-bug-fixes-tested-verified-ready-to-merge` claimed `5 PRs on forks`
  • The breakdown right next to it lists sail (3), octane (2), and cashier-stripe (2), which sums to 7
  • Updated the headline number to 7 to match the breakdown

Blockquotes can be copied

  • Hover over any blockquote on an article page to reveal a small Copy button
  • Click copies the quote text to the clipboard, same pattern as code block copy
  • Works in both light and dark themes

Dark mode contrast bump on small metadata text

  • Lighthouse flagged the `text-[10px]` spans in dark mode (reading time, depth/length labels, keyboard hints, project tech tags) as failing the 4.5:1 WCAG AA contrast ratio
  • Swapped `dark:text-gray-500` to `dark:text-gray-400` on those spans so they now pass
  • Light-mode spans moved to `text-gray-500` as the visual counterpart, no regression
  • The small text is a bit brighter in dark mode than before, the trade is readability

Front page loads without the hero flicker

  • The hero background used to flash black on first paint before the WebGL gradient took over
  • The canvas now fades in after its first frame is drawn, so the accent-tinted CSS gradient behind it is what you see until the shader is ready
  • Dropped the React wrapper around the canvas so the shader starts without waiting for hydration
  • Home page also stopped preloading images from the about and projects pages, which were competing with the LCP on every navigation

Link directly to any heading in an article

  • Hover over any h2, h3, or h4 on an article page to reveal a # anchor
  • Click updates the URL with a hash like `/articles/<slug>/#view-transitions` so you can share a link straight to that section
  • IDs are generated at build time, so deep links work on first load without waiting for hydration

Remove em dashes from the repo

  • Replaced every existing em dash in repo content with a comma, period, or reworded phrasing

RSS feed no longer exposes a fake email address

  • The RSS feed at `/feed.xml` used to list `josh@joshsalway.com` as the author; that address is not a real mailbox
  • Replaced with `<dc:creator>Josh Salway</dc:creator>`, the email-free convention for RSS author attribution
  • Readers who want to reach me can still use the contact form at `/contact`

Short links now surfaced in the machine-readable feeds

Every article has a memorable short link in its frontmatter (for example /laravel-queue-safety, /fraghub, /kpis) that already 301s to the canonical URL. Those short links were only used by the human-facing redirects until now. Surfaced them across the machine-readable surfaces too:

  • llms.txt lists each article with both the canonical URL and the short link
  • /articles.json gains a short_url field per entry
  • The MCP JSON-RPC server at /mcp and the WebMCP tools on every page include short_url in their search-articles and list-articles results

Net effect: an AI agent asked for a shareable link to the queue safety post can now return joshsalway.com/laravel-queue-safety instead of the long /articles/improving-queue-safety-in-laravel form.

Topic pill buttons now meet the 24px touch target guideline

  • The small topic tags (Laravel, Open Source, etc.) on article rows were 19px tall, under the WCAG 2.5.8 recommended minimum of 24x24
  • Bumped vertical padding and added a 24px minimum height so the buttons are easier to hit on touch devices
  • Lighthouse accessibility `target-size` audit now passes

Light mode code blocks and site improvements

  • Code blocks now use a light theme (github-light) in light mode, dark theme (github-dark) in dark mode
  • Replaced runtime highlight.js with Shiki build-time syntax highlighting for faster page loads
  • Copy button on code blocks adapts styling for light and dark modes
  • Added missing excerpt to queue safety article for the articles listing page
  • Added comprehensive Playwright test suite (76 tests across 8 files)

Site improvements: fonts, favicon, SEO, and more

  • Updated typeface to Inter (body) and system-ui (headings)
  • Added JS favicon and default OG image for social sharing
  • Added robots.txt and llms.txt for search engine and AI crawler discoverability
  • Added OG meta tags on all article pages
  • Fixed redirect chains (302 to 301, trailing slashes)
  • Added changelog page
  • Improved RSS feed with full article content
  • Auto-calculated reading time on article cards
  • Added recent article suggestions to 404 page
  • Added redirect for truncated Twitter URL
  • Excluded future-dated articles from builds

Published: Improving Queue Safety in Laravel

  • Published queue safety audit with 14 findings and 9 community-reported issues
  • All verified against Laravel Framework v13.4.0 with links to real GitHub issues spanning 2015 to 2026
  • Includes SafeJob base class, AI audit prompt, and actionable safeguards
  • Documents root cause of two Vapor billing incidents ($140 in 2019, $218.90 in 2023)

Fix TOC anchor links scrolling behind sticky nav

  • Clicking a heading in the article table of contents now scrolls the heading into view below the sticky nav, instead of being covered by it
  • Applies to all h2 and h3 headings inside article content

Updated developer bio

Removed "full-stack" from developer bio on index and about pages.

Published: Why Your Laravel Jobs Might Retry Forever After an OOM

Published a fact-checked blog post documenting Laravel Issue #58207 with end-to-end proof on Laravel 13.4.0. Includes before/after test results, source code verification, and links to the public reproduction repo.

Share button on articles

Added a copy-to-clipboard button next to article titles. Copies the short link URL with a toast notification.

Dynamic short link redirects

Short links are now generated at build time from article frontmatter instead of being hardcoded in netlify.toml. Adding `shortLink: "/my-link"` to any article automatically creates the redirect.

Site polish and mobile fixes

  • Animated JS logo with hover typing effect
  • Clickable topic tags
  • Tag filter bar on articles page
  • Tech filter on projects page
  • Collapsible table of contents
  • TLDR summaries
  • Service worker for offline support
  • Lazy-loaded syntax highlighting
  • Fixed touch targets for mobile accessibility
  • Fixed dark mode flash on navigation

Site rebuilt from Statamic to Astro

  • Complete rebuild using Astro 6 with React islands
  • Migrated 14 articles from Statamic SSG
  • WebGL hero canvas
  • Spotify-inspired dark theme with 8 colour themes
  • Ctrl+K search with Fuse.js
  • Reading progress bar
  • Table of contents
  • Copy-to-clipboard code blocks
  • RSS feed and sitemap
  • Konami code easter egg
  • 301 redirects for all old URLs

Final articles on Statamic

  • "Windows vs Mac for Claude Code: The Benchmark That Changed How I Work"
  • "14 More PRs Merged Into Laravel"
  • "Laravel PR #59323 Post-Mortem"

Published Laravel contribution articles

  • "FragHub.gg" project showcase
  • "25 Laravel Bug Fixes, Tested, Verified, Ready to Merge"
  • "Update: Unblocked, 25 Laravel Fixes and What's Next"
  • "20 PRs Merged Into Laravel in 12 Days"

Published project showcases

  • "I Built a Free Poker Timer App"
  • "Barcoder: A Free Barcode Scanner and Creator"

Published two career articles

  • "Why KPIs Work Best When People and Customers Come First"
  • "Why I Chose to Build My Own Apps Instead of Taking a Traditional Job for Now"

Published: Dark mode selector with Alpine.js and Tailwind

Tutorial on adding a dark mode selector to Alpine.js and Tailwind CSS apps.

Site launched on Statamic

  • Launched joshsalway.com using Statamic SSG on Netlify
  • Published first two articles: "Using Laravel Constants in React with Inertia.js" and "Using Laravel Enums in React with Inertia.js"