# Executive Summary — personal-site ("The Construct") Codebase Analysis

Date: 2026-07-01 · Synthesizer roll-up of four `codebase-*` skill reports

---

## What this repo IS

`personal-site` is Boden Fuller's public site **bodenfuller.com ("The Construct")** — a Next.js 16 App Router marketing/proof/writing property with a filesystem-backed MDX blog, a per-request strict CSP, and a bespoke "phosphor-terminal" design system. It is a **content-as-code hybrid**: ~9,451 LOC of code (`src/`, `middleware.ts`, `scripts/`, `e2e/`) wrapped around a much larger ~728-file markdown knowledge/career workspace (`career/`, `.agents/`, `docs/`, root canon docs). Only the code surface plus the 25 rendered MDX essays are software; the rest is architecture context, deliberately excluded from code audit.

The whole architecture pivots on one deliberate constraint: rendering is server-default (RSC), a nonce-based strict CSP is minted per request in `middleware.ts`, and `style-src` is intentionally left `'self' 'unsafe-inline'` (`src/lib/constants.ts:22`) because the design system styles via React inline `style={{}}` attributes — which nonces do not cover. That single decision shapes rendering, deployment, and half the findings below.

## Architecture-health verdict

**Healthy and unusually deliberate for a personal site — no critical defects, but carrying one real security exposure and a cluster of DRY/consistency debt.** The security scaffolding (strict `script-src`, documented `style-src` rationale, path-traversal guards, XSS-escaping JSON-LD serializer) and accessibility scaffolding (skip link, reduced-motion kill-switch, focus-visible, labeled hero, axe e2e) are genuinely strong and verified, not assumed. The two soft spots: the pinned Next.js version sits on an XSS advisory that targets this app's own nonce model, and the home page's JSON-LD bypasses both the nonce path and the escaper. Neither is catastrophic; both are cheap to close.

---

## The four reports

**01 — Archaeology** (`01-archaeology.md`): Maps the layer model — `middleware.ts` (nonce+CSP) → `layout.tsx` (`await connection()` forces dynamic render, wires fonts to CSS vars) → RSC pages with surgical client islands → `src/lib`/`src/data` → filesystem (no DB, no external API in request paths). Identifies three sources of truth (`src/content/writing/*.mdx`, `src/data/builds.ts`, `src/lib/mission.ts`) and nine maintenance gotchas. Establishes that `getAllPosts()` (`src/lib/blog.ts:31-114`) is the single content pipeline feeding pages, `/api/posts`, `sitemap.ts`, and `/llms.txt`.

**02 — Architecture Report** (`02-architecture-report.md`): The handoff-grade technical writeup of the same seam, centered on the CSP. Notably **corrects the scout map**: `.github/workflows/ci.yml:13-105` DOES exist and runs `validate` → e2e + vibe-check on push/PR to main — CI is a real enforcement layer, not absent. Also flags Node version drift (`package.json:74` pins `24.x`; CI `ci.yml:23` and `Dockerfile:5` use `node:20-alpine`, Dockerfile self-labeled "Next.js 15") and the hand-maintained `sitemap.ts:8-17 SITEMAP_SOURCE_PATHS` route list.

**03 — Audit** (`03-audit.md`): 17 findings across security/perf/ux-a11y/api — **Critical 0 · High 1 · Medium 6 · Low 10**. The one High is the outdated Next.js on the CSP-nonce XSS advisory (GHSA-ffhc-5mcf-pf4q). Mediums cluster around the home JSON-LD, per-request MDX recompilation, the blocking `git log` sitemap spawn, mobile-nav focus order, and low-contrast accent text. Confirms the CSP/a11y scaffolding is genuinely solid.

**04 — Pattern Extraction** (`04-pattern-extraction.md`): 9 recurring un-extracted patterns. Highest-value: 7 copy-paste OG images where the DRY helper (`src/lib/proof-og.tsx:10-63`) already exists and is proven by `src/app/work/*`; ~100 hardcoded neutral-ramp hex literals that block the amber "Drawing Set" reskin; and the home nonce-less JSON-LD (same defect as audit M1, cross-confirmed). Notes which three extractions (OG renderer, color ramp as per-skin CSS vars, `.mjs` script harness) pay off twice across Bo's green/amber skins.

