Capability Report: Canonical Surface Refactoring
Date: 2026-04-09 Author: Q (Quality System) Project: Unified Canonical Architecture (FR-7 + FR-9) Status: Operational
What Was Built
A typed Sanity query layer (apps/website/src/lib/sanity/methodology.ts) and a 42-file surface refactoring that together eliminated hardcoded presentation data from the website. Every manifesto presentation page (10), trap presentation page (10), Value Path stage page (8), stage embed page (8), Value Path hub page (4), and presentation layout (4) now sources its metadata from Sanity CMS at request time through methodology.ts, instead of importing from static TypeScript data files.
Three hardcoded files were deleted as a result: manifesto-data.ts and trap-data.ts were removed entirely; stage-data.ts was moved to src/remotion/data/ where it serves only the Remotion video generation pipeline (which runs offline and does not benefit from live CMS queries).
What Problem It Solves
Before this work, methodology presentation pages imported their configuration from three hardcoded TypeScript files in src/data/:
manifesto-data.ts-- Static objects for 10 manifesto presentations (title, subtitle, description, prev/next navigation)trap-data.ts-- Static objects for 10 trap presentations (trap number, title, subtitle, navigation)stage-data.ts-- Static objects for 8 Value Path stage presentations (stage number, title, mantra, phase, navigation)
These files duplicated content that already existed in Sanity CMS. When a methodology name changed in Sanity (e.g., a trap's oneLineDefinition was updated), the presentation page continued showing the old hardcoded value. There was no mechanism to detect this drift because the presentation layer and the CMS were completely disconnected.
Additionally, every time a new manifesto or trap was added to Sanity, a developer had to manually create a matching entry in the corresponding data file. Forgetting this step produced a presentation page with no metadata -- no title, no subtitle, no prev/next navigation.
The Query Layer: methodology.ts
methodology.ts (433 lines) provides six async functions and one convenience aggregator:
Query Functions
| Function | Returns | GROQ Source |
|---|---|---|
getManifestoPresentations() |
ManifestoPresentation[] (10 items) |
valueReality documents where number <= 10 |
getManifestoPresentation(slug) |
Single ManifestoPresentation |
Same query, filtered client-side |
getTrapPresentations() |
TrapPresentation[] (10 items) |
trap documents where category == "core-framework" |
getTrapPresentation(slug) |
Single TrapPresentation |
Same query, filtered client-side |
getStagePresentations() |
StagePresentation[] (8 items) |
All valuePathStage documents |
getStagePresentation(slug) |
Single StagePresentation |
Same query, filtered client-side |
getAllStages() |
StageSummary[] (8 items) |
Same stage query, minimal shape |
getAllMethodologyPresentations() |
All three arrays in parallel | Promise.all of the above |
Type System
The module exports six TypeScript interfaces that match the shapes the presentation layouts already consume:
ManifestoPresentation-- slug, sanitySlug, title, subtitle, description, commitmentCount, slideCount, prev/next navigationTrapPresentation-- trapNumber, slug, sanitySlug, title, subtitle, description, slideCount, prev/next navigationStagePresentation-- stageNumber, slug, title, subtitle, mantra, phase (Path TO/OF Value), themeClass, prev/next navigation, descriptionStageSummary-- num, name, mantra, phase, slug, iconManifestoConfig,TrapConfig,StageConfig-- Type aliases for backward compatibility with existing layout Props interfaces
Slug Mapping
Sanity stores methodology content with descriptive slugs (value-first-ai, leads-trap, audience). Presentation URLs use shorter display slugs (ai, leads, audience). Two mapping functions handle the translation:
manifestoPresentationSlug()-- Stripsvalue-first-prefix:"value-first-ai"becomes"ai"trapPresentationSlug()-- Strips-trapsuffix:"leads-trap"becomes"leads"
Stage slugs require no mapping -- Sanity and presentation URLs use the same format.
Caching
All queries use sanityFetch() with a 300-second (5-minute) TTL. Methodology content changes rarely (methodology updates are measured in weeks, not hours), so the 5-minute cache provides near-zero latency for repeat requests within a Vercel serverless function instance while still reflecting CMS updates within minutes of publication.
Navigation Ordering
Prev/next navigation for manifestos and traps is controlled by ordered arrays (MANIFESTO_ORDER, TRAP_ORDER) defined in the module. These arrays determine the sequence a visitor navigates through presentations. Stage navigation is derived from the number field in the Sanity query results, which is ordered by number asc.
This ordering approach decouples navigation sequence from Sanity's storage order. If a manifesto needs to be reordered in the presentation flow, only the array constant changes -- no CMS restructuring required.
Surface Refactoring: 42 Files
10 Manifesto Presentation Pages
Each manifesto page (src/pages/presentations/manifesto-{slug}.astro) was refactored from:
import { manifestos } from '../../data/manifesto-data';
const config = manifestos['ai'];
To:
import { getManifestoPresentation } from '../../lib/sanity/methodology';
const config = await getManifestoPresentation('ai');
Pages: manifesto-ai, manifesto-communication, manifesto-content, manifesto-culture, manifesto-delivery, manifesto-humans, manifesto-leadership, manifesto-measurement, manifesto-partner, manifesto-platform.
10 Trap Presentation Pages
Each trap page (src/pages/presentations/trap-{slug}.astro) was refactored identically:
import { getTrapPresentation } from '../../lib/sanity/methodology';
const config = await getTrapPresentation('leads');
Pages: trap-advertising, trap-ai-replacement, trap-authority, trap-conformity, trap-erp, trap-lead-magnet, trap-leads, trap-managed-services, trap-measurement, trap-qualification.
8 Stage Presentation Pages + 8 Stage Embed Pages
Stage pages and their embed variants at src/pages/presentations/stage-{slug}.astro and src/pages/presentations/stage-{slug}/embed.astro now use getStagePresentation().
Pages: stage-audience, stage-researcher, stage-hand-raiser, stage-buyer, stage-value-creator, stage-adopter, stage-advocate, stage-champion (plus 8 /embed variants).
4 Value Path Hub Pages
Value Path directory pages that display stage summaries:
src/pages/value-path/index.astro-- Main Value Path overviewsrc/pages/value-path/stages/index.astro-- All stages listingsrc/pages/value-path/stages/[slug].astro-- Individual stage detailsrc/pages/value-path/assessment/index.astro-- Value Path assessment
These use getAllStages() or getStagePresentations() for stage summary data.
4 Presentation Layouts
The shared layout components that wrap all presentation pages:
src/layouts/ManifestoPresentation.astro-- AcceptsManifestoPresentation(aliased asManifestoConfig)src/layouts/TrapPresentation.astro-- AcceptsTrapPresentation(aliased asTrapConfig)src/layouts/StagePresentation.astro-- AcceptsStagePresentation(aliased asStageConfig)src/layouts/EmbedPresentation.astro-- Shared embed wrapper
Type aliases (ManifestoConfig, TrapConfig, StageConfig) were added to methodology.ts to maintain backward compatibility with the layouts' existing Props interfaces, avoiding a separate refactoring pass on every layout file.
Hardcoded Files Eliminated
| File | Status | Rationale |
|---|---|---|
src/data/manifesto-data.ts |
Deleted | Fully replaced by getManifestoPresentations() |
src/data/trap-data.ts |
Deleted | Fully replaced by getTrapPresentations() |
src/data/stage-data.ts |
Moved to src/remotion/data/ |
Remotion video pipeline runs offline, does not benefit from live CMS queries |
Build Verification
The website build completed in 181 seconds with zero errors after the refactoring. All 42 refactored pages build and render with Sanity-sourced metadata. The build output was verified for correct page generation across all manifesto, trap, and stage routes.
How to Verify It Works
# Confirm methodology.ts exists and exports the query functions
grep -c "export async function" /mnt/d/Projects/value-first-operations/apps/website/src/lib/sanity/methodology.ts
# Expected: 8
# Confirm hardcoded files are gone from src/data/
ls /mnt/d/Projects/value-first-operations/apps/website/src/data/manifesto-data.ts 2>/dev/null
ls /mnt/d/Projects/value-first-operations/apps/website/src/data/trap-data.ts 2>/dev/null
ls /mnt/d/Projects/value-first-operations/apps/website/src/data/stage-data.ts 2>/dev/null
# Expected: all three return "No such file or directory"
# Confirm stage-data.ts exists only in Remotion
find /mnt/d/Projects/value-first-operations/apps/website/src -name "stage-data.ts"
# Expected: src/remotion/data/stage-data.ts (only)
# Count consumer files (pages, layouts, hub pages)
grep -rl "getManifestoPresentations\|getTrapPresentations\|getStagePresentations\|getManifestoPresentation\|getTrapPresentation\|getStagePresentation\|getAllStages\|getAllMethodologyPresentations" \
/mnt/d/Projects/value-first-operations/apps/website/src/ \
--include="*.ts" --include="*.tsx" --include="*.astro" | \
grep -v 'methodology.ts' | grep -v 'lib/sanity/index.ts' | wc -l
# Expected: 42
# Verify a specific page imports from methodology.ts (not data files)
head -5 /mnt/d/Projects/value-first-operations/apps/website/src/pages/presentations/manifesto-ai.astro
# Expected: import from '../../lib/sanity/methodology'
# Build verification (full build)
cd /mnt/d/Projects/value-first-operations/apps/website && pnpm build
# Expected: 0 errors, all presentation routes in output
What This Enables
Single source of truth for methodology metadata. Sanity CMS is now the sole authority for manifesto titles, trap definitions, and stage descriptions across both the presentation pages and the rest of the website. Updating a trap's
oneLineDefinitionin Sanity Studio immediately propagates to the trap presentation page at the next request (within the 5-minute TTL).Drift elimination. The class of bug where "Sanity says X but the presentation page shows Y" is structurally impossible. There is no second copy of the data to drift.
New methodology content without code changes. If a new manifesto or trap is added to Sanity, the query functions pick it up automatically. The only code change needed is adding the slug to the ordering array and creating the presentation page (which is a content decision, not a data synchronization task).
Foundation for deeper canonical integration. The pattern established here -- typed GROQ queries returning interfaces consumed by Astro pages -- extends to any methodology content surface. Course pages, playbook pages, and assessment pages can follow the same pattern to source their metadata from Sanity instead of hardcoded files.
Methodology query tool alignment.
methodology-query.jsin the canonical query layer already queries Sanity for methodology content alignment. With presentation pages now also querying Sanity, the rendered surface and the canonical audit surface are reading from the same source, making drift between "what the audit reports" and "what the page shows" structurally impossible.
Ownership
- Query layer:
apps/website/src/lib/sanity/methodology.ts(433 lines) - Re-export:
apps/website/src/lib/sanity/index.ts - Consumer surfaces: 42 files across
src/pages/presentations/,src/pages/value-path/, andsrc/layouts/ - CMS authority: Sanity Studio (valueReality, trap, valuePathStage schemas)
- Architecture authority: V
- Quality authority: Q (build verification, drift detection)