For AI Agents: This document provides comprehensive context about the codebase architecture, conventions, and recent work to enable effective onboarding.
Name: Dmitrii Fotesco's Portfolio
Stack: React 19 + TypeScript + Vite
Purpose: Personal portfolio showcasing PM/engineering work with case studies
URL: https://github.com/fotescodev/portfolio
| Metric | Limit | Rationale |
|---|---|---|
| Initial Bundle | < 200KB (Gzipped) | SEO & Mobile LCP performance |
| Animation Rate | 60 FPS | Premium "smooth" feel requirement |
| Type Safety | 100% Zod | Data integrity for YAML-to-JSON pipeline |
portfolio/
├── src/
│ ├── components/
│ │ ├── Portfolio.tsx # Main portfolio component
│ │ ├── ThemeToggle.tsx # Theme switcher component
│ │ ├── sections/ # Page sections
│ │ │ ├── HeroSection.tsx # Receives profile prop
│ │ │ ├── AboutSection.tsx # Receives profile prop
│ │ │ ├── ExperienceSection.tsx
│ │ │ ├── CertificationsSection.tsx # Receives profile prop
│ │ │ ├── PassionProjectsSection.tsx
│ │ │ ├── SocialSection.tsx
│ │ │ ├── FooterSection.tsx
│ │ │ └── CaseStudiesSection.tsx
│ │ ├── case-study/ # Case study components
│ │ │ ├── CaseStudyDrawer.tsx
│ │ │ ├── CaseStudyContent.tsx
│ │ │ └── CaseStudyFooter.tsx
│ │ └── common/ # Reusable UI components
│ │ ├── AmbientBackground.tsx
│ │ └── Omnibar.tsx
│ ├── pages/ # Route pages
│ │ ├── BasePortfolio.tsx # Base portfolio (/)
│ │ └── VariantPortfolio.tsx # Personalized variants (/company/role)
│ ├── context/
│ │ ├── ThemeContext.tsx # Theme provider (dark/light)
│ │ └── VariantContext.tsx # 🆕 Variant profile injection
│ ├── lib/
│ │ ├── content.ts # Content loader (Vite import.meta.glob)
│ │ ├── schemas.ts # Zod validation schemas
│ │ └── variants.ts # 🆕 Variant loading & merging
│ ├── types/
│ │ ├── portfolio.ts # Portfolio type definitions
│ │ └── variant.ts # 🆕 Variant type definitions
│ ├── styles/
│ │ └── globals.css # Design system tokens
│ └── tests/
│ ├── setup.ts # Test configuration
│ ├── design-system/ # Design system test suite
│ └── mobile/ # Mobile responsiveness tests
├── content/
│ ├── profile.yaml # Base profile data
│ ├── experience/index.yaml
│ ├── case-studies/*.md
│ ├── blog/*.md
│ ├── knowledge/ # 🆕 Source of truth for facts
│ │ ├── index.yaml # Entity relationships
│ │ ├── achievements/ # STAR-format accomplishments
│ │ └── stories/ # Extended narratives
│ └── variants/ # Personalized variants
│ ├── README.md
│ ├── _template.yaml
│ ├── bloomberg-technical-product-manager.{yaml,json}
│ └── gensyn-technical-product-manager.{yaml,json}
├── scripts/
│ ├── validate-content.ts # Content validation CLI
│ ├── generate-cv.ts # Variant generation CLI
│ ├── sync-variants.ts # 🆕 YAML→JSON sync
│ ├── evaluate-variants.ts # 🆕 Claims ledger generator
│ └── redteam.ts # 🆕 Adversarial scanner
├── capstone/ # 🆕 AI Product Quality Framework
│ ├── develop/
│ │ ├── evaluation.md # Evaluation rubric
│ │ ├── red-teaming.md # Threat model
│ │ ├── evals/ # Claims ledgers per variant
│ │ └── redteam/ # Red team reports per variant
│ ├── define/
│ ├── discover/
│ └── deliver/
├── docs/
│ ├── guides/
│ │ ├── adding-case-studies.md
│ │ ├── capstone-workflow.md # Quality pipeline guide
│ │ ├── content-management.md # Content schema guide
│ │ ├── cv-data-ingestion.md
│ │ ├── universal-cv-cli.md # CLI tools guide
│ │ └── universal-cv.md # Universal CV guide
│ ├── personal/ # Personal docs (not for AI agents)
│ └── history/ # Archived docs
├── context/
│ ├── CODEBASE.md # This file - architecture docs
│ ├── DESIGN.md # Design system documentation
│ └── PROJECT_STATE.md # Single source of truth (strategic + session log)
└── vitest.config.ts # Test configuration
-
Inline Styles with CSS Variables: Components use inline
styleprops with CSS variablesstyle={{ color: 'var(--color-text-primary)' }}
-
Theme via data-attribute: Theme switching uses
data-themeon<html>[data-theme="light"] { --color-background: #fafafa; }
-
ThemeContext: Provides
isDark,toggleTheme,theme- but NOT colors (deprecated) -
Universal CV - Variant System: Portfolio personalization for job applications
- Base profile + variant overrides = personalized experience
- Dynamic routes via BrowserRouter (
/:company/:role) - VariantContext provides merged profile to components
- Sections receive
profileprop from context (not direct imports)
The Universal CV system creates personalized portfolio variants tailored to specific job applications using AI-generated overrides of the base profile.
Live Examples:
- Base:
https://edgeoftrust.com/ - Bloomberg TPM:
https://edgeoftrust.com/bloomberg/technical-product-manager - Gensyn TPM:
https://edgeoftrust.com/gensyn/technical-product-manager
Data Flow:
URL (/company/role)
→ React Router captures params
→ VariantPortfolio.tsx loads variant JSON
→ variants.ts: mergeProfile(base + overrides)
→ VariantContext provides merged profile
→ Portfolio.tsx receives from context
→ Sections (Hero, About, etc.) receive profile prop
→ Personalized content rendered
Key Files:
src/context/VariantContext.tsx- Profile injection systemsrc/pages/VariantPortfolio.tsx- Dynamic variant loadersrc/lib/variants.ts- Loading + merging logicsrc/types/variant.ts- Type definitionsscripts/generate-cv.ts- CLI generator (243 lines)content/variants/*.{yaml,json}- Variant data
interface Variant {
metadata: {
company: string;
role: string;
slug: string;
generatedAt: string;
jobDescription: string;
generationModel?: string;
};
overrides: {
hero?: {
status?: string;
headline?: HeadlineSegment[];
subheadline?: string;
};
about?: {
tagline?: string;
bio?: string[];
stats?: Stat[];
};
sections?: {
beyondWork?: boolean;
blog?: boolean;
onchainIdentity?: boolean;
skills?: boolean;
passionProjects?: boolean;
};
};
relevance?: {
caseStudies?: Array<{ slug: string; relevanceScore: number; reasoning?: string }>;
skills?: Array<{ category: string; relevanceScore: number }>;
projects?: Array<{ slug: string; relevanceScore: number; reasoning?: string }>;
};
}CLI Generator:
npm run generate:cv -- \
--company "Company" \
--role "Role Title" \
--jd "./job-description.txt" \
--provider geminiSupported Providers:
- Claude (Anthropic):
ANTHROPIC_API_KEY - OpenAI:
OPENAI_API_KEY - Gemini (Google):
GEMINI_API_KEY
IMPORTANT: Sections that show personalized content MUST receive profile as a prop:
// ✅ CORRECT - Receives profile from context
function HeroSection({ profile, isMobile }: { profile: Profile; isMobile: boolean }) {
const { hero } = profile; // Uses variant profile
// ...
}
// ❌ INCORRECT - Direct import bypasses variants
import { profile } from '../lib/content';
function HeroSection({ isMobile }: { isMobile: boolean }) {
const { hero } = profile; // Always uses base profile!
// ...
}Updated Components:
HeroSection.tsx- ReceivesprofilepropAboutSection.tsx- ReceivesprofilepropCertificationsSection.tsx- Receivesprofileprop
In Portfolio.tsx:
const { profile } = useVariant(); // Get from context
<HeroSection profile={profile} isMobile={isMobile} ... />
<AboutSection profile={profile} isMobile={isMobile} ... />BrowserRouter (clean URLs):
// src/App.tsx
<BrowserRouter>
<Routes>
<Route path="/" element={<BasePortfolio />} />
<Route path="/:company/:role" element={<VariantPortfolio />} />
</Routes>
</BrowserRouter>Hosting Requirements:
- Vercel/Netlify: Works out of the box (auto-rewrites to index.html)
- GitHub Pages: Requires 404.html workaround or switch to HashRouter
- Clean URLs:
/company/role(no hash) - Fully shareable and bookmarkable
Variants are bundled as separate chunks for optimal loading:
dist/assets/
├── bloomberg-technical-product-manager-[hash].js (4KB)
├── gensyn-technical-product-manager-[hash].js (4KB)
└── index-[hash].js (main bundle)
Vite's import.meta.glob enables lazy loading:
const variantFiles = import.meta.glob('../../content/variants/*.json', {
eager: false // Lazy load only when needed
});The capstone project wraps the Universal CV system with production-grade AI product evaluation.
YAML is canonical → JSON is derived
Facts live in knowledge base → Variants reference them
Every claim must be traceable → Machine-checkable ledger
| Script | Purpose | Command |
|---|---|---|
sync-variants.ts |
YAML→JSON sync | npm run variants:sync |
evaluate-variants.ts |
Generate claims ledger | npm run eval:variant -- --slug <slug> |
redteam.ts |
Adversarial scanning | npm run redteam:variant -- --slug <slug> |
"predev": "npm run variants:sync",
"prebuild": "npm run validate && npm run variants:sync"Every npm run dev and npm run build now enforces YAML/JSON parity.
content/knowledge/
├── index.yaml # Entity graph
├── achievements/ # STAR-format: Situation, Task, Action, Result
│ ├── ankr-15x-revenue.yaml
│ ├── eth-staking-zero-slashing.yaml
│ └── ...
└── stories/ # Extended narratives
Golden Rule: Fix facts in the knowledge base, not in the variant output.
For each variant, the evaluation script extracts metric-like claims and generates:
capstone/develop/evals/<slug>.claims.yaml— Machine-checkablecapstone/develop/evals/<slug>.eval.md— Human checklist
npm run eval:variant -- --slug bloomberg-technical-product-manager
npm run eval:check # Fails if unverified claims| Check ID | Catches |
|---|---|
RT-SEC-SECRETS |
API keys, tokens |
RT-SEC-CONFIDENTIAL |
NDA language |
RT-TONE-SYCOPHANCY |
"thrilled", "dream company" |
RT-ACC-INFLATION |
"about 15×" near metrics |
RT-XVAR-CONTAM |
Mentions other target company |
npm run redteam:variant -- --slug bloomberg-technical-product-manager
npm run redteam:check --strict # WARN = FAILWork one variant at a time through the pipeline:
npm run variants:syncnpm run eval:variant -- --slug <slug>- Verify claims in
*.claims.yaml npm run eval:checknpm run redteam:variant -- --slug <slug>npm run redteam:check- Fix KB or variant wording until clean
- Commit + deploy
| Category | Examples |
|---|---|
| Colors | --color-background, --color-text-primary, --color-accent |
| Spacing | --space-xs (4px) through --space-3xl (64px) |
| Typography | --font-serif, --font-sans |
| Transitions | --ease-smooth, --transition-medium |
| Token | Dark Mode | Light Mode |
|---|---|---|
--color-background |
#08080a | #fafafa |
--color-text-primary |
#e8e6e3 | #050505 |
--color-accent |
#c29a6c | #8a6642 |
Section Labels:
// Section eyebrow pattern - used for all section headers
<span className="eyebrow">About</span>Typography:
.eyebrow { /* 11px, 600 weight, 0.15em tracking, uppercase, muted */ }
.text-readable { max-width: 65ch; }
.bullet-list { max-width: 600px; line-height: 1.6; }Tags:
// Unified tag styling for experience, case studies, blog
<span className="tag">STAKING</span>Testimonials:
// Quote marks with consistent positioning
<div className="quote-mark">"</div> // Mobile: 0.3 opacity
<div className="quote-mark-subtle">"</div> // Desktop: 0.18 opacityCards:
// Light mode shadow enhancement
<article className="light-card">...</article>| Component | Purpose | Usage |
|---|---|---|
TestimonialCard.tsx |
Testimonial card with quote, author, avatar | Mobile carousel + desktop list |
Omnibar.tsx |
Floating action bar (Email/CV/Book) | Mobile bottom bar |
Marquee.tsx |
Scrolling text banner | Optional decoration |
1. Section Headers (eyebrow + divider):
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-lg)' }}>
<span className="eyebrow">Section Name</span>
<div style={{ flex: 1, height: '1px', background: 'var(--color-border-light)' }} />
</div>2. Responsive Section Padding:
const sectionPadding = isMobile ? '48px 24px' : isTablet ? '64px 40px' : '80px 64px';3. Card Gradient Background:
background: 'linear-gradient(135deg, var(--color-background-secondary) 0%, var(--color-background-tertiary) 100%)'Run commands:
npm run test:design-system # Design system tests (29 tests)
npm run test # All tests
npm run test:watch # Watch mode| File | Tests | Purpose |
|---|---|---|
css-variables.test.ts |
12 | Verify design tokens exist |
theme-toggle.test.tsx |
6 | ThemeProvider functionality |
components.test.tsx |
11 | Component render checks |
Objective: Migrate from React context-based colors to CSS custom properties
Changes Made:
- Created
src/styles/globals.csswith all design tokens - Updated
ThemeContext.tsxto setdata-themeattribute on<html> - Migrated 8 components to use CSS variables instead of
useTheme().colors:- ThemeToggle.tsx
- FooterSection.tsx
- HeroSection.tsx
- AboutSection.tsx
- SocialSection.tsx
- ExperienceSection.tsx
- CertificationsSection.tsx
- CaseStudyModal.tsx
Git Commits:
34a925e- feat: migrate design system to CSS custom properties1d5668e- test: add design system test suite with Vitest
- Use CSS variables for all colors:
var(--color-*) - Use spacing tokens:
var(--space-md)not16px - Use font variables:
var(--font-serif)not hardcoded strings - Run
npm run test:design-systemafter design changes
- Import
colorsfromuseTheme()- this is deprecated - Use hardcoded hex colors in components
- Use
isDarkconditionals for colors (CSS handles this now)
Core:
- react@19.2.0
- react-dom@19.2.0
- vite@7.2.4
Dev/Testing:
- vitest@3.2.4
- @testing-library/react
- @testing-library/jest-dom
- jsdom
# 1. Create feature branch
git checkout -b feat/your-feature
# 2. Push work
git push origin feat/your-feature
# 3. Create Pull Request (via gh CLI)
gh pr create --title "feat: descriptive title" --body "context about changes"
# 4. Merge after approval
gh pr merge --squash