---

## Prioritized action list

### P0 — do first (security correctness)

1. **[SECURITY/High] Update Next.js off the CSP-nonce XSS advisory.** `package.json` `"next": "^16.1.7"` resolves to **16.2.4**; `npm audit` flags GHSA-ffhc-5mcf-pf4q ("XSS in App Router apps using CSP nonces") plus middleware-bypass and image-DoS advisories across `16.0.0–16.2.5`. This targets the app's entire nonce-based security model. Fix: `npm install next@latest` (≥16.2.6), re-run `npm audit`, add a CI audit gate. — *Audit H1 / `package.json:74`*

2. **[SECURITY/Correctness] Migrate home JSON-LD to `JsonLdScript`.** `src/app/page.tsx:33-36` emits structured data via raw `<script dangerouslySetInnerHTML={{__html: JSON.stringify(jsonLd)}}>` — no nonce (at risk of being stripped under strict-dynamic prod CSP, losing Person/WebSite structured data) and bypasses `serializeJsonLd` escaping. Every other block uses the nonce-aware component. One-line fix: `<JsonLdScript id="home-jsonld" data={jsonLd} />`. Verify against `next build && next start`, not `next dev`. — *Audit M1 + Pattern P4 (cross-confirmed) / `src/app/page.tsx:33-36`*

### P1 — high value

3. **[PERF] Cache the per-request MDX compile.** `layout.tsx:103 await connection()` forces the whole site dynamic, defeating `writing/[slug]` `generateStaticParams` so `MDXRemote` + `rehypeHighlight` (`page.tsx:96-104`) re-compiles and re-highlights every post on every request. Wrap `getPostBySlug`/compiled MDX in React `cache()`/`unstable_cache` keyed by slug. — *Audit P1 / `src/app/writing/[slug]/page.tsx:96-104`*

4. **[PERF] Stop the blocking `git log` in sitemap.** `sitemap.ts:25-30 spawnSync('git log')` runs ~9× per request; on Vercel (no `.git`) each fails through to `statSync` + a `console.warn`. Compute dates at build time or use frontmatter/`builds.ts` dates. — *Audit P2 / `src/app/sitemap.ts:25-30`*

5. **[DRY/High] Collapse the 7 copy-paste OG images onto the existing helper.** `src/app/{about,method,ai-partner,training,work,writing}/opengraph-image.tsx` (~55 LOC each) re-inline JSX that `renderProofOg` (`src/lib/proof-og.tsx:10-63`) already extracts; `src/app/work/vibe-check/opengraph-image.tsx` proves the 15-line call. Biggest LOC deletion, near-zero risk. Take the accent color as a param so the amber skin reuses it. — *Pattern P1 / `src/lib/proof-og.tsx:10-63`*

6. **[DRY/High] Promote the ~100 hardcoded neutral-ramp hexes to semantic tokens.** `#8a948d` (~28-29×), `#9aa39c` (15×), `#c2cbc5` (13×), `#828c84` (13×), `#aab4ad` (11×) — 175 total inline hex across `construct`+`app`. Add a `COLORS`/`C` set to `src/components/construct/tokens.ts` (stay inline-style per the `style-src` decision), then promote to per-skin CSS vars like `--acc` so the amber Drawing Set reskin becomes config, not find-replace. — *Pattern P5 / `src/components/construct/tokens.ts`*

7. **[A11y] `inert`/`hidden` the collapsed mobile nav.** `Header.tsx:92-155` hides the menu with `max-h-0 opacity-0` without `hidden`/`aria-hidden`/`tabIndex`, leaving ~7 invisible links in the keyboard tab order. `inert` when closed is the one-attribute fix. — *Audit UX1 / `src/components/Header.tsx:92-155`*

8. **[A11y] Raise low-contrast accent metadata text.** Date/reading-time/tags/labels render in green-500 at 0.6 alpha ≈ 3.6:1 on `#070a08`, below WCAG AA 4.5:1 for informational text. Raise to ≥0.8 alpha or use `--acc-b`; add an axe contrast assertion to the e2e a11y spec. — *Audit UX2 / `src/app/writing/[slug]/page.tsx:80-82`, `src/components/construct/tokens.ts:19-24`*

### P2 — polish / debt

