| CI | |
| Stack | |
| Tooling | |
| Meta |
A minimal React + TypeScript personal site, built entirely through AI pair-programming — no manual code.
Why it exists — two experiments running in parallel:
- AI-only development: every line was written by Claude. Deliberately avoiding the urge to switch back to manual was the point — to find where AI assistance genuinely helps and where it falls short. This is not a workflow I'd recommend for production work.
- First time with MUI: Material UI (v9) was chosen specifically to learn it from scratch with AI assistance, to see how the tool handles an unfamiliar library.
- Strict mode across the stack: TypeScript
strict: true, no-any ESLint, Prettier, full Jest + jest-axe coverage. The discipline catches API misuse and type gaps early; the trade-off is more scaffolding overhead. Observing how that shapes AI-generated code was part of the experiment.
Models used (via Claude Code): Sonnet 4.6, Sonnet 5, Fable 5, Haiku 4.6, Haiku 5, Opus 4.8.
yarn install— install dependenciesyarn dev— start Vite development serveryarn build— create production buildyarn preview— preview production build locallyyarn test— run Jest tests (includes jest-axe accessibility checks)yarn test:coverage— run Jest tests with coverage reportyarn lint— run ESLintyarn lint:fix— fix lintable issuesyarn format— format code with Prettieryarn format:check— check formatting without writing changesyarn typecheck— run TypeScript type checkingyarn draft:mappings— report-only draft of responsibility/recommendation → skill mappings (writes to gitignoredscripts/output/, never tosrc/data/)
Each résumé bullet/recommendation is linked to skills via skillIds (on the row in src/data/careerHistory.json) and skill.jobIds (in src/data/skills.json). The flow:
- Draft —
yarn draft:mappingsembeds text + skills and reports likely matches to gitignoredscripts/output/. Suggestion-only; it never editssrc/data/. - Semantic-merge — a human/Claude applies accepted rows by hand (the embedding pass is lossy, so committed mappings are richer than it suggests) and commits.
- Freeze — committed mappings are frozen: only new or edited bullets get (re)mapped; existing arrays are never reshuffled. Ordering is fixed (
skillIds/recommendationIdsalphabetical,jobIdsin career order), so re-deriving unchanged rows is a zero diff.
- CI — lint, format check, typecheck, test (with coverage), build. Runs on every PR to
mainand on push tomain. Coverage output is uploaded as a workflow artifact and summarised in the job summary.- Coverage percentage is written to a GitHub Gist on every push to
main— the badge above reflects the current value.
- Coverage percentage is written to a GitHub Gist on every push to
- Lighthouse CI — builds the app, serves it locally, and audits Performance, Accessibility, Best Practices, and SEO. Results are uploaded as a workflow artifact and to temporary public storage; it's informational and does not block merges.
- The badge reflects workflow pass/fail; full score reports are available as artifacts on each run.
Data loads asynchronously via React 19's use() hook under Suspense; the nav bar has a settings dropdown (tune icon) with colour theme (green / purple), light/dark mode, View Source, and Report a Problem.
Resume (/)
- Chronological work + education timeline with inline recommendations; shows a skeleton while data loads
- Three track tabs (General, Lead / Engineering Manager, Senior Engineer) synced to
?track=— jobs with nothing in-track collapse to a compact card; education always visible - Click a "Key Skills" chip to jump to that skill on the Skills page highlighted
- Click "View this role's skills on the graph" to open the Skills page with all of that role's skills highlighted
- Click a recommendation's LinkedIn icon to open the original
- Deep-link via
?skill=<name>or?recommendation=<id>— auto-scrolls + highlights; synonyms resolve
Skills (/skills)
- List, bar chart, and radar views (persisted in
?view=); search + category/sub-category filters;?skill=<name>deep links - Categories/sub-categories from the active track's taxonomy; years of experience = summed durations of linked jobs
- Hover a skill for a tooltip — name, subcategory, years, per-company breakdown, links to Resume/recommendations
- Toggle "Patterns" (bar chart) for colour-blind-safe textures
- Filter by category/sub-category via the Filters dropdown (multi-select); stale filters self-clean on track switch
- Copy the current URL (track, view, filters, search, highlighted skill) via the copy-link button
Articles (/articles) — fetches and renders posts from a Medium RSS feed.