Repository navigation
fix(seo): correct event status, poi image and breadcrumb urls - #470
Merged
Merged
Conversation
The event detail page declared eventStatus EventScheduled for every phase, so a past event still claimed to be upcoming. schema.org has no "completed" status, so a past event now carries no eventStatus instead of a false one. Announced, running and hidden (unlisted, not yet started) events keep EventScheduled, which stays correct for them. The mapping lives in layers/events/utils/eventSchemaStatus.ts and takes the phase the server already decided in useEventDetail, so no clock is read here (see tests/events/phase-on-server.spec.ts). Test: pnpm exec vitest run tests/events/event-schema-status.spec.ts Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
The community POI breadcrumb pointed its ancestor at /de/community-poi/, which 301s to the canonical /de/community-poi. Breadcrumb items must name the canonical URL, the same way the locale root was already fixed. The same trailing slash was in the POI back link and in the team fallback URLs (ItemList on the team index, Person on a member without a slug), so they are corrected too. breadcrumb-urls.spec.ts now covers every locale-prefixed template literal ending in a slash, not only the locale root, so the next index page cannot drift back. Test: pnpm exec vitest run tests/seo/breadcrumb-urls.spec.ts Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
The POI CreativeWork took the raw frontmatter thumbnail (/images/...) as its image and left it relative. In production those images resolve through img.onelitefeather.net, so the path on onelitefeather.net returns 404 and the structured data pointed at a missing file. usePageSeo now returns the socialImage it already computes for og:image, and the POI page uses that URL for the CreativeWork image. Both tags therefore share one resolution path and one absolute, transformed URL. Test: pnpm exec vitest run tests/seo/poi-jsonld-image.spec.ts Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
launchpad | 1e1c6d1 | Commit Preview URL Branch Preview URL |
Oct 10 2026, 09:55 PM |
The emitter table, module versions and the sitemap version tell no longer matched main, and violations fixed since were still listed as live. The open zeroRuntime gap stays documented. Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
The embedded BlueMap started loading at page load despite loading="lazy": on the Yggdrasil page that was 284 requests and about 70 MB on mobile. A placeholder button now mounts the iframe only when asked for. Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
…468) nuxt-posthog starts posthog-js on every page. Before this change that loaded surveys.js (35 KB, ~85 % unused), dead-clicks-autocapture.js (629 ms script evaluation on /en/projects/falco, TBT 1.6 s there) and posthog-recorder.js (69 KB, ~37 KB unused) during startup. Nothing in the repository uses surveys, dead-click events or session replay (no references in layers/, pages/, docs/ or openspec/), so: - disable_surveys: true - capture_dead_clicks: false (client value overrides the server-side setting that enables it) - disable_session_recording: true at init; the recorder is started from layers/base/plugins/posthog-recording.client.ts after idle time (requestIdleCallback, 4 s timeout, 2 s timer fallback) Left unchanged on purpose: - posthog.init stays synchronous. The consent spec requires capture from page start and the stored opt-out applied before any capture; deferring init would replace nuxt-posthog's client plugin and reorder that. - Feature flags still bootstrap from the SSR state, and pageview capture is still nuxt-posthog's router.afterEach hook. - startSessionRecording() still honours opt-out, sampling and linked flags. Tests: tests/base/defer-until-idle.spec.ts and tests/analytics/posthog-config.spec.ts (both red before the change). Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
The image transforms for og:image and the Article image variants set width and height but no fit, so Cloudflare scaled them down and served 630x630 where 1200x630 is declared. fit=cover crops to the size the tags announce. Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
* feat(seo): describe projects as richer SoftwareApplication The project detail page emitted a SoftwareApplication without the fields search engines use to relate a project to its code and runtime. Add them from the existing frontmatter: - codeRepository from links.source - applicationCategory DeveloperApplication - runtimePlatform from platforms. Paper, Folia and Minestom are server runtimes the plugin runs inside, not operating systems, so operatingSystem would be the wrong property. - license as an spdx.org URL for known SPDX ids. schema.org expects a URL here; free text such as "Proprietary" is omitted instead of guessed at. programmingLanguage is deliberately not emitted: the frontmatter does not state it. The node is built by layers/projects/utils/softwareApplication.ts so the mapping is unit-tested without rendering the page. Test: pnpm exec vitest run tests/projects tests/seo Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX * feat(seo): link POI build to its Place node When a POI has coordinates the page emits a Place, but the CreativeWork describing the build did not reference it, so the Place was an unconnected node in the graph. The CreativeWork now sets contentLocation to the Place's @id, and only when that Place is emitted. Test: pnpm exec vitest run tests/seo/poi-place-link.spec.ts Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
The home page was titled "Home", project titles lacked the terms people search for, and several blog titles and descriptions ran far past what Google shows. Titles now name the German-speaking Minecraft server and the Paper and Minestom projects, and descriptions fit a search snippet. Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
Event.image resolved the raw /images path against the site origin, which 404s; it now uses the same image URL as og:image. Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
* fix(about): state 2019 as the founding year everywhere The site contradicted itself. The FAQ (de and en) and public/llms.txt said 2019, while the About texts (de, en) and the Organization identity in nuxt.config.ts said 2021. The Organization JSON-LD served on every page carried foundingDate "2021", so search engines received the wrong year. The owner confirmed 2019. Changed: - nuxt.config.ts schemaOrg.identity.foundingDate: '2021' -> '2019-09-01' (the full date the identity used before 268ee96) - content/about/de/home.json: "2021 als ... gegründet" -> "2019" - content/about/en/home.json: "Founded in 2021" -> "Founded in 2019" tests/about/founding-year.spec.ts asserts that every source names 2019. Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX * fix(team): give team profiles a real meta description The profile page used the member's slogan as meta description when the member had no bio. blndr2 has only the slogan "Modrinth.", so the live page ships <meta name="description" content="Modrinth."> on https://onelitefeather.net/de/team/blndr2. New rule, applied to the page description, the Person JSON-LD and the OG card: - the bio, when there is one - otherwise the slogan, only if it is a sentence of at least 50 characters ending in . ! or ? - otherwise a sentence built from the role (or the rank label when there is no role), via team.profile_description_in_area / _plain in the i18n files The title appends the role when the member has one ("joltras – Entwicklung"). Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX * fix(team): word the generated profile description naturally Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
Keep both the absolute POI image from this branch and the Place link from #469. Claude-Session: https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX
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.
Summary
Three corrections to the schema.org and canonical URLs emitted by the event detail, community POI and team pages. Each is a fix for output that was wrong on the live site; the additions from the same review are in
feat/seo-structured-data.Changes
Four commits, one per defect:
f99f9dd): the event detail page emittedeventStatus: EventScheduledfor every phase, so a past event still claimed to be upcoming. schema.org has no "completed" status, so a past event now carries noeventStatus. Announced, running and hidden (unlisted, not yet started) events keepEventScheduled. The mapping islayers/events/utils/eventSchemaStatus.tsand takes the phase the server already decided inuseEventDetail, so no clock is read on the page.9990726): the POICreativeWorktook the raw frontmatter thumbnail (/images/...) as a relative URL. In production those images are served throughimg.onelitefeather.net, so the path ononelitefeather.netreturns 404.usePageSeonow returns thesocialImageit already computes forog:image, and the POI page uses that URL, so both tags share one resolution.5a32292): the POI breadcrumb pointed at/de/community-poi/, which 301s to the canonical/de/community-poi. The POI back link and the team fallback URLs (ItemListon the team index,Personon a member without a slug) had the same slash and are corrected too.tests/seo/breadcrumb-urls.spec.tsnow checks every locale-prefixed template literal ending in/, not only the locale root.79225f4): the event detailEvent.imagehad the same defect as the POI image (new URL(event.value.thumbnail, site.url)on the raw/imagespath). It now uses thesocialImagefromusePageSeo, the same URL asog:image, and is omitted when there is no thumbnail.Tests
tests/events/event-schema-status.spec.ts,tests/seo/poi-jsonld-image.spec.ts,tests/seo/event-jsonld-image.spec.ts; extendedtests/seo/breadcrumb-urls.spec.ts.pnpm test: 156 files, 1236 tests passed on this branch.pnpm quality: no regression (ESLint errors 186, warnings 17, TypeScript errors 3, same as baseline).pnpm build: passes.The rendered JSON-LD and the live redirects were not re-checked in this PR; the findings come from the review of the live site, and the changes are covered by the unit specs above.
https://claude.ai/code/session_019zws4ceJu2jctk3XT48fMX