Start here →
AGENTS.md·SPEC_INDEX.md·ARCHITECTURE.md.
| Field | Value |
|---|---|
| Module id | page-routing-and-seo |
| Source path(s) | src/pages/, src/layouts/, src/lib/seo.ts, src/lib/sitemap*.ts |
| Doc kind | Module spec |
| Coverage score | 100% assessed 2026-07-24; 14/14 mandatory fields PRESENT, focused ASP route evidence added |
| Generated from | module-spec @ 0.2.1 |
| generated_by / approved_by / updated_at | codex (gpt-5.6) / repository owner scope approval / 2026-07-24 |
| Validation status | pass-with-warnings; validator claude-opus-4-8-1m (runtime independent of codex), target HEAD b9c7321ee3642bdb0137734e187a102a7f5de9be, assessed 2026-07-24; 0 Blocking, 0 Important, 0 Medium, 2 Minor; full suite environment-blocked (Node 24 + registry pending) |
Behavioral claims cite current route/config/test files. Preserved migration specs supplied route intent and acceptance cases, but do not override the Astro file tree.
| Source material | Scope | Decision | Disposition |
|---|---|---|---|
| Existing ASP listing specification | page behavior | verified/corrected | Current /asp-apps behavior is represented in requirements and routing flow |
| Current route tree and SEO tests | implementation/tests | authoritative | Requirements, public surface, error handling, test strategy |
Astro file-based routing under src/pages/ owns public pages, same-origin APIs, operational endpoints, authentication callbacks, media proxies, robots policy, and sitemap output. Shared layouts apply metadata and page chrome; src/lib/seo.ts centralizes title, description, canonical URL, and social-image construction.
Owns URL-to-handler mapping and indexable metadata for the current Astro application; it does not own external data normalization or browser interaction state.
Astro 6 SSR routes, TypeScript endpoint handlers, Astro layouts, Vitest route/SEO tests, Playwright higher-tier checks.
src/pages/
├── *.astro # public SSR pages
├── applications/[appId] # dynamic app detail
├── category/[category] # category listing
├── api/ # same-origin JSON/action routes
├── auth/ # App Hub OAuth
├── integration/ # native integration OAuth
└── sitemap*.ts, robots.txt.ts, ping.ts, getImage.ts
| File | Holds |
|---|---|
src/pages/ | Actual route existence and method handlers |
astro.config.mjs | SSR output, site origin, static sitemap and redirects |
src/lib/seo.ts | Metadata defaults and normalization |
src/pages/sitemap.xml.ts | Dynamic sitemap generation/cache/compression |
src/lib/sitemap-config.ts | Static page catalog |
src/lib/middleware/permanent-redirects.ts | Canonical 301 redirects |
| Contract ID | Type | Surface | Purpose | Compatibility | Detail | Root index |
|---|---|---|---|---|---|---|
page.catalog | HTTP | GET Astro page routes | Browse/search catalog | URL and redirect stability | src/pages/ | CONTRACTS.md |
seo.robots | HTTP | GET /robots.txt | Crawler policy | Text/status stability | src/pages/robots.txt.ts | CONTRACTS.md |
seo.sitemap | HTTP | GET /sitemap.xml | Discover public URLs | XML/gzip compatibility | src/pages/sitemap.xml.ts | CONTRACTS.md |
catalog.search | HTTP | GET /api/search | Server-side catalog search | Query/result compatibility | src/pages/api/search.ts | CONTRACTS.md |
Compatibility notes:
Synchronized catalog/category data, Redis, page layouts/components, SEO helpers, middleware redirects/security, and configured site host.
| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Gaps | Confidence |
|---|---|---|---|---|---|---|
PAGE-R-001 | Astro MUST derive routes from src/pages/; dynamic parameters MUST be validated before data lookup. | Prevents invented routes and unsafe identifiers. | src/pages/applications/[appId].astro, src/pages/category/[category].astro | tests/unit/lib/listing-app.test.ts | none | PRESENT |
PAGE-R-002 | /asp-apps MUST render the ASP/agentic listing using current synchronized data and the shared listing layout. | Preserves the shipped listing contract without reviving legacy routing. | src/pages/asp-apps.astro, src/lib/fetch-synced-data.ts | playwright/tests/functional/navigation/collection-routes.spec.ts | none | PRESENT |
PAGE-R-003 | Page metadata MUST use centralized SEO defaults, bounded descriptions, absolute canonical/social URLs, and route-specific values. | Prevents inconsistent or invalid index metadata. | src/lib/seo.ts, src/layouts/BaseLayout.astro | tests/unit/lib/seo.test.ts, tests/unit/lib/seo-spec-constants-drift.test.ts | none | PRESENT |
PAGE-R-004 | Robots and sitemap output MUST exclude non-indexable auth/API/error/search surfaces and include current public static/dynamic URLs. | Keeps crawler output aligned with user-facing content. | astro.config.mjs, src/pages/robots.txt.ts, src/pages/sitemap.xml.ts | tests/unit/pages/robots-txt.test.ts, tests/unit/pages/sitemap-generation.test.ts | none | PRESENT |
PAGE-R-005 | Permanent legacy URLs MUST resolve through the centralized redirect definitions before normal route handling. | Maintains inbound-link compatibility. | src/lib/middleware/permanent-redirects.ts, src/middleware.ts | tests/unit/config/permanent-redirects.test.ts | none | PRESENT |
Routes are declarative files. Pages obtain server data before rendering and pass serializable initial state to islands. Static sitemap discovery is configured in Astro; the custom sitemap merges static pages with synchronized app/category URLs, normalizes/escapes them, caches the result, and serves compressed bytes when supported.
flowchart LR
Request --> Middleware --> Route["Astro route"]
Route --> Data["sync/SEO helpers"]
Data --> Render["layout + components"]
Render --> Response
Data --> Sitemap["XML generation/cache"]
| Operation group | Diagram | Failure coverage |
|---|---|---|
| SSR page | Page render | Missing data selects empty/404 behavior |
| Sitemap | Sitemap generation | Cache miss and unavailable data remain valid output paths |
sequenceDiagram
participant B as Browser
participant M as Middleware
participant P as Astro page
participant R as Redis/sync helpers
B->>M: GET page
M->>P: next()
P->>R: read normalized data
alt route entity exists
R-->>P: data
P-->>B: SSR HTML + metadata
else missing/invalid
R-->>P: null
P-->>B: 404/empty contract
end
sequenceDiagram
participant C as Crawler
participant S as Sitemap route
participant R as Redis/sync readers
C->>S: GET /sitemap.xml
S->>S: check process cache
alt cache miss
S->>R: read apps and categories
R-->>S: current snapshot or unavailable subset
S->>S: merge static/dynamic, normalize, escape, compress
end
S-->>C: XML or gzip XML
classDiagram
class AstroRoute
class Layout
class SeoHelpers
class SyncReaders
AstroRoute --> Layout
AstroRoute --> SeoHelpers
AstroRoute --> SyncReaders
src/pages/asp-apps.astro, src/lib/fetch-synced-data.ts./applications/:appId → identifier lookup → detail or not-found response. Evidence: src/pages/applications/[appId].astro.src/pages/robots.txt.ts, src/pages/sitemap.xml.ts.astro.config.mjs and robots/sitemap code.| Condition | Signal | Recovery |
|---|---|---|
| Invalid/missing dynamic ID | 404 or route-specific empty result | Correct URL; do not call upstream with unchecked ID |
| Search/index unavailable | API error/fallback result | Retry after sync; inspect sync health |
| Sitemap source unavailable | Valid response from available data | Regenerate after sync |
| Unknown route/probe | 404 from middleware/Astro | No retry unless URL corrected |
src/pages/, not the legacy API inventory.site is derived from configured host in astro.config.mjs; wrong environment host produces wrong canonicals.File-based routing keeps route ownership visible and build-checked, while custom sitemap generation accepts extra runtime complexity to include Redis-backed dynamic catalog URLs.
Unit-test SEO normalization, redirects, robots, sitemap escaping/deduplication/compression, and route helpers. Use build and visual/E2E suites for representative SSR routes and navigation.
| Behavior | Existing test evidence | Gap |
|---|---|---|
PAGE-R-003 | tests/unit/lib/seo.test.ts | none |
PAGE-R-004 | robots/sitemap unit suites | none |
PAGE-R-005 | permanent redirect tests | none |
| ASP listing SSR | playwright/tests/functional/navigation/collection-routes.spec.ts | none |
ARCHITECTURE.mdCONTRACTS.md.sdd/manifest.json