The public site — a self-contained Vite + React 19 + TypeScript + Tailwind v4 workspace,
and this repository's only code. It never writes into docs/, which is roadkeep's, and it
reads from there rather than copying it.
npm install # once
npm run dev # dev server at /polyweave/
npm run build # generate → tsc → client → SSR → og image + prerender
npm test # the site's own claims, against what the build produced
npm run typecheck # tsc -b, no emit
npm run preview # serve the built dist/
npm run build is the gate, and it is one command on purpose: it regenerates the roadmap
module from docs/ROADMAP.md, type-checks, builds the client, rasterises the social card,
builds the SSR bundle and prerenders every route with its Markdown twin, its
manifest.json, its sitemap.xml and its robots.txt. A drifted <head> template, a
route with no page, or a feature record naming a block the roadmap has lost all fail it.
npm test then asserts the built output, so it runs after the build rather than instead of
it.
The chrome is the design system's site layer, @viglet/viglet-design-system pinned in
package.json: the nav, the hero, the section heads, the buttons, the copy button, the
sponsor block, the footer and the theme toggle come from ./site, the ground from
./tokens.css on data-tone="cool", their styles from ./site.css, and the prerender and
the social card's rasteriser from ./vite. What src/index.css keeps is the product's own:
the brand re-keyed to the hub's teal, the mark's violets for the lattice, and the sections.
GitHub Pages derives the base path from the repository name, so Vite's base is
/polyweave/ and every asset path carries that prefix. It is written twice — in
vite.config.ts and in src/routes.tsx — and nowhere else.
It sells polyweave as the roadmap leaves it, and never claims a result no run has measured.
The landing page is a pitch for the finished product — every block built — written for a person who has never heard of polyweave and has a few seconds to see what it is for. So:
- It states no status. A count typed or derived into the pitch either goes stale or turns
the page into a status report; what is still open lives in
docs/ROADMAP.md, which the footer links.scripts/prerender.test.mjsrefuses an open-line count on the landing and requires the install commands and every roadmap non-goal to reach it. - It is pictures first: tiles, a hub diagram, a review-page mock, one sentence each. Claude Code is the operator and the page is about what it gains, so there is no separate "Claude Code" item in the nav.
scripts/lint.test.mjsrefuses copy that claims a measured result for polyweave ("faster than", "we measured", "benchmarked"). Describing what a feature does is the pitch; quoting a number nobody recorded is not.
| Path | What |
|---|---|
src/lib/site-content.ts |
All copy — sections only render it, so a claim is one array element a reviewer can check |
src/lib/roadmap.generated.ts |
Generated from docs/ROADMAP.md through roadkeep: the blocks, the lines and the non-goals. Committed, because roadkeep is not |
src/lib/roadmap.ts |
The typed read over it: block titles for the depth pages, and the non-goals |
src/lib/features.ts |
One record per roadmap block — route, <head> and page, declared together |
src/lib/diagrams.ts |
The depth pages' SVGs and the session transcript, kept verbatim as figures |
src/components/ui/Icon.tsx |
The line icons the landing tiles use, drawn in the current text colour |
src/components/Chrome.tsx |
The nav and the footer every route shares, drawn by the design system's site layer |
src/components/Consent.tsx |
The analytics banner: Google Analytics loads only after the visitor allows it |
vite.config.ts pre-paint script + THEME_KEY |
The theme follows the OS, a stored choice overrides it, applied before first paint |
src/routes.tsx |
The route table and its metadata, asserted against each other at import time |
src/components/sections/ |
One component per landing section; the composition lives here |
src/pages/Landing.tsx |
The landing page — the section order is the pitch |
scripts/ |
The generator, the prerender, the social card and the tests that read dist/ |
.roadkeep is in this repository's .gitignore, so a clean checkout has the roadmap but
not the tool that reads it. npm run generate refreshes src/lib/roadmap.generated.ts
where roadkeep is reachable and leaves the committed file alone where it is not, so the
build works either way.
scripts/roadmap.test.mjs is what keeps that honest: with roadkeep present it regenerates
into memory and asserts the committed file is byte-identical, so a line added, shipped,
renumbered or reworded without a regenerate fails. Without roadkeep it checks the committed
module against itself — unique ids, every dependency resolving, every block declared.
Set $ROADKEEP to a command if yours is not at .roadkeep/scripts/roadkeep.py or on PATH.
No third-party fonts fetched at page load (the product claims nothing leaves your machine; the site adds no fetch of its own), and no analytics unless the visitor allows it: the tag is not in the HTML, and the banner the hub runs is what loads it. Inter and JetBrains Mono are named with system fallbacks rather than pulled from a CDN.