Projects Architecture
Introducing Projects as a first-class organizing concept to give docs, posts, and demos the context they need.
April 2, 2026That means the site needs a real organizing unit. Right now content floats — docs and posts exist without context, and when you're reading one you have no sense of where you are relative to anything else. The fix is Projects as a first-class concept.
The Core Idea
A Project is the parent of everything. Docs, posts, demos, commits, assets — all of it belongs to a project. Nothing is published in isolation.
Projects
jaygriff-v2 ← the site itself, actively documented
some-cli-tool
old-experiment ← archived, still browsable
This isn't just an organizational nicety. It's what gives a doc its context. A doc titled "Content Pipeline: Technical Deep Dive" is meaningless without knowing it's the jaygriff-v2 project. Under a project, it's immediately intelligible.
What Changes
New projects table
Projects need to be first-class DB entities, not just a string column on content.
Proposed schema:
CREATE TABLE IF NOT EXISTS projects (
id TEXT PRIMARY KEY,
slug TEXT UNIQUE NOT NULL, -- URL segment, e.g. "jaygriff-v2"
name TEXT NOT NULL, -- display name, e.g. "jaygriff v2"
description TEXT,
status TEXT NOT NULL DEFAULT 'active', -- active | archived | graduated
color TEXT, -- optional oklch accent color
icon TEXT, -- emoji or lucide icon name
url TEXT, -- external URL if graduated
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)
status values:
active— currently being worked on, shows prominently in sidebararchived— done or dormant, still browsable but de-emphasizedgraduated— shipped commercially, link out to external URL, no content hosted here
project_id on content becomes a real foreign key
Currently stubbed as nullable. With the projects table it becomes a real reference. Content without a project_id should eventually be disallowed — everything belongs somewhere.
Routes
Option A — Namespaced routes: /projects/jaygriff-v2/docs/[slug]
Option B — Flat slugs with project context in UI: /docs/[slug] stays the same, sidebar and page header show the project.
Recommendation: Option B. Slugs are globally unique, existing URLs don't break, and project context is a UI concern not a routing concern. Can revisit if projects ever have slug collisions (unlikely).
Sidebar Design
Expanded
jaygriff-v2 ← logo + name, links to homepage
Projects
▼ jaygriff-v2 ← auto-expanded because you're currently in it
Docs
Posts
▶ some-tool
▶ old-thing ← archived, visually de-emphasized
──────────────────
Theme
Collapse
The active project expands automatically based on the current page's content project_id. When you're reading a doc, the sidebar shows the project it belongs to and sibling content types as sub-nav. This solves the "naked doc" problem — you always know where you are and can navigate laterally.
Collapsed (icon-only)
One icon per project. Tooltip shows project name on hover. Site logo at top, theme + collapse at bottom. Same structure, just compressed.
Content Directory Structure
Move files into project subdirectories. The sync script reads the subdirectory name, resolves it to a project_id via DB lookup, and populates the column on every INSERT and UPDATE.
content/
md/
jaygriff-v2/
notes.md
content-pipeline-deep-dive.md
styling-conventions.md
admin-auth-plan.md
market-statistic.md
projects-architecture.md ← this file
The existing rename detection (hash match) handles the moves — file_path updates, slugs are preserved, no broken URLs.
Implementation Order
scripts/migrate-projects.ts— createprojectstable, run once- Seed
jaygriff-v2— insert the first project row manually or via a seed script - Restructure
content/md/— move all files intocontent/md/jaygriff-v2/ - Update sync script — parse subdirectory name → resolve
project_id→ populate on INSERT/UPDATE - Add
getAllProjects()tosrc/lib/content.ts(and agetProjectBySlug()) - Rebuild sidebar — one
SidebarGroupper project, collapsible, active state driven by current content'sproject_id - Add project context to doc/post pages — breadcrumb or header line showing which project you're in
What's Not Decided Yet
- Project overview page — does
/projects/jaygriff-v2exist as a route? Obvious answer: yes, a README-style page with description + recent docs/posts. - Collapsed sidebar icons — one icon per project; emoji is fine for now, can upgrade to custom SVGs later
- Content types per project — only show types that actually have content. Let the DB drive it rather than hardcoding "every project has Docs and Posts".
- Cross-project content — single
project_idper content item is fine. Not worth solving multi-project tagging. - Admin UI — project management (create, edit, archive) will live at
/admin/projects, same pattern as content admin planned inadmin-auth-plan.md.