Page Routing and SEO — SPEC

Start here → AGENTS.md · SPEC_INDEX.md · ARCHITECTURE.md.

Metadata

FieldValue
Module idpage-routing-and-seo
Source path(s)src/pages/, src/layouts/, src/lib/seo.ts, src/lib/sitemap*.ts
Doc kindModule spec
Coverage score100% assessed 2026-07-24; 14/14 mandatory fields PRESENT, focused ASP route evidence added
Generated frommodule-spec @ 0.2.1
generated_by / approved_by / updated_atcodex (gpt-5.6) / repository owner scope approval / 2026-07-24
Validation statuspass-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)

Evidence Rules

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 Register

Source materialScopeDecisionDisposition
Existing ASP listing specificationpage behaviorverified/correctedCurrent /asp-apps behavior is represented in requirements and routing flow
Current route tree and SEO testsimplementation/testsauthoritativeRequirements, public surface, error handling, test strategy

Overview

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.

Purpose / Responsibility

Owns URL-to-handler mapping and indexable metadata for the current Astro application; it does not own external data normalization or browser interaction state.

Stack

Astro 6 SSR routes, TypeScript endpoint handlers, Astro layouts, Vitest route/SEO tests, Playwright higher-tier checks.

Folder / Package Structure

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

Key Files (source of truth)

FileHolds
src/pages/Actual route existence and method handlers
astro.config.mjsSSR output, site origin, static sitemap and redirects
src/lib/seo.tsMetadata defaults and normalization
src/pages/sitemap.xml.tsDynamic sitemap generation/cache/compression
src/lib/sitemap-config.tsStatic page catalog
src/lib/middleware/permanent-redirects.tsCanonical 301 redirects

Public Surface

Contract IDTypeSurfacePurposeCompatibilityDetailRoot index
page.catalogHTTPGET Astro page routesBrowse/search catalogURL and redirect stabilitysrc/pages/CONTRACTS.md
seo.robotsHTTPGET /robots.txtCrawler policyText/status stabilitysrc/pages/robots.txt.tsCONTRACTS.md
seo.sitemapHTTPGET /sitemap.xmlDiscover public URLsXML/gzip compatibilitysrc/pages/sitemap.xml.tsCONTRACTS.md
catalog.searchHTTPGET /api/searchServer-side catalog searchQuery/result compatibilitysrc/pages/api/search.tsCONTRACTS.md

Compatibility notes:

Requires (dependencies)

Synchronized catalog/category data, Redis, page layouts/components, SEO helpers, middleware redirects/security, and configured site host.

Requirements

IDWHATWHYSource EvidenceTest / Example EvidenceGapsConfidence
PAGE-R-001Astro 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].astrotests/unit/lib/listing-app.test.tsnonePRESENT
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.tsplaywright/tests/functional/navigation/collection-routes.spec.tsnonePRESENT
PAGE-R-003Page 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.astrotests/unit/lib/seo.test.ts, tests/unit/lib/seo-spec-constants-drift.test.tsnonePRESENT
PAGE-R-004Robots 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.tstests/unit/pages/robots-txt.test.ts, tests/unit/pages/sitemap-generation.test.tsnonePRESENT
PAGE-R-005Permanent 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.tstests/unit/config/permanent-redirects.test.tsnonePRESENT

Design Overview

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.

Data Flow

flowchart LR
  Request --> Middleware --> Route["Astro route"]
  Route --> Data["sync/SEO helpers"]
  Data --> Render["layout + components"]
  Render --> Response
  Data --> Sitemap["XML generation/cache"]

Sequence Diagram(s)

Operation groupDiagramFailure coverage
SSR pagePage renderMissing data selects empty/404 behavior
SitemapSitemap generationCache 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

Class / Component Relationships

classDiagram
  class AstroRoute
  class Layout
  class SeoHelpers
  class SyncReaders
  AstroRoute --> Layout
  AstroRoute --> SeoHelpers
  AstroRoute --> SyncReaders

Use Cases

Business Rules & Invariants

Concurrency & Reactive Flow

UI Flow

Error Handling & Failure Modes

ConditionSignalRecovery
Invalid/missing dynamic ID404 or route-specific empty resultCorrect URL; do not call upstream with unchecked ID
Search/index unavailableAPI error/fallback resultRetry after sync; inspect sync health
Sitemap source unavailableValid response from available dataRegenerate after sync
Unknown route/probe404 from middleware/AstroNo retry unless URL corrected

Pitfalls

Module Do’s / Don’ts

Key Design Trade-off

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.

Test-Case Strategy (module)

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.

BehaviorExisting test evidenceGap
PAGE-R-003tests/unit/lib/seo.test.tsnone
PAGE-R-004robots/sitemap unit suitesnone
PAGE-R-005permanent redirect testsnone
ASP listing SSRplaywright/tests/functional/navigation/collection-routes.spec.tsnone

Traceability