Medium#15 (+ the head-duplication half of Medium#6) from the 2026-07-08 audit: react-helmet-async is archived/unmaintained, and React 19 hoists <title>/<meta> natively. Helmet also appends head tags instead of replacing the index.html shell defaults, so per-route tags duplicate the static ones.
Mechanical migration: bare <title>/<meta> per page, drop HelmetProvider + the dependency. Touches app/src/routes/index.tsx, ~15 pages (LandingPage, PlotsPage, SpecPage, StatsPage, LibrariesPage, McpPage, MapPage, LegalPage, AboutPage, SpecsListPage, NotFoundPage, PalettePage, …), useUrlSync.ts, plus test mocks that stub react-helmet-async.
The audit classed this as codemod-able. Resolves the append-vs-replace duplication without moving shell defaults into a root-layout Helmet block.
Audit: agentic/audits/2026-07-08-product-ux.md (Medium#15, Medium#6 second half).
Medium#15 (+ the head-duplication half of Medium#6) from the 2026-07-08 audit:
react-helmet-asyncis archived/unmaintained, and React 19 hoists<title>/<meta>natively. Helmet also appends head tags instead of replacing theindex.htmlshell defaults, so per-route tags duplicate the static ones.Mechanical migration: bare
<title>/<meta>per page, dropHelmetProvider+ the dependency. Touchesapp/src/routes/index.tsx, ~15 pages (LandingPage,PlotsPage,SpecPage,StatsPage,LibrariesPage,McpPage,MapPage,LegalPage,AboutPage,SpecsListPage,NotFoundPage,PalettePage, …),useUrlSync.ts, plus test mocks that stubreact-helmet-async.The audit classed this as codemod-able. Resolves the append-vs-replace duplication without moving shell defaults into a root-layout Helmet block.
Audit:
agentic/audits/2026-07-08-product-ux.md(Medium#15, Medium#6 second half).