One site, three views.
Merge drewkhoury-website (Hugo, 22 essays + bio + GSD series) into portfolio-clean (Astro, 12 projects + 40 experiments). Result: a single site at drewkhoury.com that shows everything Drew has shipped — readable, hireable, and playable — with three switchable homepage modes for different audiences.
Operating principles
Preserve URLs verbatim
Medium-UUID slugs stay. The Manager README URL stays. SEO and inbound links survive untouched.
Migrate everything timestamped
Every essay, talk, project, and venture — including the dormant ones. The timeline IS the artifact.
One content collection, multiple views
Almanac (default) is the chronological feed; Editorial leads with writing; Atelier leads with making. Settings toggle.
Reverse the merge direction
Portfolio's design system + Astro stack. But drewkhoury.com is the canonical domain. portfolio-drew.web.app retires.
Before / after
Two sites, two stacks, two identities
drewkhoury.com— Hugo, hugo-clarity theme, S3-hosted. 22 essays, bio, manager README, GSD pillar.portfolio-drew.web.app— Astro 6.3, themed, Firebase. 12 projects, 40 experiments, Cmd+K palette.- No cross-linking. Two design systems. Two URLs to share.
One site, one stack, layered identity
drewkhoury.com— Astro, Firebase, the portfolio's design system.- Almanac is the front door (chronological feed of everything).
- Editorial / Atelier modes available via settings ⚙.
- About page tells the evolution story (consulting → principal → maker).
portfolio-drew.web.appredirects to drewkhoury.com.
Where to start
Make the Decisions first — they unlock the migration. Then review the URL Map and Schemas. The Phases tab lays out the 4-week build sequence. Mockups show the destination.
Inventory
All 52 content entries from the Hugo site, each with a suggested new category, new URL, and migration plan. Five views over the same data. Click a pill to filter by type; click a chip to scope.
Decisions
14 choices that unblock migration. Your selections persist in this browser. Recommendations are pre-marked. None are locked — change your mind anytime.
Progress: 0 / 14 decisions made
Each decision shapes a migration step. Once all 14 are made, the script can run end-to-end without further input.
URL Map
Every URL on the Hugo site and what happens to it. Preserve means the URL keeps resolving on the new site (zero-risk path). Redirect means a 301 in firebase.json. New means a new surface that didn't exist before. Removed means deleted (rare).
Essays — all UUIDs preserved (22 URLs)
Reports (3) + Manager README consolidation
Notes (3)
Talks — moved from /categories/public/ to /talks/ (11 URLs)
Projects — added to existing /projects/ collection (2 URLs)
Pages — consolidations (5 URLs + redirects)
Aliases — Hugo declared these as redirect targets; preserve as redirects
New surfaces — added by the merger
Content collection schemas
Astro content collections use Zod schemas. Each content type gets its own collection with a defined front-matter shape. The migration script transforms Hugo TOML/YAML into this Astro shape.
Essay essay
What it is
Long-form thinking, 500+ words. Standard reading layout. Standalone or part of a series. URL: /essays/[slug] (or preserved Medium-UUID slug at /post/[slug]).
Source content
22 entries: 14 main essays + 8 GSD essays. All migrated verbatim, slugs preserved.
// src/content/config.ts const essays = defineCollection({ type: 'content', schema: z.object({ title: z.string(), date: z.date(), description: z.string().optional(), tags: z.array(z.string()).default([]), series: z.string().optional(), // 'gsd' | '3-musketeers' | 'dx' | 'one-devops-please' slug: z.string().optional(), // preserved Medium UUID slug featureImage: z.string().optional(), coAuthors: z.array(z.string()).default([]), eraNote: z.string().optional(), // "Written in 2019 Liatrio era, GAE/serverless has shifted since." status: z.enum(['active', 'archived']).default('active') }) });
Report report
What it is
Pillar pieces with extra craft — "the occasional ten-page report nobody asked for". Visually special in feed. URL: /reports/[slug].
Source content
3 entries: Manager README, Principals for Principals, Optimizing for DX in Cloud-Native World.
const reports = defineCollection({ type: 'content', schema: z.object({ title: z.string(), date: z.date(), description: z.string(), tags: z.array(z.string()).default([]), pinned: z.boolean().default(false), // Manager README is pinned readingTime: z.number().optional(), // minutes coverImage: z.string().optional(), tldr: z.string().optional() // pull-quote for hero }) });
Note note
What it is
Short, dated, roundups. Less polished than essays. URL: /notes/[slug].
Source content
3 entries: AWS 2021 Highlights, IT Consultancy Ramblings, possibly VSM.
const notes = defineCollection({ type: 'content', schema: z.object({ title: z.string(), date: z.date(), tags: z.array(z.string()).default([]), eraNote: z.string().optional() }) });
Talk talk
What it is
Public speaking entries. Workshop / meetup / podcast / webinar. URL: /talks/[slug].
Source content
11 entries, 2013–2022.
const talks = defineCollection({ type: 'content', schema: z.object({ title: z.string(), date: z.date(), venue: z.string().optional(), // "Melbourne Meetup", "Cloud Native Louisville" format: z.enum(['meetup', 'workshop', 'webinar', 'podcast', 'panel']), coHosts: z.array(z.string()).default([]), recordingUrl: z.string().url().optional(), slidesUrl: z.string().url().optional(), city: z.string().optional() }) });
Project project
What it is
Things Drew built. Already defined in portfolio-clean. Hugo adds 2 entries.
Source content
14 total (12 existing + The Network 2022 v1 + Ari by Design).
// Extends portfolio-clean's existing project schema with: status, era const projects = defineCollection({ type: 'data', schema: z.object({ // existing fields... name: z.string(), slug: z.string(), tagline: z.string(), intro: z.string(), // new fields for the migration: status: z.enum(['active', 'dormant', 'archived', 'historical']).default('active'), era: z.string().optional(), // "2022 v1", "2026 rebuild" supersededBy: z.string().optional() // slug of newer version (e.g., the-network → the-network) }) });
Page page
What it is
Static surfaces not in the chronological feed. Implemented as .astro files in src/pages/, not a content collection.
Source content
5 pages: /about, /manager-readme, /hire, /now (optional), /archive.
Build phases
Four phases, roughly one weekend each. Check off as you go — progress persists. Phase 1 unblocks everything else; Phase 4 is the cutover.
Sample pages
Visual proof of what the new site looks like. Three homepage modes plus deep pages for reading, about, and a series index. Open each to flick through; they share a design system.
Homepage modes (already built)
Deep pages (new — to be built)
Mockups intentionally minimal — focus is on layout + IA, not pixel-perfect styling. The real build inherits from portfolio-clean's design system.
Risks register
Things that could go wrong, with a planned mitigation for each. Severity reflects impact × likelihood, not just impact.