From 6a2839c1c412b302c55ff5dd3e11dc86fae22d75 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 10 Jul 2026 19:14:09 -0700 Subject: [PATCH] Keep public corpus links under the site base. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defense: wiki/interface/site.md now requires sync-generated corpus links to preserve the production /misaligned base, so valid internal routes do not escape to the domain root. 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta Code --- site/README.md | 2 + site/package.json | 4 +- site/scripts/sync-wiki.mjs | 24 ++++++++++- wiki/interface/site.md | 27 +++++++++++-- .../log/2026-07-10-public-route-base-links.md | 40 +++++++++++++++++++ wiki/log/DEVLOG.md | 5 +++ 6 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 wiki/log/2026-07-10-public-route-base-links.md diff --git a/site/README.md b/site/README.md index d76a3d37..f665559c 100644 --- a/site/README.md +++ b/site/README.md @@ -38,6 +38,8 @@ Every site asset/page must have a source clause, per `wiki/process/living-spec.m - Generated `src/content/docs/`, `src/content/assets/`, `src/generated/`, and `dist/` — outputs of `scripts/sync-wiki.mjs` / Astro, justified by `wiki/process/wiki.md` and `wiki/process/workflows.md`; never hand-edit them. + Sync rewrites corpus-internal markdown links under `SITE_BASE` (production + default `/misaligned`) so public routes match Tangled sub-path hosting. ## Commands diff --git a/site/package.json b/site/package.json index 29a0a3af..92750d20 100644 --- a/site/package.json +++ b/site/package.json @@ -5,10 +5,10 @@ "private": true, "scripts": { "sync": "node scripts/sync-wiki.mjs", - "dev": "node scripts/sync-wiki.mjs && SITE_BASE=/ node ./node_modules/astro/bin/astro.mjs dev", + "dev": "SITE_BASE=/ node scripts/sync-wiki.mjs && SITE_BASE=/ node ./node_modules/astro/bin/astro.mjs dev", "start": "pnpm dev", "build": "node scripts/sync-wiki.mjs && node ./node_modules/astro/bin/astro.mjs build", - "build:local": "node scripts/sync-wiki.mjs && SITE_BASE=/ node ./node_modules/astro/bin/astro.mjs build", + "build:local": "SITE_BASE=/ node scripts/sync-wiki.mjs && SITE_BASE=/ node ./node_modules/astro/bin/astro.mjs build", "preview": "node ./node_modules/astro/bin/astro.mjs preview", "astro": "node ./node_modules/astro/bin/astro.mjs" }, diff --git a/site/scripts/sync-wiki.mjs b/site/scripts/sync-wiki.mjs index b82ebb93..951291bf 100755 --- a/site/scripts/sync-wiki.mjs +++ b/site/scripts/sync-wiki.mjs @@ -123,6 +123,17 @@ function yamlEscape(s) { return s; } +/** + * Site base prefix without trailing slash. Empty string when the site is + * served at domain root (local `SITE_BASE=/`). Production Tangled sub-path + * hosting defaults to `/misaligned` — same default as astro.config.mjs. + */ +function siteBasePrefix() { + const raw = process.env.SITE_BASE ?? '/misaligned'; + if (!raw || raw === '/') return ''; + return raw.endsWith('/') ? raw.slice(0, -1) : raw; +} + /** wiki-relative path → Starlight slug (no leading/trailing slash). */ function wikiRelToSlug(relPosix) { const normalized = path.posix.normalize(relPosix); @@ -138,6 +149,13 @@ function wikiRelToSlug(relPosix) { return slug.toLowerCase(); } +/** Starlight slug → public URL under SITE_BASE (trailing slash). */ +function slugToPublicUrl(slug) { + const prefix = siteBasePrefix(); + if (!slug) return `${prefix}/`; + return `${prefix}/${slug}/`; +} + function destFileForWikiRel(relPosix) { if (relPosix === 'DESIGN.md') return path.join(DOCS_OUT, 'constitution.md'); if (relPosix === 'SUMMARY.md') return null; @@ -202,7 +220,11 @@ function rewriteMarkdownLinks(body, fromWikiRel) { if (targetWikiRel === 'SUMMARY.md') return text; const slug = wikiRelToSlug(targetWikiRel); - const url = `/${slug}/`; + // Must include SITE_BASE. Root-absolute `/slug/` links 404 on Tangled + // sub-path hosting (cameron.tngl.io/misaligned/...), where the public + // route is `/misaligned/slug/`. Starlight prefixes its own chrome; body + // markdown is emitted as written, so the sync owns the base. + const url = slugToPublicUrl(slug); const withHash = hash ? `${url}#${hash}` : url; return `[${text}](${withHash})`; }); diff --git a/wiki/interface/site.md b/wiki/interface/site.md index 3d74492c..0fecd86a 100644 --- a/wiki/interface/site.md +++ b/wiki/interface/site.md @@ -10,7 +10,9 @@ Status note: docs theme implemented 2026-07-08 (site/src/styles/ (the illustration plate: porcelain|ink bisect, monolith, blood pool) is adopted by wiki/art/visual-identity.md as the public identity plate. Changes to how the public site looks amend this spec first. On 2026-07-09 the corpus role - became visible on every rendered document. + became visible on every rendered document. On 2026-07-10 corpus-internal + markdown links are rewritten under SITE_BASE so production sub-path + routes (e.g. /misaligned/vision/player-contract/) resolve. Stage: Process Design: - wiki/art/visual-identity.md#visual-identity-clinical-gore @@ -85,6 +87,14 @@ Everything below derives from it. define current design. The legacy root route says NAVIGATION and explicitly carries no design authority. Role differences use border weight/pattern and bone/chrome contrast; amber and crimson keep their existing meanings. +- **Corpus routes honor the production base.** The public site is a Tangled + sub-path (`base: /misaligned` in production; `SITE_BASE=/` for local root + preview). Wiki-to-wiki markdown links rewritten by the sync must include + that base so a corpus link to the player contract resolves at + `/misaligned/vision/player-contract/` (not the domain-root + `/vision/player-contract/`, which is outside the site). Starlight chrome + (sidebar, pagination) already prefixes `base`; body markdown does not, + so the sync owns base-correct hrefs. ## Where it is implemented @@ -92,10 +102,14 @@ Everything below derives from it. as Starlight token overrides plus component styling (header, sidebar, content, role banners, tables, asides, TOC, pagination, search). - `site/scripts/sync-wiki.mjs` — validates one supported Type line per wiki - page, turns it into the visible role banner, and labels the root doorway. -- `site/astro.config.mjs` — IBM Plex `head` links for all doc pages. + page, turns it into the visible role banner, labels the root doorway, and + rewrites corpus-internal markdown links under `SITE_BASE` (default + `/misaligned`). +- `site/astro.config.mjs` — `base` from `SITE_BASE`, IBM Plex `head` links + for all doc pages. - `site/src/pages/index.astro` — the splash; carries the same tokens - inline and the reference composition. + inline and the reference composition. Splash CTAs already use + `import.meta.env.BASE_URL`. ## Acceptance criteria @@ -112,3 +126,8 @@ Everything below derives from it. no page opting out. 7. Every rendered document displays its authority role before the body; LOG and NAVIGATION explicitly disclaim current design authority. +8. Production build (`./tools/site-build.sh`, base `/misaligned`) emits + corpus-internal body links under `/misaligned/…/`. In particular, + links to the player contract resolve to + `/misaligned/vision/player-contract/`, and `site/dist/vision/player-contract/index.html` + exists. Local `SITE_BASE=/` builds keep root-absolute `/…/` links. diff --git a/wiki/log/2026-07-10-public-route-base-links.md b/wiki/log/2026-07-10-public-route-base-links.md new file mode 100644 index 00000000..2d2ba8a3 --- /dev/null +++ b/wiki/log/2026-07-10-public-route-base-links.md @@ -0,0 +1,40 @@ +# 2026-07-10 — Fix public corpus links under SITE_BASE + +``` +Type: log +``` + +## Intent + +Diagnose and fix the public-route failure where corpus-internal links to +pages such as the player contract did not resolve under the production +Tangled base path `/misaligned`. + +## Finding + +`site/scripts/sync-wiki.mjs` rewrote wiki-to-wiki markdown links as +root-absolute `/slug/` URLs (for example `/vision/player-contract/`). +Starlight chrome already prefixes Astro `base`, but body markdown is +emitted as written. On Tangled sub-path hosting the real public route is +`/misaligned/vision/player-contract/`; the base-less href hits domain root +and returns Not Found. + +## Changed + +- `site/scripts/sync-wiki.mjs`: rewrite corpus links via `SITE_BASE` + (default `/misaligned`, empty when `SITE_BASE=/`). +- `site/package.json`: pass `SITE_BASE=/` into sync for `dev` and + `build:local` so local root previews stay consistent. +- `wiki/interface/site.md`: binding clause and acceptance criterion for + base-correct corpus routes. + +## Checks + +`./tools/site-build.sh` (production base). Verify +`site/dist/vision/player-contract/index.html` exists and built HTML body +links use `/misaligned/vision/player-contract/`. + +## Spec impact + +Amends `wiki/interface/site.md` (public site routing under production +base). No game-rule change. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index ec9c16a5..8724e650 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -111,6 +111,11 @@ add or amend a session log, then re-run the generator. - Intent: The README still named continuous witness/narration as the next priority even though the narration spec is IMPLEMENTED and ROADMAP item #30 is complete. - Log: [wiki/log/2026-07-10-readme-status.md](2026-07-10-readme-status.md) +## 2026-07-10 - Fix public corpus links under SITE_BASE + +- Intent: Diagnose and fix the public-route failure where corpus-internal links to pages such as the player contract did not resolve under the production Tangled base path `/misaligned`. +- Log: [wiki/log/2026-07-10-public-route-base-links.md](2026-07-10-public-route-base-links.md) + ## 2026-07-10 - Project operations doorway - Intent: Make the project's live state, dispatch rules, reproducible evidence, and task lifecycle easy to discover and hard to let drift, while keeping each binding spec as the single owner of its work status. -- 2.51.2