Repository walkthrough · 470 tracked files · main @ fd189ef

Three websites, one static export, one FastAPI box.

A file-by-file map of itsjaya — the monorepo behind jayaremala.com, gradeVITian and VRF Bricks. What every folder owns, which files are hand-written versus generated, and how content flows from one JSON directory out to all three surfaces.

470files
8.5kpy lines
46kts/tsx lines
3surfaces
1source of truth

The big picture

This is a personal portfolio that grew two extra products. All three ship from a single Next.js static export; a single FastAPI container serves all of their dynamic needs.

Portfolio + Avocado

jayaremala.com

The main site: career pages, blog, lab logs, gallery, plus Avocado — a RAG chatbot at /chat that answers questions about Jaya from the knowledge base.

gradeVITian

gradevitian.jayaremala.com

A student-tools app for VIT: GPA/CGPA calculators, attendance and grade predictors, a semester planner, plus accounts, badges and a rulebook Q&A over the academic regulations PDF.

VRF Bricks

vrfbricks.jayaremala.com

A real fly-ash brick yard in Kavali (the owner's father's business). Bilingual English/Telugu, WhatsApp-first conversion, a brick-quantity calculator.

How the pieces connect

GitHub Pages  ──serves──▶  jayaremala.com          ┐
                                                     │  same out/ folder,
nginx @ Lightsail ─┬─▶ gradevitian.jayaremala.com │  built once by CI
                     └─▶ vrfbricks.jayaremala.com   ┘
                     │
                     └─▶ api.jayaremala.com ──▶ Docker :8000 (FastAPI)
                                                  │
                                     /data volume ├── chroma_db/      (vectors)
                                                  ├── analytics.db
                                                  ├── content.db
                                                  └── gradevitian.db
                                                        ↑ nightly S3 backup

There is no Node server in production. Next.js runs with output: "export", so every page is pre-rendered HTML. Anything dynamic — chat, analytics, logins, saved calculators — is a client-side fetch to the FastAPI box.


Repository root

PathWhat it is
README.mdThe human-facing tour — 715 lines. System diagram, deployment topology, design notes. The most complete narrative document in the repo.
CLAUDE.mdThe working map for AI agents: layout rules, commands, conventions, "never edit this" warnings. Shorter and more prescriptive than the README.
.env.exampleTemplate for the real .env. Documents every knob: API base URL, Gemini model chain, DB paths, the fastmcp host allow-list, Search Console token.
.envgitignored Real secrets. Read by infra/compose.yml for local runs.
.gitignoreStandard Node + Python ignores, plus out/, .next/ and the regenerable PDF-scrape intermediates under backend/data/gradevitian/_*.
.claude/Claude Code project settings (settings.local.json — permission allowlist).
.cursor/ .superpowers/Editor/agent scratch dirs, untracked. .superpowers/sdd holds spec-driven-development working files.
backend/ frontend/The two deployables. Each owns its own Dockerfile and .dockerignore.
scripts/ infra/ docs/ .github/Codegen, deployment, prose, CI. Covered below.

Two flows that matter

Almost every "why is this file here?" question resolves into one of these two pipelines. Learn them and the layout stops being surprising.

1 · Knowledge sync — how content reaches all three surfaces

