Agent Guide
Operational rules, failure modes, and safe extension patterns for coding agents working in this repository.
This guide supplements the root AGENTS.md and nested AGENTS.md files with architecture-informed guidance. It is the canonical source for repository-wide architecture invariants and safe extension patterns. For validation matrices, build variants, and repo-standard command shapes, use Testing strategy. When rules conflict, prioritize the nearest AGENTS.md to the file being changed.
Before you start
Scope assessment checklist
Before editing any file:
- Is this a content update (data module only) or a UI/behavior change?
- Does the change affect a shared component? If yes, identify all consumers.
- Does it touch motion, theme, or styling infrastructure? If yes, verify intensity scaling is preserved.
- Does it span more than 3 source files? If yes, consider an ExecPlan.
Architecture invariants
These are the non-negotiable rules. Breaking any of these produces a regression.
1. Provider nesting order
ThemeProvider → WelcomeOnboardingProvider → WelcomeAudioProvider → BrowserRouter → CommandPaletteProvider → AppContent
Do not rearrange providers. Each depends on context from its parent.
2. Motion intensity contract
All Framer Motion animation durations and delays must flow through useMotionScale(). When motionIntensity is off:
- All entrance animations must complete instantly (duration: 0)
- All stagger delays must be 0
- All CSS decorative animations must be disabled (
cssAnimations: false) prefers-reduced-motion: reducemust force theofflevel
3. Style builder pipeline
Theme-conditional styles must come from createComponentStyleMap() or createAppStyleMap(), not from inline sx objects that read theme.appearanceTreatment directly. The style builders are memoized; bypassing them creates redundant computation and risks inconsistency.
4. Data modules are source of truth
Never generate content procedurally or fetch it from APIs (except GitHub profile data, which has explicit fallback handling). All portfolio, climbing, photography, and blog content lives in src/data/.
5. SPA routing and PUBLIC_URL
All routes are client-side. Direct links to any route must work when the host rewrites unknown paths to index.html. Asset URLs must use PUBLIC_URL for static hosting compatibility.
6. Route registry consistency
Public route exposure must stay consistent across src/constants/siteRoutes.ts, src/App.tsx, src/constants/routeActions.ts, and src/constants/commandPaletteActions.ts. Do not hide or expose a route in one surface without updating the others.
Decision trees
Where does this component go?
Where does this style go?
Where does this type go?
Common failure modes
1. Broken motion handoffs
Symptom: Page sections never appear, or the home hero freezes mid-animation.
Cause: Changing an onAnimationComplete callback, motion variant, or AnimatePresence mode without understanding the sequence chain.
Prevention: Read the entrance sequence in Page choreography before touching motion code. Test with motion intensity set to off to verify content renders without animation.
2. Theme drift
Symptom: A component looks correct with one appearance preset but breaks on another.
Cause: Hardcoded hex colors, custom font sizes, or spacing values that bypass theme tokens.
Prevention: Always use theme.palette.*, theme.typography.*, theme.spacing(), or style builder surfaces. Never hardcode colors or sizes.
3. AnimatedContentList composition breakage
Symptom: CV card items reveal all at once instead of staggering, or hidden cards become visible prematurely.
Cause: tiltItems changes the composition model — when enabled, each item must reveal independently via AnimatedContentCard/MotionTiltCard, not from a shared stagger container.
Prevention: Don't change AnimatedContentList's render paths without checking both tiltItems: true and tiltItems: false behavior.
4. Style builder staleness
Symptom: A new style shows up in one preset/mode but not another.
Cause: Reading theme.appearanceTreatment in a component instead of adding the style to the appropriate builder module.
Prevention: Add new theme-conditional styles to componentStyleBuilders.ts or appStyleBuilders.ts. The hooks automatically memoize and update when the theme changes.
5. Route exposure drift
Symptom: Header navigation, command palette entries, and registered routes disagree about which public routes exist.
Cause: Updating route JSX without updating siteRouteMap, derived route actions, or smoke expectations.
Prevention: Keep route exposure metadata central in siteRoutes.ts, derive navigation and recovery from it, and revalidate smoke coverage after public-route changes.
6. GitHub API in dev/test
Symptom: CV page makes live GitHub API calls in development, causing rate-limiting or stale data.
Cause: Not respecting the REACT_APP_ENABLE_GITHUB_API_IN_DEV guard.
Prevention: GitHub API calls are skipped outside production by default. Don't bypass this unless you understand the implications.
Intentional exceptions
Two subsystems intentionally deviate from the shared design system. Do not "fix" these:
| Subsystem | Where | Why it's different |
|---|---|---|
| IDE hero chrome | src/components/ide/ | Must look like VS Code, not like the portfolio |
| CV story mode | src/components/cv/CVStory*.tsx | Full-screen immersive experience with its own motion system; bounded through UnsafeTypography |
Blog uses prose context via Text roles and is not a design-system exception. Photography uses inverse-tone overlay context via Text and is not a design-system exception.
Validation handoff
Testing strategy is the canonical source for validation commands, build variants, browser-validation expectations, and the repo-wide validation matrix. Use it whenever root or scoped instructions tell you to validate a change.
Architecture-specific reminders:
| Change area | Additional check |
|---|---|
| Motion/animation | Verify motion intensity off and reduced-motion handling collapse entrance and stagger behavior |
| Theme/styling | Validate both light/dark modes and at least two appearance presets when shared styling changes |
| Shared component | Validate a primary consumer and at least one additional clear consumer |
| Route exposure / runtime env | Verify the production build matches the intended public route surface; use npm run build:e2e only when behavior depends on the test runtime |
Safe extension patterns
Adding a new CV section
- Add section data to
src/data/cv.ts - Add section key to
CVSectionKeyinsrc/components/cv/cvSectionMetadata.ts - Add layout placement to
cvPageSectionLayoutinsrc/pages/cvPageLayout.ts - Create section component in
src/components/cv/ - Register in the CV page's section renderer
Adding a new route
- Add route path to
src/constants/siteRoutes.ts - Add route definition in
src/App.tsx - Create page component in
src/pages/ - Add navigation entry to header/command palette as needed
- Update smoke or route-level coverage when the public route surface changes
Adding a new appearance preset
- Add preset definition to
APP_APPEARANCESinsrc/theme/appAppearance.ts - Define light + dark palettes, typography fonts, surface treatments, and motion treatment
- Add to the appearance switcher UI
- Test across all 6 routes with both light and dark modes
Adding animation to a component
- Import from
src/motion/components.tsx(don't create new animated primitives) - Use existing variants from
src/motion/variants.tsor define local variants that respectuseMotionScale() - Duration tokens must come from
src/motion/tokens.tsand scale withmotionScale.durationFactor - Test with motion intensity
offto verify the component functions without animation
File ownership boundaries
| Area | Data source | Hook | Page | Components |
|---|---|---|---|---|
| CV | data/cv.ts | useCVSectionData() | pages/CV.tsx | components/cv/* |
| Climbing | data/climbs.ts | useClimbingData() | pages/Climbing.tsx | components/climbing/* |
| Photography | data/photography.ts | usePhotographyData() | pages/Photography.tsx | components/photography/* |
| Blog | data/blog.ts | useBlogData() | pages/Blog.tsx pages/BlogPost.tsx | components/blog/* |
| Theme | theme/appAppearance.ts | useTheme() | — | ThemeProvider.tsx |
| Motion | motion/tokens.ts | useMotionScale() | — | motion/components.tsx |
Further reading
- Testing strategy — test patterns and validation commands
- App architecture — provider hierarchy, route structure
- Component architecture — ownership boundaries and composition rules
- Motion architecture — intensity scaling and anti-patterns
- Theme and styling — style builder system and placement rules
- Page choreography — route-level assembly and sequencing