9. **[SECURITY] Narrow `img-src`.** `constants.ts:32` allows any `https:` origin; narrow to `covers.openlibrary.org` per `next.config.ts` `remotePatterns`. — *Audit M2 / `src/lib/constants.ts:32`*

10. **[DRY] Extract `buildPageMetadata()`.** Title+description repeat up to 3× (metadata/openGraph/twitter) on 10+ pages, and `about/page.tsx` + `writing/[slug]/page.tsx` already drop `twitter` — the exact drift a builder kills. — *Pattern P2 / `src/lib/metadata.ts` (new)*

11. **[DRY] Extract `wrapJsonLdGraph()` + `AUTHOR_NODE`.** The `@context/@graph` envelope is hand-inlined 3× and the author Person node repeats 5-6× in `jsonld.ts`. Also add the missing `TechArticle`+`Breadcrumb` JSON-LD to the 4 case pages that emit none. — *Pattern P3/P4 / `src/lib/jsonld.ts`*

12. **[API] Standardize the response envelope.** `api/posts/route.ts`, `api/posts/[slug]/route.ts`, and error paths use three different shapes with no versioning. — *Audit API1 / `src/app/api/posts/`*

13. **[DRY] `StatRow`→`Stats` merge + `CACHE_1H_1D` constant.** `case.tsx:27-38 StatRow` is `Stats` with `cols=4` hardwired; the cache literal repeats in 3 routes + 3 tests. — *Pattern P6/P8 / `src/components/construct/case.tsx`, `src/lib/constants.ts`*

---

## Quick wins (cheap, high-value)

- **Home JSON-LD → `JsonLdScript`** — one-line swap, closes a security correctness gap (P0 #2).
- **`inert` the closed mobile nav** — one attribute, closes an a11y Medium (P1 #7).
- **`npm install next@latest`** — one command + `npm audit` re-run, closes the only High (P0 #1).
- **`StatRow`→`Stats` merge** — delete a proven duplicate, callers pass `cols={4}` (P2 #13).
- **`CACHE_1H_1D` constant** — hoist one string, kills route/test skew (P2 #13).
- **7 OG images → `renderProofOg`** — largest LOC deletion in the repo, helper already ships (P1 #5).

---

## Gaps, conflicts, and human decisions

- **Scout map was wrong about CI; report 02 is right.** The scout brief (and report 01's implication) claimed "no GitHub Actions / `.github/workflows` empty." Report 02 verified `.github/workflows/ci.yml:13-105` exists and enforces `validate` → e2e + vibe-check. **Treat CI as present** and use it as the home for the P0 `npm audit` gate.
- **Node version drift is unreconciled and needs a human call.** `package.json` engines `24.x` vs CI/Dockerfile `node:20-alpine` (Dockerfile also self-labeled "Next.js 15"). Canonical deploy is Vercel, so the Dockerfile may be dead metadata — decide whether to fix it to `24`/Next 16 or delete it. No skill ruled on this. — *`package.json:74`, `ci.yml:23`, `Dockerfile:5`*
- **The `style-src 'unsafe-inline'` decision is deliberate — do NOT "harden" it.** Both the audit and pattern reports independently flag this as correct (`constants.ts:22`); any color-token work (P1 #6) must stay inline style, not migrate to CSS classes. A future automated security scan will likely re-flag this as a false positive.
- **No skill audited the ~728-file markdown workspace** (`career/`, `.agents/`, canon docs) — correctly excluded as content, but note there is no coverage of link-rot, stale-fact, or consistency risk in that layer if that ever matters.
- **Coverage thresholds are deliberately low** (statements 14 / branches 12 / functions 13 / lines 14, `vitest.config.ts:28`). No skill judged whether that is acceptable for a personal site — a human should confirm this is intentional rather than accidental erosion.
- **Minor metric drift across reports** (LOC ~8,241 vs ~9,451; unit-test count 18 vs 20 vs 23; one-off hex counts off by one). Immaterial to any finding; the audit/pattern file:line citations are the load-bearing evidence.
- **In-flight uncommitted work** (`src/app/first-win/`, `outputs/`, modified `ai-partner/page.tsx`, `sitemap.ts`, `construct/primitives.tsx`) was present during analysis — re-verify findings that touch those files against the committed state before acting.