SOURCEbackend/data/knowledge/*.jsonHand-edited. Profile, experience, projects, skills, testimonials, apps, gallery, quotes, spotlights.
+ MDXfrontend/src/content/Blog and lab posts, hand-written with frontmatter.
RUNsync-knowledge.mjsMDX → blog.json / lab.json, then copies all knowledge JSON into the frontend.
OUT Afrontend/src/data/knowledge/Typed by data/*.ts, imported by pages at build time.
OUT BChroma vectorsBackend re-ingests at startup when the JSON hash changed — powers Avocado.
Never edit downstream

frontend/src/data/knowledge/ is overwritten on every build. So are blog.json and lab.json in the backend. Edit backend/data/knowledge/*.json (or the MDX) and re-run npm run sync.

2 · Deploy — what ships where

TRIGGERpush to maindetect-changes runs paths-filter to decide which halves deploy.
FRONTENDbuild → out/Static export, uploaded to GitHub Pages and rsynced to Lightsail for the subdomains.
FRONTENDsync-knowledgeCommits regenerated JSON back to main with [skip ci].
BACKENDimage → GHCRDocker build and push.
BACKENDblue-green swapdeploy.sh health-checks the new container on :8001, then swaps it onto :8000.

backend/ FastAPI · Python 3.11 · 8,486 lines

A single FastAPI app behind api.jayaremala.com. It serves the RAG chatbot, all three surfaces' analytics, the gradeVITian product API, the content CMS, and a public MCP server — from one container.

backend/
├── data/
│   ├── knowledge/          ← SINGLE SOURCE OF TRUTH for portfolio content
│   └── gradevitian/        VIT regulations corpus + legacy comments
├── src/app/               the package (src layout, installed editable)
│   ├── main.py             app assembly, lifespan, CORS, MCP mount
│   ├── mcp_server.py       public read-only MCP
│   ├── agent/  core/  db/  integrations/  obs/  rag/  routers/
├── scripts/parse_regulations.py
├── tests/
├── Dockerfile · pyproject.toml · runtime.txt
Working directory matters

Run everything from backend/, never backend/src/. DB paths default to cwd-relative (./chroma_db), so a different cwd silently creates a second, empty vector store and Avocado goes blank.

backend/data/ — the knowledge base

Fifteen JSON files. Twelve are hand-edited and authoritative; two are generated; one directory is a separate corpus for gradeVITian.

FileControlsStatus
knowledge/profile.jsonName, tagline, bio, obsession, previous/interested domain, location, contact links, resume URL, availability.edit
knowledge/experience.jsonRoles, companies, dates, bullet points.edit
knowledge/education.jsonDegrees, institutions, GPA, highlights.edit
knowledge/projects.jsonTitle, description, tags, featured flag, award, source links, notes.edit
knowledge/skills.jsonSkill categories and their items.edit
knowledge/testimonials.jsonName, designation, company, LinkedIn, quote, date, source.edit
knowledge/apps.jsonLive apps listed on /apps, with status (live/beta/wip/archived).edit
knowledge/gallery.jsonImages and captions for /gallery.edit
knowledge/quotes.json/quotes entries, categorised (Work / Life / Technology / Philosophy / Creativity / Mindset).edit
knowledge/spotlights.jsonHomepage spotlight cards with CTAs.edit
knowledge/inbox_signals.jsonInputs for the weekly digest email.edit
knowledge/blog.jsonBlog index — built from the MDX files by the sync script.generated
knowledge/lab.jsonLab index — same, from content/lab/*.mdx.generated
gradevitian/regulations.jsonCurated, citable VIT academic rules. Also copied into the frontend bundle by the sync script, so it counts as a frontend input in CI too.edit
gradevitian/regulation_chunks.json1,273-line retrieval corpus chunked from the regulations PDF, queried by rag/gv_rulebook.py.derived
gradevitian/gv_legacy_comments.jsonComments carried over from the pre-rewrite gradeVITian site; seeded into SQLite on first boot.edit

backend/scripts/parse_regulations.py

Regenerates the two gradeVITian regulation files from docs/gradevitian/Academic-Regulations.pdf. Needs the optional [scrape] extra (PyMuPDF) — it is never imported at runtime.

core/ · obs/ · db/

FileResponsibilityLines
core/settings.pyThe pydantic-settings singleton. Holds API keys, DB paths, CORS origins, and the provider:model fallback chain (Gemini → Groq → OpenRouter, each included only if its key is set).101
core/limiter.pySlowAPI rate limiter keyed on the real client IP (unwraps proxy headers).13
core/gv_auth.pygradeVITian auth using stdlib only — no PyJWT, no bcrypt. Hand-rolled HMAC token sign/verify, PBKDF-style password hashing, password-reset tokens, and the current_user/optional_user FastAPI dependencies.130
core/gv_moderation.pyDependency-free moderation for student comments. Normalises obfuscation (l33tspeak, repeated chars), detects links and shouting, and escalates borderline cases to an LLM classifier.152
obs/trace.pyA tiny context-manager tracer. Each request accumulates named stage timings, which feed the waterfall on the /system dashboard.40

db/ — three SQLite stores on the persistent volume

FileOwnsLines
db/analytics.pyThe biggest DB module. Unique visitors (SHA-256 hashed IPs, never raw), geo lookup, per-model breakdowns, latency percentiles, stage timings, error records, top questions, feedback and experience ratings, page stats, lead captures, reliability. Everything the /system dashboard renders.772
db/content.pyThe admin CMS store — blog posts, lab entries, quotes. Seeds itself from JSON when empty, and can regenerate blog.json/lab.json back out so admin-authored posts reach the RAG index.763
db/gradevitian.pyStudent accounts, saved calculations, per-calculator persisted state, badges, streaks, comments with moderation status, notifications, referrals, traffic counters, admin metrics.605
db/blog_stats.pyBlog engagement: views (unique per IP, idempotent) and claps (max 50 per IP per post). Prunes orphaned rows when a post is deleted.264

rag/ — retrieval

FileResponsibilityLines
rag/ingest.pyBuilds the vector store from the knowledge JSON. Has a _build_*_documents function per content type (blog, testimonials, quotes, gallery, lab, apps, FAQ, system FAQ, inbox signals, Drive résumé). Hash-gated: re-ingests only when the JSON SHA-256 changed, cached at chroma_db/.ingest_hash.954
rag/store.pyThe retrieval engine. ChromaDB client, fastembed ONNX embeddings (BAAI/bge-base-en-v1.5 — no PyTorch), a BM25 index, RRF merge of dense + sparse results, an optional cross-encoder rerank, an LRU query cache and a warmup path.289
rag/graph.pyA lightweight static knowledge graph. Given retrieved chunks, pulls in related skill and experience documents so an answer about a project also knows the stack and the role it came from.192
rag/gv_rulebook.pyCompletely separate retrieval over the VIT regulations — its own tokenizer and index, isolated from the portfolio corpus so a student question never retrieves Jaya's résumé.80

What /ai/chat actually does

1ExpandOptional HyDE; builds several query variants from the message + history.
2RetrieveDense Chroma query + BM25, batched across variants.
3MergeRRF fusion, then rerank, then top 5.
4Expand graphPull adjacent skill/experience docs.
5GenerateInject as context; walk the provider fallback chain on 429/503.

routers/ — the HTTP surface

RouterPrefixEndpointsLines
ai.py/aiThe heart of Avocado. /chat (non-streaming fallback), /chat/stream (SSE), /chat/agentic (visible tool-calling agent with per-step trace), /summarize, /draft, /rewrite, /followups, /lead-capture, /feedback, /warmup. Also owns HyDE, prompt assembly, capacity-error detection and the provider fallback loop.1205
gradevitian.py/gvSignup/login/me, forgot- and reset-password, saved calcs, per-calculator state, page-load and visit counters, public stats, admin metrics, referrals, comments (+ moderation queue), notifications, rulebook /ask, achievements.437
admin.py/adminAll bearer-token gated: force re-ingest and its status, sync status, Google OAuth init/callback/revoke, Gmail digest, Calendar status and preview, Drive résumé sync, Google Docs draft import, digest preview/send, analytics pruning.372
content.py/contentCRUD for blog, lab and quotes. Reads are public; writes and the drafts list require the admin token. This is what the /admin editors talk to.294
stats.py/stats/overview, /system (the dashboard payload), /system/traces, /visit, /experience-rating, /admin.213
blog.py/blogFour endpoints: record view, record clap, per-post stats, index summary. Thin — all logic is in db/blog_stats.py.48
tools.py/toolsLists and invokes the agent tool registry over HTTP. Powers the /mcp explorer page on the site.49

integrations/ · agent/ · main.py

FileResponsibilityLines
main.pyApp assembly. Lifespan runs run_ingest() on a background thread so startup isn't blocked; mounts the MCP server at /mcp; applies path-aware CORS via a custom ScopedCORSMiddleware (permissive for /mcp, strict everywhere else); a MCPTrailingSlashShim fixes client path quirks; exposes /health.207
agent/tools.pyThe read-only tool registry, shared by Agent mode and MCP so both expose exactly the same surface: search_knowledge, get_profile, get_experience, get_projects, get_project, get_skills, get_education, get_now, get_resume, get_blog, get_lab, get_apps, get_app, check_availability, get_booking_link.320
mcp_server.pyWraps that registry as a public read-only fastmcp app. Small, because agent/tools.py already did the work.49
integrations/google_auth.pyShared OAuth2 token lifecycle — auth URL, code exchange, refresh, persistence, revoke. Gmail, Calendar and Drive all go through it.160
integrations/calendar.pyReal free/busy lookup so Avocado can answer "when is he free?" honestly. Computed on a thread pool with a timeout and cached, so a slow Google call never stalls a chat reply.238
integrations/gmail.pySends recruiter lead-capture intros and gradeVITian transactional mail; parses inbox recruiter signals for the digest.192
integrations/drive.pySyncs the résumé from Drive (so the RAG index tracks the real document) and imports Google Docs drafts into the blog editor.212
integrations/digest.pyBuilds the weekly HTML digest from analytics and decides whether it's worth sending at all.134

tests/ + build files

FileWhat it does
tests/conftest.pyPuts src/ on the path and points GV_DB_PATH at a temp DB before the settings singleton is constructed. An autouse fixture blocks external calls.
tests/test_gradevitian.py17 tests — the only tested subsystem. Signup/login/me, duplicate rejection, auth gating, saved calcs, persisted calc state, traffic counters, referrals, comment moderation (integration + unit + LLM escalation), the password-reset flow, and admin-metrics token gating.
pyproject.tomlDeps and the ruff config (100-col). Note the deliberate fastmcp>=3.4.3,<3.5 pin — 3.4.3 added Host allow-list enforcement, and an unpinned drift once broke the deployed MCP server.
Dockerfilepython:3.11-slim. Pre-downloads the embedding model at build time so container startup needs no network call. Keep that model string in step with EMBED_MODEL in rag/store.py.
runtime.txtPins python-3.11.9.

frontend/ Next.js 16 · React 19 · Tailwind 4 · ~46k lines

One app, three products. The organising principle is the feature vertical: everything belonging to one product lives together, and only genuinely content-free code sits in a shared bucket.

frontend/src/
├── app/          routes — (portfolio) group, chat, admin, gradevitian, vrfbricks
├── components/
│   ├── ui/         generic primitives — no product content
│   ├── portfolio/  chat/  blog/  lab/  system/  admin/
│   └── gradevitian/  vrfbricks/
├── lib/          api/ content/ portfolio/ admin/ gradevitian/ vrfbricks/ + root utils
├── data/         knowledge/ (generated) + typed *.ts re-exports + per-site data
├── content/      blog/*.mdx  lab/*.mdx
└── proxy.ts        dev-only subdomain rewrite
The placement rule

If a component renders product-specific content, it goes in that product's folder. It belongs in components/ui/ only if it is content-free and at least two verticals could use it.

Build configuration

FileWhat it does
next.config.tsSets output: "export", trailingSlash, unoptimized images — and inlines NEXT_PUBLIC_BUILD_YEAR. That last one exists because new Date() in a client component is a hydration hazard on a static export: the HTML is rendered once at build, but reruns in every browser, so "© 2026" mismatches the moment the year rolls over.
package.jsonScripts, and the key detail: predev and prebuild both run the three codegen scripts. You never have to remember to sync — npm run dev does it.
postcss.config.mjsLoads @tailwindcss/postcss. There is no tailwind.config.js — Tailwind 4 config lives in src/app/globals.css under @theme inline.
tsconfig.jsonStrict TS, the @/* path alias to src/.
eslint.config.mjsFlat config extending eslint-config-next.
Dockerfile / .dockerignoreUsed only by infra/compose.yml for the local full stack. Production never runs a Node server.
CLAUDE.md / AGENTS.md / README.mdFrontend-scoped agent notes (1 and 5 lines — they defer to the root) and the Next.js starter README.
scripts/gen-pwa-icons.mjsGenerates the portfolio PWA icon set from src/app/icon.png — 192/512 "any", a padded 512 maskable, and a 180px apple-touch-icon. Run by hand; needs sharp.
scripts/gen-gv-icons.mjsSame for gradeVITian — draws a mortarboard on the brand accent, keeping content inside the maskable safe zone.
src/proxy.tsNext 16 renamed Middleware → Proxy. Rewrites gradevitian.localhost/gpa/gradevitian/gpa so you can preview clean subdomain URLs locally. Dev only — a static export disables proxies, and nginx does this job in production.

src/app/ — routes 12,641 lines

Root-level files (apply to every surface)

FileWhat it doesLines
layout.tsxThe root HTML shell. Loads nine Google fonts (Geist, Geist Mono, Source Serif 4, Playfair, EB Garamond, Roboto, Cormorant, Inter, Caveat), mounts ThemeProvider, SiteTracker and PWARegister.151
globals.cssThe design system. Tailwind 4's @theme inline block plus every custom animation and prose rule — 1,689 lines, and the single largest non-generated source file in the repo.1689
icon.pngThe favicon. App Router auto-detects it; no <link> needed.
not-found.tsxThe portfolio 404 — an interactive page, not a stub.252
loading.tsxFull-screen AvocadoLoader.5
sitemap.ts / robots.tsMetadata routes for the apex domain. The subdomains get their own static files instead.125 / 23
feed.xml/route.tsRSS for the blog, revalidated hourly so admin-published posts appear without a rebuild.112
llms.txt/route.tsA machine-readable site summary for LLM crawlers — profile, page list, blog and lab entries. Same hourly cadence as the sitemap and feed.153

(portfolio)/ — the route group

Parentheses mean the folder adds no URL segment. So (portfolio)/page.tsx is /, not /portfolio — that route does not exist. Everything in here shares a Nav + Footer layout.

RoutePageLines
/The homepage — a long-scroll editorial narrative composing ~25 components: hero name, doodle field, dot grid, origin story, spotlights, featured work, skills constellation, opinions, RAG pipeline card, site vitals, testimonials, contact form, signature.803
/experience /education
/projects /apps
Career and work surfaces, rendered from the knowledge JSON.
/blog · /blog/[slug]
/blog/tag/[tag]
Index, post and tag index. The [slug] folder also holds opengraph-image.tsx, which generates a per-post social card at build time.
/lab · /lab/[slug]Living build logs. Same loader shape and OG-image pattern as the blog, different voice.
/gallery /quotes /nowSupporting pages.
/systemThe public observability dashboard — latency percentiles, cost, quality, reliability, trace waterfalls.
/mcpAn explorer for the public MCP server: lists the tools and lets you invoke them live.
layout.tsx / loading.tsxThe shared portfolio chrome and its loading state.

Routes outside the group

RouteWhat it is
/chatAvocado, full-screen. Deliberately outside (portfolio) so it gets no nav and no footer. A mobile FAB on portfolio pages links here.
/adminThe token-gated content editor. page.tsx is 4,395 lines — the largest source file in the repo — and orchestrates every editor component. layout.tsx marks it no-index; google-callback/ completes the OAuth handshake.
/gradevitian/*19 routes: home, the seven calculators, /ask, rules, account, login/signup, forgot- and reset-password, feedback, privacy, terms, plus 404/, not-found.tsx and opengraph-image.tsx.
/vrfbricks/*7 routes: home, bricks, delivery, visit, why-fly-ash, plus 404/ and its own OG image.
Why each subdomain has a 404/ folder

A static export never emits a segment's not-found.tsx as a real file. So each site also has a literal 404/page.tsx route, and nginx points error_page 404 at the HTML it produces. Similarly, opengraph-image.tsx exports extension-less, so each vhost must set default_type image/png for that exact path or no scraper renders the card.

src/data/ and src/content/

PathWhat it holdsStatus
data/knowledge/*.jsonTwelve files copied verbatim from the backend by the sync script.generated
data/*.tsThin typed wrappers — profile.ts, projects.ts, skills.ts, experience.ts, education.ts, apps.ts, gallery.ts, quotes.ts, spotlights.ts, testimonials.ts. Each imports its JSON, declares the interface, and re-exports. This is where the untyped JSON becomes typed.edit
data/gradevitian/pages.jsonThe single page list gradeVITian's nav, search modal and sitemap generator all read.edit
data/gradevitian/regulations.jsonCopy of the backend's curated regulations.generated
data/vrfbricks/business.tsVerified business facts only. Ends with a NEEDS_CONFIRMATION block; unverified fields are skipped at render time rather than guessed.edit
data/vrfbricks/copy.tsEvery rendered string, in English and Telugu. Kavali is a Telugu market, so the site is genuinely bilingual, not translated as an afterthought.edit
data/vrfbricks/testimonials.tsCustomer quotes, typed against the bilingual copy shape.edit
content/blog/*.mdxThree posts. Frontmatter: title, date (display), publishedAt (the sort key — set once, never changed), description, tags[].edit
content/blog/BLOGGING_GUIDE.mdThe house style guide for writing posts.edit
content/lab/itsjaya.mdxA 918-line living build log for this very repo.edit

Callout, BlogImage and Divider are auto-injected into MDX — no import line needed in a post.

src/lib/ — logic without JSX 2,570 lines

FileResponsibilityLines
api/client.tsThe backend fetch wrapper (apiPost and friends) — base URL, headers, error shape.79
api/content.tsTyped calls against /content/*. Used by SWR hooks in client components and by generateStaticParams at build time.138
content/blog.tsThe MDX loader — reads the files, parses frontmatter with gray-matter, sorts by publishedAt.86
content/lab.tsSame shape for lab entries.81
portfolio/site-nav.tsxSingle source of truth for the site's navigable pages. Nav, footer, search and sitemap all derive from it.93
portfolio/searchIndex.tsBuilds the ⌘K search index across pages, posts, projects and lab entries, with scoring.233
portfolio/seo.tsCanonical URLs plus the sitewide WebSite JSON-LD entity and per-section breadcrumbs.42
portfolio/pages.tsPage metadata used by breadcrumbs and nav.92
admin/githubStaging.tsxStages knowledge-JSON edits as GitHub commits from the admin UI.119
admin/useGitHubFile.tsHook to read and write one file through the GitHub API.63
gradevitian/calc.tsAll calculator maths — pure typed ports of the original site's client-side JS. Given inputs, returns results; no state, no DOM.403
gradevitian/seo.tsCanonicals, per-page metadata, JSON-LD for the subdomain.178
gradevitian/auth.tsAuth API calls and Bearer-token storage in localStorage.158
gradevitian/nav.tsxThe feature-page list the nav dropdowns, footer columns and home toolkit all derive from.71
gradevitian/usePersistentCalc.tsA useState-shaped hook that autosaves a logged-in user's calculator fields to the backend.53
gradevitian/searchIndex.tsSearch types and scoring over pages.json.47
gradevitian/regulations.tsTyped access to the curated VIT rules, so calculators cite real numbers.37
gradevitian/useGvBase.tsThe mount-point hook — returns "" on the subdomain, "/gradevitian" on the main domain.32
gradevitian/badges.tsxBadge definitions and their icons.50
vrfbricks/seo.tsCanonicals, metadata and JSON-LD — including the LocalBusiness entity.213
vrfbricks/nav.tsxSingle source of truth for VRF navigation.96
vrfbricks/useVrfBase.tsThe VRF mount-point hook.35
session.tsChat session persistence — messages survive a reload via a JSON round-trip.74
sound.tsUI sound effects with a user-toggleable mute.77
visitor.tsA stable per-device UUID in localStorage (jaya_vid), so stats count devices rather than shared-IP networks.20

components/ui/ — shared primitives 19 files, no product content

ComponentWhat it does
ThemeProvider · ThemeTogglenext-themes wrapper and the light/dark control.
ScrollReveal · ScrollProgress
ScrollToTop · ScrollAtmosphere
The scroll toolkit: reveal-on-enter, a progress bar, a back-to-top control, and an ambient background that responds to scroll position.
Parallax · ParallaxImageApple-style vertical parallax; the image variant pre-scales so no gap appears at the edges.
StackSectionSticky card-stacking scroll with content scrubbing — the homepage's signature scroll mechanic.
SectionIndicator · PageTransitionA scroll-position indicator, and page transitions using the native View Transitions API when available.
HeroDotGrid · LiquidWave · SparkleIconDecorative primitives. The sparkle is the site's AI glyph — a four-point star with a small companion.
PWARegister · InstallPWARegisters the portfolio service worker at root scope; InstallPWA is the install prompt UI.
JsonLd16 lines. Emits a structured-data script tag server-side so crawlers see it without running JS.
SoundToggle · MobileNoBgThe mute control, and a helper that drops heavy backgrounds on phones.

components/portfolio/ — the main site 33 files, ~6,300 lines

ComponentWhat it doesLines
Nav.tsxThe site header — dropdowns, mobile drawer, search trigger, theme toggle.449
HeroDoodleField.tsxThe hero's margin: hand-written stances you can pick up and throw. The most physical piece of interaction on the site.533
IntroScreen.tsxThe first-visit opening sequence.343
RagPipelineModal.tsxAn explainer of how Avocado retrieves — opened from RagPipelineCard on the homepage.321
QuotesClient.tsxThe interactive /quotes surface: filtering, categories, layout. QuotesFeed is its 30-line server shell.303
SearchModal.tsx⌘K search over the index built in lib/portfolio/searchIndex.ts.301
ContactForm.tsxThe contact form, sent via EmailJS from the browser (no server needed).292
HopeMolecules.tsxAn ambient node field with per-axis drift parameters.280
AvocadoChatButton.tsxThe mobile FAB that opens /chat.270
ProjectsGrid · GalleryGrid
TestimonialsCarousel
The three content grids, each driven by its knowledge JSON.231 · 229 · 223
SkillsSection · SkillsConstellationTwo views of the same skills data — a structured list and a constellation graph.203 · 184
McpExplorer.tsxPowers /mcp — lists the public MCP tools and invokes them live against /tools.206
StillRunning · SpotlightSection
Chapter · Breadcrumbs · HeroName
Homepage narrative sections and the shared content-width constraint.186 · 161 · 183 · 121 · 120
HeroStats · SiteVitals · SiteTrackerLive numbers from /stats; SiteTracker is the invisible component that records the visit.124 · 68 · 27
OriginStory · OpinionsTwo editorial chapters — why he builds, and what the work refuses to do.98 · 77
AvocadoMark · AvocadoLoader
Signature
Brand marks. The avocado is an emerald body with an amber pit; the signature draws its own rule.22 · 52 · 30

components/chat/ — Avocado 15 files, ~2,700 lines

ComponentWhat it doesLines
ChatInterface.tsxThe orchestrator: SSE stream consumption, message state, session persistence, model-badge updates when a fallback fires, analytics pings, agent-mode switching.838
ChatMessage.tsxOne message — markdown rendering, source citations, feedback controls.270
NavSuggestions.tsxTurns a reply into "go here next" links, so the chatbot routes people into the site.239
ChatLanding.tsxThe empty state — suggested openers before the first message.198
ChatCloseButton · ChatInput
ChatToolbar
The chrome: exit control, composer, and mode/settings bar.172 · 166 · 133
LeadCaptureCard.tsxThe recruiter capture card — posts to /ai/lead-capture, which emails an intro.156
Tile.tsx · RichCards.tsxThe card canon as a reusable primitive, and the rich results that render inside replies.107 · 96
LoadingGame.tsxA small game to play while a slow reply generates.103
AnswerTrace · AgentStepsThe transparency layer: which chunks were retrieved, and which tools the agent called.100 · 78
BookingCard · AvocadoBgReal calendar slots from /admin/calendar, and a single barely-there gradient wash.87 · 16

components/blog/ and components/lab/

ComponentWhat it doesLines
BlogGuideDrawer.tsxAn in-app authoring guide surfaced beside the editor.659
BlogIndexStats.tsxAggregate views and claps on the blog index, from /blog/stats/summary.624
BlogEngagement.tsxThe per-post view/clap widget. Claps are debounced 1.5 s before sending, so holding the button costs one request, not fifty.238
BlogImage.tsxAuto-injected MDX image with captions and lightbox.196
MDXComponents.tsxThe MDX component map — where Callout, BlogImage and Divider get auto-injected.109
BlogSwitcher · TableOfContents
ReadingProgress · ReadingMode
FontSizeControl · ProseReveal
The reading experience: post switcher, sticky TOC, progress bar, distraction-free mode, type-size control, paragraph reveal.96 · 74 · 64 · 64 · 74 · 62
CodeBlock · ShareButtons
BlogViewCount
Shiki-highlighted code, share links, and the small view counter.61 · 70 · 28
BlogSectionDynamic
BlogPostMarkdown
The path for admin-published posts, which live in content.db as plain Markdown rather than as MDX files.67 · 20
lab/LabList · LabMDXComponents
lab/LabSectionDynamic
The lab's three components, mirroring the blog's shape with its own MDX map.187 · 156 · 35

components/system/ — the /system dashboard

Nine files, ~620 lines. SystemDashboard.tsx fetches /stats/system and composes the panels; types.ts defines the payload contract.

PanelShows
LatencyPanelp50/p95/p99 and per-stage timings.
TraceWaterfallIndividual request traces from obs/trace.py, drawn as a waterfall.
QualityPanelFeedback ratings and answer quality signals.
ReliabilityStripError rate and fallback frequency across the model chain.
CostPanelToken spend by model.
PeriodToggle · StatThe time-range control and the shared stat tile.

components/admin/ — the editor 22 files, ~7,000 lines

Two distinct groups, and the difference matters: some editors write to GitHub (they edit the knowledge JSON, which is version-controlled), others write to the backend database (blog, lab and quotes authored in-app).

ComponentWrites toLines
LabEditor.tsxFull-featured authoring view for living system docs — the largest editor.1485
ContentLabEditor · ContentBlogEditor
ContentQuotesEditor
content.db via /content/*. Drafts, publish, delete.811 · 758 · 372
GalleryEditor.tsxImages and captions, with upload handling.484
AdminShared.tsxThe shared editor primitives — field types, list reordering, save state — used by every other editor.424
AvailabilityEditor · NowEditorAvailability windows and the /now page.277 · 240
SpotlightsEditor · KnowledgeBaseEditor
AppsEditor · SkillsEditor
KnowledgeDataView · ProjectsEditor
ProfileEditor · ExperienceEditor
TestimonialsEditor · EducationEditor
HeroStatsEditor
The knowledge JSON, staged as GitHub commits through lib/admin/githubStaging.tsx.242 · 220
209 · 187
191 · 170
166 · 139
137 · 135
114
GradevitianPanel.tsxgradeVITian admin: metrics and the comment moderation queue.136
PublishBar · StatCardThe save/publish control strip and a small metric tile.51 · 22

components/gradevitian/ 43 files, ~5,600 lines

The tools

ComponentWhat it calculatesLines
SemesterPlannerPlans a full semester's course load against credit rules.187
GradePredictorWhat you need on the final to land a target grade.149
AskRulebookThe Q&A surface over the regulations — posts to /gv/ask, shows cited clauses.143
CgpaCalculator · CgpaGoalTracker
CgpaEstimator
Current CGPA, the GPA needed to hit a goal, and forward projection.105 · 103 · 38
RulesReferenceBrowsable view of the curated regulations.96
GpaCalculator · AttendanceCalculator
AttendanceBar
Single-semester GPA, attendance percentage, and its visual bar.80 · 79 · 18

None of these hold the maths — every formula lives in lib/gradevitian/calc.ts as a pure function, so it is testable and shared.

Shell, accounts and growth

ComponentWhat it doesLines
GVHomeThe landing page — hero, grouped toolkit, social proof.516
GVNavCategorical dropdown nav, derived from lib/gradevitian/nav.tsx.454
GVIntroScreen · GVInstallFirst-visit sequence and the PWA install flow.281 · 272
AuthForms · GVAuthProvider
AccountDashboard
Login/signup/reset forms, the auth context, and the account view with saved calcs and badges.214 · 97 · 182
GVWallOfLove · CommentsWall
GVRefer
Social proof, the moderated comments wall, and the referral flow.136 · 90 · 110
GVFooter · GVSearchModal
GVStats · GVNotes · GVStats
Footer link columns, search, live usage counters, per-user notes.113 · 111 · 93 · 89
GVLink14 lines, and load-bearing. Prepends the mount-point prefix to internal hrefs. Always use this, never next/link directly.14
GVCanonicalRedirectBounces the path form on the main domain over to the subdomain.21
GVFaq · GVJsonLdA native <details> accordion that also emits matching FAQPage structured data — so what Google reads is what the visitor sees.46 · 16
GVServiceWorker · SaveCalcButton
Badges · GVResultModal · ui.tsx
PWA registration, the save-this-calculation control, badge display, result modal, and the shared UI primitives.30 · 58
56 · 72 · 108
GVHeroTitle · GVPageHeader
GVExploreMore · GVScrollTop
GVSearch · GVVisits
GVRedditEmbed · GVLinkedInEmbed
Presentation and cross-linking pieces.63 · 33
62 · 29
54 · 55
43 · 20

components/vrfbricks/ 19 files, ~2,900 lines

ComponentWhat it doesLines
VRFHomeThe landing page. Chaptered long-scroll, bilingual throughout.411
VRFNavHeader with the language switch.414
VRFField · VRFUiForm primitives and shared UI — Section, Inner, Chapter, Headline, Eyebrow, WhatsAppButton, CallButton, TextLink.258 · 225
VRFWhyPage · VRFBricksPage
VRFDeliveryPage · VRFVisitPage
The four content pages: why fly-ash, the product range, delivery terms, and how to visit the yard.215 · 212
153 · 146
BrickDiagramA dimensioned axonometric drawing generated from the brick's real measurements — not an illustration, a derived drawing.162
BrickCalculatorHow many bricks a wall needs. The original site answered this nowhere, so homeowners had to guess or phone up.131
QuoteComposerThe enquiry form is a WhatsApp message composer, not a form submission — it opens WhatsApp with the message pre-filled.116
whatsapp.tsDeep-link construction. WhatsApp is the primary conversion path on this site.70
VRFLangLanguage state (English ⇄ Telugu) for the whole site.81
VRFPhotoPhotographs at their true proportions, with honest sourcing.109
VRFFaq · VRFJsonLdSame accordion-plus-FAQPage discipline as gradeVITian, plus the LocalBusiness entity.77 · 20
VRFLink · VRFCanonicalRedirectThe mount-point link wrapper and the subdomain canonicaliser.20 · 22
VRFFooter · VRFContactFab
VRFTestimonials
Footer, the floating contact control on phones, and customer quotes.130 · 83 · 105
This one is a real business

VRF Bricks is the owner's father's brick yard. Do not invent details about it, and do not caption sourced stock photography as if it documents that specific yard. Unverified facts belong in the NEEDS_CONFIRMATION block in business.ts, where they are skipped at render time.

public/ — static assets 73 files

PathWhat it holds
CNAMETells GitHub Pages the custom apex domain.
manifest.webmanifest · sw.js
icon-192/512/maskable · apple-touch-icon
The portfolio PWA set, generated by scripts/gen-pwa-icons.mjs.
blog/15 images referenced from MDX as /blog/filename.png.
gallery/22 photos for /gallery — NYU IT, Wipro, VIT, Tailorbird, plus the system-architecture diagram.
gradevitian/The subdomain's own icon set, manifest, service worker, illustration SVGs, and robots.txt + sitemap.xml (the latter generated). nginx serves the last two at the subdomain root, because Google only reads robots.txt from an origin root.
vrfbricks/Five yard and masonry photos, plus its own robots.txt and generated sitemap.xml.

scripts/ repo-level codegen · 366 lines

All three run automatically from the frontend's predev and prebuild hooks. You rarely invoke them by hand.

ScriptWhat it generatesLines
sync-knowledge.mjsThe important one. Converts blog and lab MDX into backend/data/knowledge/blog.json and lab.json, then copies all knowledge JSON into frontend/src/data/knowledge/. Also copies the gradeVITian regulations across — which is why that file counts as a frontend CI input.137
gen-gv-sitemap.mjsBuilds public/gradevitian/sitemap.xml from data/gradevitian/pages.json. <lastmod> comes from the last commit touching each route's page file; if git can't answer, the entry ships without a lastmod rather than claiming "modified today" — the signal Google learns to distrust.91
gen-vrf-sitemap.mjsSame discipline for VRF Bricks.72
sync-blog.mjsThe earlier, blog-only version of the sync. Superseded by sync-knowledge.mjs, which handles blog and lab together.66

infra/ compose · nginx · deploy scripts

FileWhat it does
compose.ymlThe local full stack. Builds from the same Dockerfiles CI uses, so your local image matches production. Mounts backend/src and backend/data over the baked-in copies so --reload works, and uses an anonymous volume to stop the host bind-mount shadowing the image's node_modules.
nginx/api.jayaremala.com.confTLS termination for the API, proxying to 127.0.0.1:8000. Includes the ACME challenge location for cert renewal.
nginx/gradevitian.confPoints the subdomain at /var/www/gv-site (the whole static export) but exposes only the /gradevitian/ segment — the portfolio's routes 404 here. Maps robots.txt, sitemap.xml and manifest.webmanifest onto the segment's copies, and sets default_type image/png for the extension-less OG image. Note the comment about add_header: nginx does not merge headers, so any location with its own add_header must repeat all of them.
nginx/vrfbricks.confThe same pattern for VRF Bricks, from the same document root.
scripts/deploy.shBlue-green rollout. Restores any missing DB from S3 first, pulls the new GHCR image, starts it on :8001, health-checks it, and only then swaps it onto :8000. Tags the outgoing image :previous.
scripts/rollback.shSwaps :previous back in. Fails loudly if no previous image exists.
scripts/backup.shNightly cron. Ships the three SQLite DBs to S3 and prunes anything older than 7 days.

.github/workflows/

deploy.yml — five jobs, on push to main

JobDoes
detect-changesRuns dorny/paths-filter and outputs two booleans, frontend and backend.
buildStatic export. Checks out with fetch-depth: 0 so the sitemap generators can read git history for <lastmod>.
deployUploads to GitHub Pages and rsyncs out/ to Lightsail for the two subdomains.
sync-knowledgeCommits the regenerated JSON back to main with [skip ci], so it doesn't retrigger itself.
build-backend / deploy-backendBuild → push to GHCR → SSH to Lightsail → run infra/scripts/deploy.sh.
The paths-filter is load-bearing

It decides what deploys, so a missing entry means an edit silently ships nothing. Two entries exist for non-obvious reasons: Tailwind 4 has no config file, so its config is covered by frontend/src/**; and backend/data/gradevitian/regulations.json is listed under both filters, because the sync script copies it into the frontend bundle.

weekly-digest.yml

Cron at 13:00 UTC every Monday. A single curl to /admin/digest/send with the admin bearer token — the workflow holds no logic, only the schedule and the credential.


docs/

FileWhat it coversLines
VRFBRICKS.mdThe fullest subdomain doc: business context, the sourcing and honesty rules, bilingual copy strategy, nginx details.351
GRADEVITIAN.mdThe subdomain's architecture, mount-point mechanics, and local preview instructions.90
gradevitian/Academic-Regulations.pdfThe source PDF backend/scripts/parse_regulations.py chunks. Kept so the corpus is reproducible.
vrfbricks-original/The original single-file brick-yard site, preserved as the fact-check reference for everything in business.ts.1816
superpowers/plans/
superpowers/specs/
Spec-driven-development artifacts from two features: the gradeVITian admin metrics work (a 568-line plan plus its design spec) and the scroll-reactive hero wordmark.750

Rules that bite

The conventions where getting it wrong fails silently rather than loudly — worth knowing before your first change.

RuleWhat happens if you don't
Edit backend knowledge JSONEdits to frontend/src/data/knowledge/ are wiped by the next build. Same for blog.json and lab.json anywhere.
Run the backend from backend/Running from backend/src/ creates a second, empty ./chroma_db. No error — Avocado just knows nothing.
Keep paths-filter in stepYour change deploys nothing, and CI reports success.
Link via GVLink / VRFLinkA raw next/link breaks on whichever mount point you didn't test — subdomain or path form.
Sort blog by publishedAtdate is an editable display string; editing it to fix a typo would silently reorder the index.
No new Date() in client componentsHydration mismatch on a static export the moment the year rolls over. Use NEXT_PUBLIC_BUILD_YEAR.
Tailwind config is in globals.cssCreating a tailwind.config.js won't be read — Tailwind 4 uses @theme inline.
Bump fastmcp deliberately3.4.3 added Host allow-list enforcement; an implicit bump once broke the deployed MCP server. It is pinned >=3.4.3,<3.5 on purpose.
Keep the Dockerfile's embed model in step with rag/store.pyThe image pre-downloads the model at build time. A drift means a network fetch on every cold start.
Don't invent VRF Bricks factsIt's a real business. Unverified details go in NEEDS_CONFIRMATION, where render skips them.