Stacks v0.5 — interactive stack builder, GitHub App write path, full registry browse
Ship the Stacks feature end-to-end: a public catalog of who's using what, OAuth-gated edit at /people/{handle}/stack/edit, a Svelte StackBuilder that buffers drafts in localStorage and publishes via direct commit through a GitHub App bot, and a registry-wide browse at /stacks/see-all-tools so members can read the catalog without having to add tools to their own stack first. Plus the home page reflows: the Dojo cards explainer moves to a new /about placeholder, leaving the home column uniform with the hero.
Changelog — 2026-04-28 (02)
Stacks v0.5
This entry consolidates the full Stacks feature, which landed across several commits over the last 36 hours without its own changelog: b2789eb (initial read path), b42ef4f (write path + Svelte + GitHub App bot), af9d30e (tool registry seed + roster), a9da596 (header popdown + AlertDisplay), and the polish landing in this commit.
Why Care?
The Agentic VC Dojo’s launch webinar is in <2 days. The whole point of the dojo is to make it easy for venture professionals to share what they’re actually using — and to learn from each other’s setups. Until this feature, the site was read-only marketing copy. Now it’s a working community surface: members log in with GitHub, build their stack interactively, and a bot publishes a commit to lossless-group/fullstack-vc:main on submit. Vercel rebuilds in ~30 seconds and the public /people/{handle} profile updates. No database, no Auth0, no Notion sync — markdown files in a git repo all the way down, in line with the firm thesis that AI tools work best on markdown and JSON.
What Got Built
Read path (public, static)
/people/[handle]— per-participant profile rendering current / aspirational / abandoned stacks viaParticipantStackView. Headshot priority: explicitheadshot:field →github_avatar./people— directory of members with public profiles./stacks— community-aggregate front door. Shows the most-adopted tools in current use (top 8) and members with public stacks./stacks/see-all-tools(new this commit) — flat alphabetical listing of every entry in the registry, with adoption-count annotations on tools that show up in someone’scurrent_stack. Front-door surfaces only the most-adopted handful; this page makes the whole registry browseable without having to add tools to your own stack first.ToolCard,ParticipantCard,ParticipantStackView— display components, mode-aware semantic tokens, no JS. All cataloged in/design-system.
Tool registry seed
- ~14 entries seeded so far, copy-pasted from the Lossless Obsidian
Tooling/*vault (Astro, Svelte, Claude Code, Cursor, Windsurf, Ghostty, n8n, CrewAI, Hermes, Pi-Dev, Bolt, Ideogram, Obsidian, Jina, ImageKit, Fallow Tools). - Schema is a strict superset of the Obsidian Tooling frontmatter (Jina + OpenGraph fetch pipeline) so files paste in unchanged.
Tool schema fix (this commit)
The schema declared OG-prefixed names (og_image, og_favicon, url_aliases) but Obsidian source files use the bare names (image, favicon, aliases). .passthrough() masked the issue (files validated), but typed renderers querying data.og_image silently missed data stored under image, so e.g. Ghostty’s favicon never rendered. Fixed by:
- Adding bare-name fields to the Zod schema as first-class typed properties (
image,favicon,aliases,hero_image_url), plus typed entries for the fields that were passing through unannotated (docs_url,github_profile_url,github_repo_url,product_of,publish). - Renderer fallback in
ToolCard.astro:og_screenshot_url ?? og_image ?? image ?? hero_image_url;og_favicon ?? favicon. - Same fallback in
/api/tools.json.tsso the StackBuilder picker typeahead inherits favicons.
The schema is now an actual strict superset of the Obsidian Tooling frontmatter, as the spec promised.
Auth (Phase 1: GitHub OAuth, hand-rolled)
/api/auth/github/{login,callback}+/api/auth/logout— three endpoints, ~100 LOC, no Auth0 / Clerk / Lucia. Session is ajose-signed JWT in an HttpOnly cookie.- Roster match is by GitHub handle first, with email fallback for registrants not yet roster’d by handle (most webinar attendees are roster’d by email only).
- LinkedIn OIDC scaffolded but not yet wired into the public flow.
Write path (the no-DB challenge, solved)
StackBuilder.svelte— the only interactive island on the site. Editscurrent_stack(v1 scope); aspirational + abandoned tiers are hand-edited in the participant.mdfor now. Buffers every keystroke tolocalStorage["stack-draft:{handle}"]so refresh / navigate / close-tab doesn’t lose work. 5-min idle threshold + visibility-loss event both trigger best-effort auto-save.POST /api/stack/save— Zod validation, frontmatter merge that preserves UI-untouched fields (so editing the current stack doesn’t blow away your aspirational stack), retry-on-409 for SHA conflicts.- GitHub App bot auth — App ID + Installation ID + Private Key (PKCS#1 or PKCS#8, both handled via Node
crypto.createPrivateKey()+joseSignJWT). Lazy env reads so HMR picks up.envchanges without restart. - Direct commit to
mainwith prefixdata(stack):for filterable git history (git log --invert-grep "^data"filters the noise so feature commits stay readable). PR + auto-merge was the spec’s original Phase 1; direct-commit was chosen for v0.5 to ship inside the 36hr launch window. PR-with-auto-merge becomes a v0.6 hardening pass. scripts/smoke-test-github-app.ts— direct READ + WRITE auth check, no dev-server needed. Saved hours when debugging the App ↔ Installation handshake.
Header surfaces (prev commit a9da596)
JumboPopdown__More— third sibling popdown alongside Groups and Projects. Static items (Stacks, People) under a “More” trigger.AlertDisplay— inline announcement bar with primary→secondary gradient and an×dismiss button. Per-id session-scoped dismissals viasessionStorage. The home page now opens with one clarifying that “FullStack VC is NOT a VC.”- Top utility strip with an
Aboutlink (above the main nav row, hidden on mobile). - Mobile drawer — gained Stacks, People, About entries.
Home / About page split (this commit)
The new Section__HowItWorks plus the home hero conflicted on column width — the section’s three-card explainer wanted ~1100px, the hero wanted max-w-4xl. Resolution: extract the cards into Section__DojoComponents (kept at 72rem) and ship it on a new /about placeholder page. The home page now reads as a single uniform column hero → Dojo intro + hierarchy diagram → “Learn more about the Dojo →” → footer. Section__HowItWorks accepts an optional hideMore prop so the about page can suppress the self-referencing link.
Design System catalog
Per the design-system maintenance motion, every component introduced lands in /design-system in the same change. Added in this batch: JumboPopdown__More, AlertDisplay, both with their own demo subpages.
Behavior
- All read paths are fully static at build time. The only server endpoints are
/api/auth/*and/api/stack/save. - Submit → commit → Vercel rebuild → live ≈ 30 seconds.
- StackBuilder draft survives a tab close until the user explicitly clears
localStorage. - Roster mismatch (logged-in GitHub user not on the Kauffman list) lands on a friendly
/login/not-on-rosterpage rather than the edit screen. - The bot writes only ever start with
data(...)so feature-commit history stays scannable.
Decisions Encoded That Revise the Original Spec
- URL structure: canonical per-person path is
/people/[handle](not/stack/people/[handle]); aggregate front door is/stacks(plural)./stack/meis preserved as a backward-compat redirector. - Direct-commit chosen over PR + auto-merge for v0.5 launch velocity. Audit-trail concerns become a v0.6 hardening pass.
/stacks/see-all-toolsadded because the front-door page caps at the top 8, which made registry browsing impossible without first adding a tool to your own stack.
Open Work for v0.6
- Aspirational + abandoned tier editing in StackBuilder (current_stack only today).
- Free-text “add a tool not in registry” → save creates
tools/{slug}.mdplaceholder + participant file in the same submit. - Per-tool detail page (
/stacks/tools/[slug]) that renders the tool’s body markdown. - Full
/stacksaggregate: heatmap (categories × tools, intensity = adoption count), leaderboards, sparklines fromadded:dates. - PR-with-auto-merge write path for audit-trail value.
- Cohort views (
/stack/cohorts/kauffman-[year]). - AI-assisted tool metadata enrichment (Browserless + LLM agent for tool YAML autogen from a URL).
Verification
pnpm build— clean across all routes, including/stacks/see-all-tools,/about,/design-system/components/alert-display,/design-system/components/jumbo-popdown-more.- Tool schema fix verified by inspecting Ghostty’s card on
/stacks/see-all-tools— favicon now renders (was previously blank because the file usesfavicon:notog_favicon:). - StackBuilder smoke-tested end-to-end: log in via GitHub OAuth → /people/mpstaton/stack/edit → drag a tool → wait 5 minutes → idle auto-save fires → page refresh → draft restored from localStorage → submit → commit lands on main with
data(stack):prefix → Vercel rebuilds → public/people/mpstatonreflects the change. scripts/smoke-test-github-app.tsconfirms App credentials independently of the dev server.
Files modified (11)
sites/fullstack-vc/src/components/sections/Section__HowItWorks.astrosites/fullstack-vc/src/components/sections/Section__DojoComponents.astrosites/fullstack-vc/src/components/stack/ToolCard.astrosites/fullstack-vc/src/content.config.tssites/fullstack-vc/src/pages/about/index.astrosites/fullstack-vc/src/pages/api/tools.json.tssites/fullstack-vc/src/pages/design-system/components/alert-display.astrosites/fullstack-vc/src/pages/design-system/components/jumbo-popdown-more.astrosites/fullstack-vc/src/pages/design-system/index.astrosites/fullstack-vc/src/pages/stacks/index.astrosites/fullstack-vc/src/pages/stacks/see-all-tools.astro