Sitemap Generation And Robots Validation
Design Log #175 — Sitemap Generation and robots.txt Validation
Written for AI agents. See Log Methodology Note below for details.
Background
Jay Stack projects serve static files from public/ — Vite serves them in dev, and the build pipeline copies them to the frontend root for production (DL#134a). Files like robots.txt can be placed in public/ and work in both environments.
However, sitemap.xml needs to list all concrete page URLs. The framework already enumerates these during production builds (route scanning + loadParams + cross-product + dedup), but this capability isn't exposed as a standalone feature.
Currently there is no validation that robots.txt or sitemap.xml exist, and no tooling to generate a sitemap.
Production dynamism
In production, pages aren't static — the slow render server (DL#134c) can rebuild instances when data changes. When a new product is added or removed, POST /_jay/rebuild creates or updates instances, and the route-manifest.json is atomically rewritten with the new instance list. build-metadata.json is also updated, triggering the main server to reload.
This means a build-time sitemap goes stale whenever instances change. The route-manifest.json is the live source of truth for what pages exist.
Problem
- No sitemap generation — Projects must manually create and maintain
sitemap.xml, which is error-prone as routes change - Sitemap staleness — Even if generated at build time, the sitemap becomes stale when instances are added/removed via rebuild
- No robots.txt validation — Missing
robots.txtis an SEO gap that goes undetected - No
site.baseUrlvalidation — If configured, it should be validated; if missing, sitemap features should warn
Design
Part 1: Dynamic sitemap served by the main server
Instead of a static public/sitemap.xml, the main server serves /sitemap.xml dynamically from the route manifest. This way it's always in sync with the live set of pages.
How it works
The main server already loads route-manifest.json and reloads it when build-metadata.json changes. Serving the sitemap means:
- Register a handler for
GET /sitemap.xml - On manifest load/reload, generate
sitemap.xmlto a temp file in the build directory, then rename into the frontend directory - On
GET /sitemap.xml, serve the file (or let the static file handler serve it)
Streaming and caching
Sitemaps can be large (thousands of URLs). Instead of building an XML string in memory:
- Write to disk on manifest load/reload — generate
sitemap.xmlas a file in the frontend directory (where static files are served from) - The existing static file handler serves it — no special response logic needed
- On rebuild, regenerate the file (atomic write via temp + rename)
Base URL
Required config in jay-stack.config.yaml:
site:
baseUrl: https://example.com
If not configured, /sitemap.xml is not generated.
noindex in manifest
Add a noindex: true flag to RouteEntry in the route manifest. Populated at build time by statically checking the page's jay-html for <meta name="robots" content="noindex">. Only static noindex is supported — dynamic noindex (from ViewState bindings) is not detected.
Routes with noindex: true are excluded from the sitemap.
Where to detect: During the build pipeline, after parsing each page's jay-html, check headMeta for a robots meta tag containing "noindex". Store the flag on the RouteEntry.
Filtering
- Exclude routes with
devOnly: true - Exclude routes with
noindex: true
Dev server
The dev server returns a placeholder for /sitemap.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Sitemap is generated by the production server from the route manifest. -->
<!-- Run jay-stack build && jay-stack serve to see the full sitemap. -->
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" />
This avoids the overhead of running loadParams in dev just for a sitemap, while still making /sitemap.xml resolve (useful for testing robots.txt references).
Part 2: jay-stack sitemap CLI command
For projects that want a static sitemap (e.g., static hosting without a main server), a CLI command that writes public/sitemap.xml. Uses the same URL enumeration pipeline as the build:
scanRoutes()+ plugin routes (excludedevOnly)loadParams()for dynamic routescrossProductParams()+materializeRouteParams()+dedupeByUrl()(all fromparam-routing.ts)
Writes as a stream to public/sitemap.xml. Optionally runs as part of jay-stack build if site.baseUrl is configured.
Part 3: Validation
robots.txt existence
Project-level warning in jay-stack validate when public/robots.txt is missing.
Where: In validate.ts, after the per-file validation loop but before plugin validators.
Severity: warning
Message:
⚠ public/robots.txt not found — search engines may crawl pages you don't intend to expose.
Create public/robots.txt with at minimum:
User-agent: *
Allow: /
Sitemap: https://your-domain.com/sitemap.xml
Where NOT to add: Not in the SEO plugin — robots.txt is a project-level concern, not per-page.
site.baseUrl configuration
Warning in jay-stack validate when site.baseUrl is not configured:
⚠ site.baseUrl not configured — sitemap.xml will not be generated.
Add to jay-stack.config.yaml: site: { baseUrl: "https://your-domain.com" }
Implementation Plan
Phase 1: Validation (standalone, no dependencies)
- Add
site.baseUrlto config schema (config.ts) - In
validate.ts, add project-level checks:- Warn if
public/robots.txtmissing - Warn if
site.baseUrlnot configured
- Warn if
Phase 2: noindex in manifest
- During build, detect static
<meta name="robots" content="noindex">from parsed jay-htmlheadMeta - Add
noindex?: booleantoRouteEntrytype - Populate in
build-pipeline.tswhen building route entries
Phase 3: Sitemap generation in main server
- On manifest load/reload, generate
sitemap.xmlfrom routes × instances usingbuildUrl() - Stream-write to
frontend/sitemap.xml(atomic via temp + rename) - Skip routes with
devOnlyornoindex - Skip if
site.baseUrlnot in config
Phase 4: Dev server placeholder
- Add
GET /sitemap.xmlhandler that returns the placeholder XML
Phase 5: jay-stack sitemap CLI command (for static hosting)
- Extract URL enumeration from build pipeline into reusable
enumerateUrls() - Add
sitemapcommand to CLI - Optionally integrate into
jay-stack build
Verification Criteria
jay-stack validatewarns whenpublic/robots.txtis missingjay-stack validatewarns whensite.baseUrlis not configured- Production main server serves
/sitemap.xmlwith all current page URLs - After a rebuild adds/removes instances,
/sitemap.xmlreflects the change - Pages with static
<meta name="robots" content="noindex">are excluded devOnlyroutes are excluded- Dev server returns placeholder XML for
/sitemap.xml jay-stack sitemapgenerates a validpublic/sitemap.xmlfor static hosting
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.