-
Your Vibed profile
+
Your Unified VCP
{typeof profileContribution.profileTotalRepos === "number"
? `${profileContribution.profileTotalRepos} repos`
@@ -1428,232 +1184,9 @@ export default function AnalysisClient({ jobId }: { jobId: string }) {
- Share, timeline, and details
+ Timeline and details
- {shareTemplate ? (
-
- {/* Share Card Preview */}
-
-
- {/* Header */}
-
-
-
- My Vibe Coding Style
-
-
- {shareTemplate.headline}
-
-
- {shareTemplate.subhead}
-
-
-
- {data?.userAvatarUrl ? (
-
- ) : (
-
- V
-
- )}
-
-
-
- {/* Vibe Metrics Grid */}
-
- {/* Streak */}
-
-
- Longest Streak
-
-
- {wrapped.streak.longest_days} day{wrapped.streak.longest_days === 1 ? "" : "s"}
-
-
- {/* Peak Window */}
-
-
- Peak Window
-
-
- {wrapped.timing.peak_window ? formatMetricLabel(wrapped.timing.peak_window) : "—"}
-
-
- {/* Commit Style */}
-
-
- Commit Style
-
-
- {wrapped.chunkiness.label ?? "—"}
-
-
- {/* Build/Fix Ratio */}
-
-
- Feature / Fix
-
-
- {wrapped.commits.features_per_fix !== null
- ? `${formatMetricValue(wrapped.commits.features_per_fix, 1)} : 1`
- : wrapped.commits.fixes_per_feature !== null
- ? `1 : ${formatMetricValue(wrapped.commits.fixes_per_feature, 1)}`
- : "Balanced"}
-
-
-
-
- {/* Footer */}
-
-
- {shareOrigin ? new URL(shareOrigin).hostname : "vibed.dev"}
-
-
- {wrapped.totals.commits} commits{metricsJson?.active_days ? ` · ${metricsJson.active_days} active days` : ""}
-
-
-
- {shareDownloadError ? (
-
- ) : null}
-
-
- {/* Action Buttons - Consolidated */}
-
- {/* Download Section */}
-
- setShareFormat(e.target.value as ShareFormat)}
- >
- {Object.entries(SHARE_FORMATS).map(([key, f]) => (
-
- {f.label}
-
- ))}
-
- handleDownloadSharePng(shareFormat)}
- disabled={downloadingShare}
- title="Download PNG"
- >
-
- PNG
-
- handleDownloadShareSvg(shareFormat)}
- title="Download SVG"
- >
-
- SVG
-
-
-
- {/* Copy Section */}
-
-
- {copied ? : }
- Text
-
-
- {copiedLink ? : }
- Link
-
-
-
- {/* Social Share Section */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Native Share Button */}
- {supportsNativeShare ? (
-
-
- Share
-
- ) : null}
-
-
- ) : null}
-
{history.length > 1 ? (
diff --git a/apps/web/src/app/analysis/page.tsx b/apps/web/src/app/analysis/page.tsx
index 182f029..e187b01 100644
--- a/apps/web/src/app/analysis/page.tsx
+++ b/apps/web/src/app/analysis/page.tsx
@@ -102,13 +102,13 @@ export default async function AnalysisIndexPage() {
- Vibed Repos
+ Repo VCPs
- Your Vibed Repos
+ Your Repo VCPs
- Each vibed repo reveals your coding persona, confidence level, and the evidence behind it.
+ Each Repo VCP reveals your coding persona, confidence level, and the evidence behind it.
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 794343b..de0eb18 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -21,24 +21,24 @@ const geistMono = Geist_Mono({
const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:8108";
export const metadata: Metadata = {
- title: "Vibed Coding · Your AI coding profile",
+ title: "Vibe Coding Profile · Your AI coding profile",
description:
- "Turn your commit history into a Vibed Coding Persona — patterns, insights, and shareable profiles grounded in commit evidence.",
+ "Turn your commit history into a Vibe Coding Profile (VCP) — patterns, insights, and shareable profiles grounded in commit evidence.",
metadataBase: new URL(appUrl),
openGraph: {
- title: "Vibed Coding · Your AI coding profile",
+ title: "Vibe Coding Profile · Your AI coding profile",
description:
- "A shareable Vibed Coding Persona from your commit history — patterns and insights grounded in evidence.",
+ "A shareable Vibe Coding Profile (VCP) from your commit history — patterns and insights grounded in evidence.",
url: appUrl,
locale: "en_US",
type: "website",
- siteName: "Vibed Coding",
+ siteName: "Vibe Coding Profile",
},
twitter: {
card: "summary_large_image",
- title: "Vibed Coding · Your AI coding profile",
+ title: "Vibe Coding Profile · Your AI coding profile",
description:
- "A shareable Vibed Coding Persona from your commit history — patterns and insights grounded in evidence.",
+ "A shareable Vibe Coding Profile (VCP) from your commit history — patterns and insights grounded in evidence.",
},
};
diff --git a/apps/web/src/app/login/page.tsx b/apps/web/src/app/login/page.tsx
index f39b206..cbe9fc0 100644
--- a/apps/web/src/app/login/page.tsx
+++ b/apps/web/src/app/login/page.tsx
@@ -7,7 +7,7 @@ export default function LoginPage() {
- Vibed Coding
+ Vibe Coding Profile
Sign in to analyze your build patterns from git history.
diff --git a/apps/web/src/app/methodology/page.tsx b/apps/web/src/app/methodology/page.tsx
index 01a1635..888cd68 100644
--- a/apps/web/src/app/methodology/page.tsx
+++ b/apps/web/src/app/methodology/page.tsx
@@ -14,7 +14,7 @@ export default async function MethodologyPage() {
How we compute your persona
- Vibed infers your Vibe Coding persona by spotting AI-assisted engineering patterns in the
+ Vibe Coding Profile infers your Vibe Coding persona by spotting AI-assisted engineering patterns in the
Git history of repos you connect.
diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx
index 4aec475..ba4d1e5 100644
--- a/apps/web/src/app/page.tsx
+++ b/apps/web/src/app/page.tsx
@@ -3,6 +3,8 @@ import { createSupabaseServerClient } from "@/lib/supabase/server";
import type { Database } from "@vibed/db";
import { wrappedTheme } from "@/lib/theme";
import { formatMatchedRule, AXIS_LEGEND } from "@/lib/format-labels";
+import { ProfileShareSection } from "@/components/share";
+import { ProfileVersionSelector } from "@/components/ProfileVersionSelector";
import { createClient } from "@supabase/supabase-js";
import {
aggregateUserProfile,
@@ -14,17 +16,48 @@ import {
} from "@vibed/core";
const heroFeatures = [
- "A Vibed profile built from vibe-coding signals in your commit history",
+ "A Vibe Coding Profile (VCP) built from AI-assisted engineering signals in your commit history",
"Persona snapshots that evolve as you add more repos",
- "Share-ready cards with playful language and honest confidence",
+ "Share-ready cards that highlight your vibe coding patterns",
"Deep dive metrics and evidence when you want receipts",
];
const timeline = [
- { title: "Connect GitHub", description: "Sign in, then pick the repos that feel like you." },
- { title: "Run a vibe check", description: "We read commit metadata and patterns (not your code)." },
- { title: "Get your Vibed read", description: "Highlights, categories, and insights into how you build." },
- { title: "See your persona", description: "A playful archetype that changes as your work evolves." },
+ {
+ title: "Fetch commit metadata",
+ description:
+ "We sample up to 300 commits across your repo's lifetime (timestamps, sizes, file paths). No code content.",
+ },
+ {
+ title: "Filter automation noise",
+ description:
+ "Bots like Dependabot or release workflows are filtered out so the signal reflects your work.",
+ },
+ {
+ title: "Compute metrics + axes",
+ description:
+ "25+ metrics roll up into six Vibe Axes (planning, guardrails, rhythm, surface area, and more).",
+ },
+ {
+ title: "Detect persona + insights",
+ description:
+ "We map your axes to a persona and generate streaks, timing, and workflow highlights.",
+ },
+ {
+ title: "Optional LLM narrative",
+ description:
+ "If you opt in, we summarize patterns using metadata only—never code or message content.",
+ },
+ {
+ title: "Create Repo VCP",
+ description:
+ "Each repo gets its own Repo VCP—a snapshot of your vibe coding style for that project.",
+ },
+ {
+ title: "Aggregate into Unified VCP",
+ description:
+ "Multiple Repo VCPs roll into your Unified VCP, weighted by commit volume across all repos.",
+ },
];
type AuthStats = {
@@ -49,6 +82,7 @@ type AuthStats = {
generatedAt: string | null;
};
userProfile?: {
+ personaId: string;
personaName: string;
personaTagline: string | null;
personaConfidence: string;
@@ -62,6 +96,13 @@ type AuthStats = {
commitCount: number;
}>;
updatedAt: string | null;
+ narrative?: {
+ headline?: string;
+ paragraphs?: string[];
+ highlights?: string[];
+ } | null;
+ llmModel?: string | null;
+ llmKeySource?: string | null;
};
};
@@ -437,7 +478,7 @@ export default async function Home({
supabase
.from("user_profiles")
.select(
- "persona_name, persona_tagline, persona_confidence, total_repos, total_commits, axes_json, repo_personas_json, updated_at, job_ids"
+ "persona_id, persona_name, persona_tagline, persona_confidence, total_repos, total_commits, axes_json, repo_personas_json, updated_at, job_ids, narrative_json, llm_model, llm_key_source"
)
.eq("user_id", user.id)
.maybeSingle(),
@@ -452,6 +493,7 @@ export default async function Home({
}>;
const maybeProfileData = userProfileResult.data as {
+ persona_id: string;
persona_name: string;
persona_tagline: string | null;
persona_confidence: string;
@@ -466,6 +508,13 @@ export default async function Home({
}>;
updated_at: string | null;
job_ids: unknown;
+ narrative_json: {
+ headline?: string;
+ paragraphs?: string[];
+ highlights?: string[];
+ } | null;
+ llm_model: string | null;
+ llm_key_source: string | null;
} | null;
const profileJobIds = Array.isArray(maybeProfileData?.job_ids)
@@ -531,13 +580,14 @@ export default async function Home({
? await supabase
.from("user_profiles")
.select(
- "persona_name, persona_tagline, persona_confidence, total_repos, total_commits, axes_json, repo_personas_json, updated_at"
+ "persona_id, persona_name, persona_tagline, persona_confidence, total_repos, total_commits, axes_json, repo_personas_json, updated_at, narrative_json, llm_model, llm_key_source"
)
.eq("user_id", user.id)
.maybeSingle()
: null;
const userProfileData = (profileIsStale ? refreshedProfileResult?.data ?? null : maybeProfileData) as {
+ persona_id: string;
persona_name: string;
persona_tagline: string | null;
persona_confidence: string;
@@ -551,6 +601,13 @@ export default async function Home({
commitCount: number;
}>;
updated_at: string | null;
+ narrative_json: {
+ headline?: string;
+ paragraphs?: string[];
+ highlights?: string[];
+ } | null;
+ llm_model: string | null;
+ llm_key_source: string | null;
} | null;
const recentDoneJobIds = recentDoneJobs.map((job) => job.id);
@@ -643,6 +700,7 @@ export default async function Home({
: undefined,
userProfile: userProfileData
? {
+ personaId: userProfileData.persona_id,
personaName: userProfileData.persona_name,
personaTagline: userProfileData.persona_tagline,
personaConfidence: userProfileData.persona_confidence,
@@ -651,6 +709,9 @@ export default async function Home({
axes: userProfileData.axes_json ?? {},
repoPersonas: userProfileData.repo_personas_json ?? [],
updatedAt: userProfileData.updated_at ?? null,
+ narrative: userProfileData.narrative_json ?? null,
+ llmModel: userProfileData.llm_model ?? null,
+ llmKeySource: userProfileData.llm_key_source ?? null,
}
: undefined,
};
@@ -743,7 +804,7 @@ function MarketingLanding() {
{
title: "Spec-Driven Architect",
description:
- "Plans thoroughly before touching code; constraints show up early and often.",
+ "Plans thoroughly before shipping changes; constraints show up early and often.",
},
{
title: "Test-First Validator",
@@ -771,13 +832,21 @@ function MarketingLanding() {
For vibe coders
- Find your Vibed coding profile {" "}
+
+ Find your Vibe Coding Profile (VCP)
+ {" "}
and the personality behind your workflow
- Vibed is a playful experiment by vibe coders who want to understand themselves
- better. We surface signals from your commit history to shine a light on how you build
- with AI. What feels like you, what feels new, and how your workflow is evolving.
+ A Vibe Coding Profile (VCP) is your AI-assisted engineering persona—revealed through patterns in your commit history. We surface signals from your git history to shine a light on
+ how you build with AI, what feels like you, what feels new, and how your workflow is
+ evolving.
+
+
+ The term “vibe coding” can be polarizing, but it captures the cultural moment around
+ AI-shaped development. We personally prefer “AI Assisted Engineering” because it’s
+ more explicit about the role of AI. This is a playful side project, so we keep it
+ light and approachable for non-technical folks.
@@ -846,7 +915,7 @@ function MarketingLanding() {
Persona previews
You may see one of these (or another persona). These are lenses on your
- vibe-coding style. Observations, not labels.
+ AI-assisted engineering (vibe-coding) style. Observations, not labels.
- Vibed Coding
+ Vibe Coding Profile
v0.1.0
@@ -994,27 +1063,6 @@ function AuthenticatedDashboard({
const axisKeys = Object.keys(axisMeta) as Array;
- const cards = [
- {
- label: "Reads captured",
- value: stats.completedJobs,
- helper: "Every run adds a chapter.",
- },
- {
- label: "Vibe shifts",
- value: shiftValue,
- helper: shiftHelper,
- },
- {
- label: "Most frequent vibe",
- value: dominantPersona ?? "Still forming",
- helper:
- recentLabels.length === 0
- ? "Complete a vibe check to see your read."
- : `Based on your last ${recentLabels.length} reads.`,
- },
- ];
-
function generateCrossRepoInsight(): string {
if (!stats.userProfile) return "Add more repos to unlock cross-repo insights.";
const repos = stats.userProfile.repoPersonas ?? [];
@@ -1050,9 +1098,51 @@ function AuthenticatedDashboard({
return `Your ${stats.userProfile.personaName.toLowerCase()} profile emerges from ${repos.length} repos and ${stats.userProfile.totalCommits.toLocaleString()} commits.`;
}
+ // Compute top axes for share card
+ const topAxes = stats.userProfile
+ ? Object.entries(stats.userProfile.axes)
+ .map(([key, val]) => ({
+ name: axisMeta[key as keyof typeof axisMeta]?.name ?? key,
+ score: val.score,
+ }))
+ .sort((a, b) => b.score - a.score)
+ .slice(0, 4)
+ : [];
+
+ // Use LLM narrative if available, otherwise fall back to deterministic insight
+ const narrativeFromLLM = stats.userProfile?.narrative;
+ const crossRepoInsight = narrativeFromLLM?.headline
+ ? narrativeFromLLM.headline
+ : generateCrossRepoInsight();
+
+ // Full narrative paragraphs (for expanded display)
+ const narrativeParagraphs = narrativeFromLLM?.paragraphs ?? [];
+ const narrativeHighlights = narrativeFromLLM?.highlights ?? [];
+ const hasLLMNarrative = Boolean(narrativeFromLLM?.headline || narrativeParagraphs.length > 0);
+
return (
-
+
+ {/* HERO: Share Card - The main action for users with a profile */}
+ {stats.userProfile ? (
+
+ ) : null}
+
+ {/* Profile History - Version selector */}
+ {stats.userProfile ? (
+
+ ) : null}
+
{/* Unified Profile Card */}
{/* Section 1: Identity */}
@@ -1061,7 +1151,7 @@ function AuthenticatedDashboard({
- Your Vibed Profile
+ Your Unified VCP
{stats.completedJobs > 0 ? (
@@ -1072,12 +1162,12 @@ function AuthenticatedDashboard({
>
Add repo
-
- View vibed repos
-
+
+ View Repo VCPs
+
>
) : (
-
- Insight
-
+
+
+ Insight
+
+ {hasLLMNarrative && stats.userProfile.llmModel ? (
+
+ AI-generated
+
+ ) : null}
+
- {generateCrossRepoInsight()}
+ {crossRepoInsight}
+ {narrativeParagraphs.length > 0 ? (
+
+ {narrativeParagraphs.map((paragraph, idx) => (
+
+ {paragraph}
+
+ ))}
+
+ ) : null}
+ {narrativeHighlights.length > 0 ? (
+
+ {narrativeHighlights.map((highlight, idx) => (
+
+ •
+ {highlight}
+
+ ))}
+
+ ) : null}
) : null}
@@ -1152,7 +1268,7 @@ function AuthenticatedDashboard({
Your Axes
- The 6 signals that define your vibe coding style
+ The 6 signals that define your AI-assisted engineering (vibe coding) style
@@ -1199,7 +1315,7 @@ function AuthenticatedDashboard({
{stats.completedJobs}
- Vibed repos
+ Repo VCPs
@@ -1361,7 +1477,7 @@ function AuthenticatedDashboard({
Add repo
- View vibed repos
+ View Repo VCPs
>
)}
diff --git a/apps/web/src/app/security/page.tsx b/apps/web/src/app/security/page.tsx
index c064c03..8acd537 100644
--- a/apps/web/src/app/security/page.tsx
+++ b/apps/web/src/app/security/page.tsx
@@ -39,7 +39,7 @@ export default function SecurityPage() {
What we do
- Vibed Coding connects to GitHub, fetches commit history, computes metrics, and stores the
+ Vibe Coding Profile connects to GitHub, fetches commit history, computes metrics, and stores the
results in Supabase Postgres. Access to rows is scoped to your authenticated account
via Supabase Row Level Security (RLS) policies for user-facing reads.
@@ -66,7 +66,7 @@ export default function SecurityPage() {
Note: when fetching commit details from GitHub, the API response can include file
- paths and diff snippets. Vibed Coding’s current worker extracts and stores counts/stats
+ paths and diff snippets. Vibe Coding Profile’s current worker extracts and stores counts/stats
(like number of files and additions/deletions), not file contents.
@@ -88,7 +88,7 @@ export default function SecurityPage() {
Recommendations
- Vibed Coding is designed for personal insight. Keep the blast radius small.
+ Vibe Coding Profile is designed for personal insight. Keep the blast radius small.
{recommendations.map((item) => (
@@ -106,7 +106,7 @@ export default function SecurityPage() {
- Vibed Coding
+ Vibe Coding Profile
Home
diff --git a/apps/web/src/app/settings/llm-keys/page.tsx b/apps/web/src/app/settings/llm-keys/page.tsx
index 2a62416..7d9571b 100644
--- a/apps/web/src/app/settings/llm-keys/page.tsx
+++ b/apps/web/src/app/settings/llm-keys/page.tsx
@@ -5,7 +5,7 @@ import { wrappedTheme } from "@/lib/theme";
import LLMKeysClient from "./LLMKeysClient";
export const metadata = {
- title: "LLM API Keys · Settings · Vibed Coding",
+ title: "LLM API Keys · Settings · Vibe Coding Profile",
description: "Manage your LLM API keys for AI-powered narrative generation",
};
diff --git a/apps/web/src/components/ProfileVersionSelector.tsx b/apps/web/src/components/ProfileVersionSelector.tsx
new file mode 100644
index 0000000..9d26ac2
--- /dev/null
+++ b/apps/web/src/components/ProfileVersionSelector.tsx
@@ -0,0 +1,165 @@
+"use client";
+
+import { useState, useEffect } from "react";
+
+interface ProfileVersion {
+ id: string;
+ version: number;
+ createdAt: string;
+ triggerJobId: string | null;
+ triggerRepoName: string | null;
+ llmModel: string | null;
+ llmKeySource: string | null;
+ persona: {
+ id: string;
+ name: string;
+ tagline?: string;
+ } | null;
+ totalCommits: number;
+ totalRepos: number;
+}
+
+interface ProfileVersionSelectorProps {
+ currentUpdatedAt: string | null;
+}
+
+export function ProfileVersionSelector({ currentUpdatedAt }: ProfileVersionSelectorProps) {
+ const [versions, setVersions] = useState
([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [expanded, setExpanded] = useState(false);
+ const [selectedVersion, setSelectedVersion] = useState(null);
+
+ useEffect(() => {
+ if (!expanded) return;
+
+ async function fetchVersions() {
+ setLoading(true);
+ setError(null);
+ try {
+ const res = await fetch("/api/profile/history", { cache: "no-store" });
+ const data = (await res.json()) as { versions?: ProfileVersion[]; error?: string };
+ if (!res.ok) {
+ setError(data.error ?? "Failed to load history");
+ return;
+ }
+ setVersions(data.versions ?? []);
+ } catch {
+ setError("Failed to load history");
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ fetchVersions();
+ }, [expanded]);
+
+ const formatDate = (dateStr: string) => {
+ const date = new Date(dateStr);
+ return date.toLocaleDateString(undefined, {
+ month: "short",
+ day: "numeric",
+ year: date.getFullYear() !== new Date().getFullYear() ? "numeric" : undefined,
+ hour: "numeric",
+ minute: "2-digit",
+ });
+ };
+
+ return (
+
+
setExpanded(!expanded)}
+ className="flex w-full items-center justify-between text-left"
+ >
+
+
+ Profile History
+
+ {currentUpdatedAt ? (
+
+ Last updated: {formatDate(currentUpdatedAt)}
+
+ ) : null}
+
+ {expanded ? "▲" : "▼"}
+
+
+ {expanded ? (
+
+ {loading ? (
+
Loading history...
+ ) : error ? (
+
{error}
+ ) : versions.length === 0 ? (
+
No history available yet.
+ ) : (
+ <>
+
+ View version:
+ {
+ const version = versions.find((v) => v.id === e.target.value);
+ setSelectedVersion(version ?? null);
+ }}
+ >
+ Current profile
+ {versions.map((v, idx) => (
+
+ v{v.version} — {formatDate(v.createdAt)} — {v.persona?.name ?? "Unknown"}
+ {idx === 0 ? " (latest)" : ""}
+
+ ))}
+
+
+
+ {selectedVersion ? (
+
+
+
+
+ {selectedVersion.persona?.name ?? "Unknown"}
+
+ {selectedVersion.persona?.tagline ? (
+
+ {selectedVersion.persona.tagline}
+
+ ) : null}
+
+ {selectedVersion.llmModel ? (
+
+ AI: {selectedVersion.llmModel}
+
+ ) : (
+
+ Non-LLM
+
+ )}
+
+
+ {selectedVersion.totalRepos} repos
+ {selectedVersion.totalCommits.toLocaleString()} commits
+ {selectedVersion.triggerRepoName ? (
+
+ Triggered by: {selectedVersion.triggerRepoName}
+
+ ) : null}
+
+
+ Version {selectedVersion.version} — {formatDate(selectedVersion.createdAt)}
+
+
+ ) : null}
+
+
+ {versions.length} version{versions.length !== 1 ? "s" : ""} saved
+
+ >
+ )}
+
+ ) : null}
+
+ );
+}
diff --git a/apps/web/src/components/share/ProfileShareSection.tsx b/apps/web/src/components/share/ProfileShareSection.tsx
new file mode 100644
index 0000000..028bc14
--- /dev/null
+++ b/apps/web/src/components/share/ProfileShareSection.tsx
@@ -0,0 +1,133 @@
+"use client";
+
+import { useMemo, useSyncExternalStore } from "react";
+import { ShareCard, ShareActions } from "./index";
+import type { ShareCardMetric, ShareImageTemplate } from "./types";
+
+// Use useSyncExternalStore to avoid SSR/CSR mismatch and comply with lint rules
+function useOrigin(): string {
+ return useSyncExternalStore(
+ () => () => {},
+ () => (typeof window !== "undefined" ? window.location.origin : ""),
+ () => ""
+ );
+}
+
+// Default persona colors for profile share cards
+const PERSONA_COLORS: Record = {
+ "prompt_sprinter": { primary: "#c026d3", accent: "#7c3aed" },
+ "guardrailed_viber": { primary: "#0891b2", accent: "#4f46e5" },
+ "spec_first_director": { primary: "#4f46e5", accent: "#7c3aed" },
+ "vertical_slice_shipper": { primary: "#7c3aed", accent: "#0ea5e9" },
+ "fix_loop_hacker": { primary: "#dc2626", accent: "#f97316" },
+ "toolsmith_viber": { primary: "#059669", accent: "#0891b2" },
+ "infra_weaver": { primary: "#475569", accent: "#0891b2" },
+ "rapid_risk_taker": { primary: "#f97316", accent: "#dc2626" },
+ "balanced_builder": { primary: "#6366f1", accent: "#8b5cf6" },
+ // Fallback for older persona IDs
+ "vibe-prototyper": { primary: "#c026d3", accent: "#7c3aed" },
+ "test-validator": { primary: "#0891b2", accent: "#4f46e5" },
+ "spec-architect": { primary: "#4f46e5", accent: "#7c3aed" },
+ "agent-orchestrator": { primary: "#7c3aed", accent: "#0ea5e9" },
+ "reflective-balancer": { primary: "#6366f1", accent: "#8b5cf6" },
+};
+
+const DEFAULT_COLORS = { primary: "#c026d3", accent: "#06b6d4" };
+
+interface ProfileShareSectionProps {
+ personaName: string;
+ personaId?: string;
+ personaTagline: string | null;
+ personaConfidence: string;
+ totalRepos: number;
+ totalCommits: number;
+ clarity: number;
+ topAxes: Array<{
+ name: string;
+ score: number;
+ }>;
+ insight: string;
+}
+
+export function ProfileShareSection({
+ personaName,
+ personaId,
+ personaTagline,
+ personaConfidence,
+ totalRepos,
+ totalCommits,
+ clarity,
+ topAxes,
+ insight,
+}: ProfileShareSectionProps) {
+ const shareOrigin = useOrigin();
+
+ const colors = PERSONA_COLORS[personaId ?? ""] ?? DEFAULT_COLORS;
+
+ const shareCardMetrics: ShareCardMetric[] = useMemo(() => {
+ const metrics: ShareCardMetric[] = [
+ { label: "Repos", value: String(totalRepos) },
+ { label: "Commits", value: totalCommits.toLocaleString() },
+ { label: "Clarity", value: `${clarity}%` },
+ ];
+ // Add top axis if available
+ if (topAxes.length > 0) {
+ metrics.push({ label: topAxes[0].name, value: String(topAxes[0].score) });
+ }
+ return metrics;
+ }, [totalRepos, totalCommits, clarity, topAxes]);
+
+ const shareUrl = useMemo(() => {
+ if (!shareOrigin) return "";
+ return shareOrigin; // Profile is at root
+ }, [shareOrigin]);
+
+ const shareText = useMemo(() => {
+ const metricsLine = `${totalRepos} repos · ${totalCommits.toLocaleString()} commits · ${clarity}% clarity`;
+ return `My Unified VCP: ${personaName}\n${personaTagline ?? ""}\n${metricsLine}\n#VCP`;
+ }, [personaName, personaTagline, totalRepos, totalCommits, clarity]);
+
+ const shareCaption = useMemo(() => {
+ return `My Unified VCP: ${personaName} — ${personaTagline ?? ""}. ${totalRepos} repos, ${totalCommits.toLocaleString()} commits. #VCP`;
+ }, [personaName, personaTagline, totalRepos, totalCommits]);
+
+ const shareImageTemplate: ShareImageTemplate = useMemo(() => {
+ return {
+ colors,
+ headline: `Unified VCP: ${personaName}`,
+ subhead: personaTagline ?? `${personaConfidence} confidence`,
+ metrics: shareCardMetrics,
+ persona_archetype: {
+ label: personaName,
+ archetypes: topAxes.slice(0, 3).map((a) => `${a.name}: ${a.score}`),
+ },
+ };
+ }, [personaName, personaTagline, personaConfidence, colors, shareCardMetrics, topAxes]);
+
+ return (
+
+
+
+
+ );
+}
diff --git a/apps/web/src/components/share/ShareActions.tsx b/apps/web/src/components/share/ShareActions.tsx
new file mode 100644
index 0000000..9455783
--- /dev/null
+++ b/apps/web/src/components/share/ShareActions.tsx
@@ -0,0 +1,291 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { Copy, Check, Link2, Share2, Download } from "lucide-react";
+import { SHARE_FORMATS, downloadSharePng, downloadShareSvg } from "./share-image";
+import type { ShareActionsProps, ShareFormat } from "./types";
+
+// Brand icons (Lucide doesn't include these)
+const XIcon = ({ size = 16 }: { size?: number }) => (
+
+
+
+);
+
+const LinkedInIcon = ({ size = 16 }: { size?: number }) => (
+
+
+
+);
+
+const FacebookIcon = ({ size = 16 }: { size?: number }) => (
+
+
+
+);
+
+const RedditIcon = ({ size = 16 }: { size?: number }) => (
+
+
+
+);
+
+const WhatsAppIcon = ({ size = 16 }: { size?: number }) => (
+
+
+
+);
+
+/**
+ * ShareActions - Reusable share action bar with copy, download, and social buttons
+ */
+export function ShareActions({
+ shareUrl,
+ shareText,
+ shareCaption,
+ shareHeadline,
+ shareTemplate,
+ entityId,
+ disabled = false,
+}: ShareActionsProps) {
+ const [copied, setCopied] = useState(false);
+ const [copiedLink, setCopiedLink] = useState(false);
+ const [shareFormat, setShareFormat] = useState("og");
+ const [downloadError, setDownloadError] = useState(null);
+ const [downloading, setDownloading] = useState(false);
+ const [supportsNativeShare, setSupportsNativeShare] = useState(false);
+
+ useEffect(() => {
+ setSupportsNativeShare(typeof navigator !== "undefined" && "share" in navigator);
+ }, []);
+
+ const handleCopyText = async () => {
+ try {
+ await navigator.clipboard.writeText(shareText);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ } catch {
+ setCopied(false);
+ }
+ };
+
+ const handleCopyLink = async () => {
+ if (!shareUrl) return;
+ try {
+ await navigator.clipboard.writeText(shareUrl);
+ setCopiedLink(true);
+ setTimeout(() => setCopiedLink(false), 2000);
+ } catch {
+ setCopiedLink(false);
+ }
+ };
+
+ const openSharePopup = (url: string) => {
+ window.open(url, "_blank", "noopener,noreferrer,width=800,height=600");
+ };
+
+ const handleShareTwitter = () => {
+ if (!shareUrl) return;
+ const u = new URL("https://twitter.com/intent/tweet");
+ u.searchParams.set("text", shareCaption);
+ u.searchParams.set("url", shareUrl);
+ openSharePopup(u.toString());
+ };
+
+ const handleShareFacebook = () => {
+ if (!shareUrl) return;
+ const u = new URL("https://www.facebook.com/sharer/sharer.php");
+ u.searchParams.set("u", shareUrl);
+ if (shareCaption) u.searchParams.set("quote", shareCaption);
+ openSharePopup(u.toString());
+ };
+
+ const handleShareLinkedIn = () => {
+ if (!shareUrl) return;
+ const u = new URL("https://www.linkedin.com/sharing/share-offsite/");
+ u.searchParams.set("url", shareUrl);
+ openSharePopup(u.toString());
+ };
+
+ const handleShareReddit = () => {
+ if (!shareUrl) return;
+ const u = new URL("https://www.reddit.com/submit");
+ u.searchParams.set("url", shareUrl);
+ if (shareHeadline) u.searchParams.set("title", shareHeadline);
+ openSharePopup(u.toString());
+ };
+
+ const handleShareWhatsApp = () => {
+ if (!shareUrl) return;
+ const u = new URL("https://wa.me/");
+ u.searchParams.set("text", `${shareCaption}\n${shareUrl}`.trim());
+ openSharePopup(u.toString());
+ };
+
+ const handleNativeShare = async () => {
+ if (!shareUrl || !shareHeadline) return;
+ if (!("share" in navigator)) return;
+ try {
+ await navigator.share({
+ title: shareHeadline,
+ text: shareCaption,
+ url: shareUrl,
+ });
+ } catch {
+ // User cancelled or share failed silently
+ }
+ };
+
+ const handleDownloadPng = async () => {
+ if (!shareTemplate) return;
+ setDownloadError(null);
+ setDownloading(true);
+ try {
+ await downloadSharePng(shareTemplate, shareFormat, entityId);
+ } catch (e) {
+ setDownloadError(e instanceof Error ? e.message : "download_failed");
+ } finally {
+ setDownloading(false);
+ }
+ };
+
+ const handleDownloadSvg = () => {
+ if (!shareTemplate) return;
+ setDownloadError(null);
+ downloadShareSvg(shareTemplate, shareFormat, entityId);
+ };
+
+ const isDisabled = disabled || !shareUrl;
+
+ return (
+
+
+ {/* Download Section */}
+ {shareTemplate ? (
+
+ setShareFormat(e.target.value as ShareFormat)}
+ >
+ {Object.entries(SHARE_FORMATS).map(([key, f]) => (
+
+ {f.label}
+
+ ))}
+
+
+
+ PNG
+
+
+
+ SVG
+
+
+ ) : null}
+
+ {/* Copy Section */}
+
+
+ {copied ? : }
+ Text
+
+
+ {copiedLink ? : }
+ Link
+
+
+
+ {/* Social Share Section */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Native Share Button */}
+ {supportsNativeShare ? (
+
+
+ Share
+
+ ) : null}
+
+
+ {/* Download Error */}
+ {downloadError ? (
+
Download failed: {downloadError}
+ ) : null}
+
+ );
+}
diff --git a/apps/web/src/components/share/ShareCard.tsx b/apps/web/src/components/share/ShareCard.tsx
new file mode 100644
index 0000000..c9e38cb
--- /dev/null
+++ b/apps/web/src/components/share/ShareCard.tsx
@@ -0,0 +1,93 @@
+"use client";
+
+import Image from "next/image";
+import type { ShareCardProps } from "./types";
+
+/**
+ * ShareCard - A reusable, visually appealing share card component
+ *
+ * Used on both repo analysis and profile pages to display a shareable
+ * summary of the user's vibe coding style.
+ */
+export function ShareCard({
+ variant,
+ persona,
+ metrics,
+ footer,
+ colors,
+ avatarUrl,
+ headerLabel,
+}: ShareCardProps) {
+ const defaultHeader = variant === "profile" ? "My Unified VCP" : "My Repo VCP";
+ const header = headerLabel ?? defaultHeader;
+
+ return (
+
+
+ {/* Header */}
+
+
+
+ {header}
+
+
+ {persona.label}
+
+
+ {persona.tagline}
+
+ {persona.confidence ? (
+
+ {persona.confidence} confidence
+
+ ) : null}
+
+
+ {avatarUrl ? (
+
+ ) : (
+
+ V
+
+ )}
+
+
+
+ {/* Metrics Grid */}
+
+ {metrics.slice(0, 4).map((metric, idx) => (
+
+
+ {metric.label}
+
+
+ {metric.value}
+
+
+ ))}
+
+
+ {/* Footer */}
+
+
+ {footer.left}
+
+
+ {footer.right}
+
+
+
+
+ );
+}
diff --git a/apps/web/src/components/share/index.ts b/apps/web/src/components/share/index.ts
new file mode 100644
index 0000000..b2f0adc
--- /dev/null
+++ b/apps/web/src/components/share/index.ts
@@ -0,0 +1,18 @@
+/**
+ * Share components module
+ */
+
+export { ShareCard } from "./ShareCard";
+export { ShareActions } from "./ShareActions";
+export { ProfileShareSection } from "./ProfileShareSection";
+export { createShareSvg, downloadSharePng, downloadShareSvg, SHARE_FORMATS } from "./share-image";
+export type {
+ ShareCardProps,
+ ShareCardMetric,
+ ShareCardColors,
+ ShareCardPersona,
+ ShareActionsProps,
+ ShareImageTemplate,
+ ShareFormat,
+ ShareFormatConfig,
+} from "./types";
diff --git a/apps/web/src/components/share/share-image.ts b/apps/web/src/components/share/share-image.ts
new file mode 100644
index 0000000..48ac996
--- /dev/null
+++ b/apps/web/src/components/share/share-image.ts
@@ -0,0 +1,242 @@
+/**
+ * Share image generation utilities
+ */
+
+import type { ShareFormat, ShareFormatConfig, ShareImageTemplate } from "./types";
+
+export const SHARE_FORMATS: Record = {
+ og: {
+ label: "OpenGraph 1200×630",
+ width: 1200,
+ height: 630,
+ pad: 80,
+ headlineSize: 64,
+ subheadSize: 28,
+ metricsSize: 24,
+ metaSize: 18,
+ watermarkSize: 18,
+ cardRadius: 48,
+ },
+ square: {
+ label: "Square 1080×1080",
+ width: 1080,
+ height: 1080,
+ pad: 88,
+ headlineSize: 72,
+ subheadSize: 32,
+ metricsSize: 28,
+ metaSize: 20,
+ watermarkSize: 20,
+ cardRadius: 64,
+ },
+ story: {
+ label: "Story 1080×1920",
+ width: 1080,
+ height: 1920,
+ pad: 96,
+ headlineSize: 84,
+ subheadSize: 36,
+ metricsSize: 30,
+ metaSize: 22,
+ watermarkSize: 22,
+ cardRadius: 64,
+ },
+};
+
+function escapeXml(value: string): string {
+ return value
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+}
+
+function wrapTextLines(
+ text: string,
+ maxCharsPerLine: number,
+ maxLines: number
+): { lines: string[]; truncated: boolean } {
+ const words = text.trim().split(/\s+/).filter(Boolean);
+ const lines: string[] = [];
+ let current = "";
+
+ for (const word of words) {
+ const next = current ? `${current} ${word}` : word;
+ if (next.length <= maxCharsPerLine) {
+ current = next;
+ continue;
+ }
+
+ if (current) {
+ lines.push(current);
+ current = word;
+ if (lines.length >= maxLines) {
+ return { lines, truncated: true };
+ }
+ continue;
+ }
+
+ lines.push(word.slice(0, Math.max(1, maxCharsPerLine)));
+ if (lines.length >= maxLines) {
+ return { lines, truncated: true };
+ }
+ current = "";
+ }
+
+ if (current) lines.push(current);
+
+ if (lines.length > maxLines) {
+ return { lines: lines.slice(0, maxLines), truncated: true };
+ }
+
+ return { lines, truncated: false };
+}
+
+function withEllipsis(text: string): string {
+ const t = text.trim();
+ return t.length === 0 ? t : `${t}…`;
+}
+
+export function createShareSvg(
+ template: ShareImageTemplate,
+ format: ShareFormat
+): string {
+ const cfg = SHARE_FORMATS[format];
+ const metricsText = template.metrics
+ .slice(0, 3)
+ .map((metric) => `${metric.label}: ${metric.value}`)
+ .join(" · ");
+ const archetypes = template.persona_archetype.archetypes.slice(0, 3).join(", ");
+
+ const headlineChars = format === "og" ? 26 : format === "square" ? 22 : 18;
+ const subheadChars = format === "og" ? 46 : format === "square" ? 36 : 30;
+ const metricsChars = format === "og" ? 72 : format === "square" ? 60 : 46;
+
+ const headlineWrapped = wrapTextLines(template.headline, headlineChars, 2);
+ const subheadWrapped = wrapTextLines(template.subhead, subheadChars, 2);
+ const metricsWrapped = wrapTextLines(metricsText, metricsChars, format === "story" ? 2 : 1);
+
+ const headlineLines = headlineWrapped.lines.map((l, idx) =>
+ escapeXml(idx === headlineWrapped.lines.length - 1 && headlineWrapped.truncated ? withEllipsis(l) : l)
+ );
+ const subheadLines = subheadWrapped.lines.map((l, idx) =>
+ escapeXml(idx === subheadWrapped.lines.length - 1 && subheadWrapped.truncated ? withEllipsis(l) : l)
+ );
+ const metricsLines = metricsWrapped.lines.map((l, idx) =>
+ escapeXml(idx === metricsWrapped.lines.length - 1 && metricsWrapped.truncated ? withEllipsis(l) : l)
+ );
+
+ const startY =
+ format === "story" ? Math.round(cfg.height * 0.34) : cfg.pad + Math.round(cfg.headlineSize * 0.9);
+
+ const headlineY = startY;
+ const subheadY = headlineY + Math.round(cfg.headlineSize * 0.95) + 18;
+ const metricsY = subheadY + Math.round(cfg.subheadSize * 0.95) + 18;
+ const metaY =
+ format === "story" ? Math.round(cfg.height * 0.72) : metricsY + Math.round(cfg.metricsSize * 0.95) + 28;
+ const hashY = metaY + Math.round(cfg.metaSize * 1.6);
+ const watermarkY = cfg.height - Math.round(cfg.pad * 0.6);
+
+ const x = cfg.pad;
+ const watermarkX = cfg.width - cfg.pad;
+
+ const headlineTspans = headlineLines
+ .map((line, idx) => `${line} `)
+ .join("");
+ const subheadTspans = subheadLines
+ .map((line, idx) => `${line} `)
+ .join("");
+ const metricsTspans = metricsLines
+ .map((line, idx) => `${line} `)
+ .join("");
+
+ return `
+
+
+
+
+
+
+
+
+
+ ${headlineTspans}
+
+
+ ${subheadTspans}
+
+
+ ${metricsTspans}
+
+
+ Persona: ${escapeXml(template.persona_archetype.label)} · ${escapeXml(archetypes)}
+
+
+ #VCP
+
+
+ vibe.coding.profile
+
+ `;
+}
+
+export function downloadBlob(blob: Blob, filename: string): void {
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = filename;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+}
+
+export async function downloadSharePng(
+ template: ShareImageTemplate,
+ format: ShareFormat,
+ entityId: string
+): Promise {
+ const svg = createShareSvg(template, format);
+ const cfg = SHARE_FORMATS[format];
+
+ const svgBlob = new Blob([svg], { type: "image/svg+xml;charset=utf-8" });
+ const svgUrl = URL.createObjectURL(svgBlob);
+
+ try {
+ const img = new Image();
+ const loaded = new Promise((resolve, reject) => {
+ img.onload = () => resolve();
+ img.onerror = () => reject(new Error("image_load_failed"));
+ });
+ img.src = svgUrl;
+ await loaded;
+
+ const scale = 2;
+ const canvas = document.createElement("canvas");
+ canvas.width = cfg.width * scale;
+ canvas.height = cfg.height * scale;
+ const ctx = canvas.getContext("2d");
+ if (!ctx) throw new Error("canvas_unsupported");
+ ctx.setTransform(scale, 0, 0, scale, 0, 0);
+ ctx.drawImage(img, 0, 0, cfg.width, cfg.height);
+
+ const pngBlob = await new Promise((resolve, reject) => {
+ canvas.toBlob((b) => (b ? resolve(b) : reject(new Error("png_encode_failed"))), "image/png");
+ });
+
+ downloadBlob(pngBlob, `vcp-${entityId}-${format}.png`);
+ } finally {
+ URL.revokeObjectURL(svgUrl);
+ }
+}
+
+export function downloadShareSvg(
+ template: ShareImageTemplate,
+ format: ShareFormat,
+ entityId: string
+): void {
+ const svg = createShareSvg(template, format);
+ const blob = new Blob([svg], { type: "image/svg+xml;charset=utf-8" });
+ downloadBlob(blob, `vcp-${entityId}-${format}.svg`);
+}
diff --git a/apps/web/src/components/share/types.ts b/apps/web/src/components/share/types.ts
new file mode 100644
index 0000000..df930c6
--- /dev/null
+++ b/apps/web/src/components/share/types.ts
@@ -0,0 +1,83 @@
+/**
+ * Share component types and interfaces
+ */
+
+export interface ShareCardMetric {
+ label: string;
+ value: string;
+}
+
+export interface ShareCardColors {
+ primary: string;
+ accent: string;
+}
+
+export interface ShareCardPersona {
+ label: string;
+ tagline: string;
+ confidence: string;
+ archetypes?: string[];
+}
+
+export interface ShareCardProps {
+ /** Variant determines layout differences */
+ variant: "repo" | "profile";
+ /** Persona information */
+ persona: ShareCardPersona;
+ /** Up to 4 metrics to display */
+ metrics: ShareCardMetric[];
+ /** Footer content */
+ footer: {
+ left: string;
+ right: string;
+ };
+ /** Gradient colors */
+ colors: ShareCardColors;
+ /** Optional avatar URL */
+ avatarUrl?: string | null;
+ /** Optional header label override */
+ headerLabel?: string;
+}
+
+export interface ShareActionsProps {
+ /** URL to share */
+ shareUrl: string;
+ /** Text content for copy/share */
+ shareText: string;
+ /** Caption for social platforms */
+ shareCaption: string;
+ /** Headline for native share */
+ shareHeadline: string;
+ /** Share template for image generation */
+ shareTemplate: ShareImageTemplate | null;
+ /** Job ID or profile ID for filename */
+ entityId: string;
+ /** Whether share is disabled */
+ disabled?: boolean;
+}
+
+export interface ShareImageTemplate {
+ colors: ShareCardColors;
+ headline: string;
+ subhead: string;
+ metrics: ShareCardMetric[];
+ persona_archetype: {
+ label: string;
+ archetypes: string[];
+ };
+}
+
+export type ShareFormat = "og" | "square" | "story";
+
+export interface ShareFormatConfig {
+ label: string;
+ width: number;
+ height: number;
+ pad: number;
+ headlineSize: number;
+ subheadSize: number;
+ metricsSize: number;
+ metaSize: number;
+ watermarkSize: number;
+ cardRadius: number;
+}
diff --git a/docs/PRD-profile-aggregation.md b/docs/PRD-profile-aggregation.md
index 4862495..fad8298 100644
--- a/docs/PRD-profile-aggregation.md
+++ b/docs/PRD-profile-aggregation.md
@@ -1,4 +1,9 @@
-# PRD: Profile Aggregation — One Persona Per User
+# PRD: Profile Aggregation — Unified VCP Per User
+
+**Status:** Core Implementation Complete — See `docs/implementation-trackers/profile-aggregation.md` for details
+
+> **Reference Documentation:** For technical details on aggregation, see:
+> - [Vibe Coding Profile Analysis Pipeline](./architecture/vibed-analysis-pipeline.md#profile-aggregation) — Aggregation algorithm details
## The Problem
@@ -8,7 +13,7 @@ We built personas per repo. The PRDs say personas should aggregate per user.
From `PRD-vibed-ux.md`:
-> "Vibed Coding builds a profile of your AI-era coding style **across projects**. One repo gives you a snapshot. Three repos show patterns. Five repos reveal who you really are when you're building with AI tools."
+> "Vibe Coding Profile builds a profile of your AI-era coding style **across projects**. One repo gives you a Repo VCP. Three repos show patterns. Five repos reveal your Unified VCP when you're building with AI tools."
> "**One project = one data point.** We can tell you what we see, but we can't know if that's 'you' or just 'this project.' **Multiple projects = actual patterns.** Now we can say with confidence: 'This is how you work, not just how this codebase works.'"
@@ -37,7 +42,7 @@ The PRD even mocked the target:
1. **Accuracy**: One repo might be a hackathon, another is work. Combined = the real you.
2. **Engagement**: "Add more repos to sharpen your profile" is the natural growth loop.
-3. **Value**: Cross-repo insights are unique to Vibed — no one else does this.
+3. **Value**: Cross-repo insights are unique to Vibe Coding Profile — no one else does this.
---
@@ -343,7 +348,7 @@ The user's aggregated profile page (the "home base"):
```
┌─────────────────────────────────────────────────────────────────┐
-│ YOUR VIBED PROFILE │
+│ YOUR UNIFIED VCP │
│ Based on 3 repos · 847 commits │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
diff --git a/docs/PRD-vibed-ux.md b/docs/PRD-vibed-ux.md
index 73a33b8..c4ccc8e 100644
--- a/docs/PRD-vibed-ux.md
+++ b/docs/PRD-vibed-ux.md
@@ -1,4 +1,6 @@
-# PRD: Vibed UX — The Pop Version of Developer Analytics
+# PRD: Vibe Coding Profile UX — The Pop Version of Developer Analytics
+
+**Status:** Partially Implemented — Core persona detection and profile pages are live. Share cards and some UX polish items are in progress.
> "Run it once, discover something surprising about yourself, share it with a friend."
@@ -18,7 +20,7 @@
| Error codes | Friendly "we hit a snag" messages |
| Developer-speak everywhere | Warm, curious, personality-quiz energy |
-**The real value:** Vibed Coding builds a profile of your AI-era coding style across projects. One repo gives you a snapshot. Three repos show patterns. Five repos reveal who you really are when you're building with AI tools.
+**The real value:** Vibe Coding Profile builds a profile of your AI-era coding style across projects. One repo gives you a snapshot (Repo VCP). Three repos show patterns. Five repos reveal your Unified VCP when you're building with AI tools.
**The goal:** A developer connects their GitHub, picks a repo, waits 30 seconds, gets a surprising insight, and thinks "I wonder what I'd learn if I added more projects..."
@@ -28,7 +30,7 @@
### The Core Insight
-Vibed Coding isn't about analyzing repos — it's about **understanding how you work with AI tools**. Every commit tells a story about your relationship with AI-assisted coding:
+Vibe Coding Profile isn't about analyzing repos — it's about **understanding how you work with AI tools**. Every commit tells a story about your relationship with AI-assisted coding:
- Do you vibe-code in bursts and clean up later?
- Do you spec things out before letting AI generate?
@@ -91,7 +93,7 @@ try adding a different kind of project."
### The Profile Page (New Concept)
-Beyond individual repo vibes, users should have an **aggregate profile**:
+Beyond individual Repo VCPs, users should have a **Unified VCP**:
```
┌─────────────────────────────────────────────────────────────┐
@@ -158,11 +160,11 @@ Important: We're not asking for access to everything. The model is:
## 0. Page Inventory & Transformation Map
-Every page in the application must reflect the Vibed personality. Here's the complete transformation:
+Every page in the application must reflect the Vibe Coding Profile personality. Here's the complete transformation:
-| Current Page | Current Feel | Vibed Feel | Key Changes |
+| Current Page | Current Feel | Vibe Coding Profile Feel | Key Changes |
|--------------|--------------|------------|-------------|
-| `/` (Landing) | Technical explainer | Personality quiz invitation | Lead with "What's your vibe?" not "What does Vibed do?" |
+| `/` (Landing) | Technical explainer | Personality quiz invitation | Lead with "What's your vibe?" not "What does Vibe Coding Profile do?" |
| `/` (Dashboard) | Stats dashboard | Profile home base | Show aggregate profile + clarity meter + next actions |
| `/login` | Standard OAuth | Trust-building moment | Emphasize "peek at commits" language, inline security |
| `/repos` | CRUD list | "Build your profile" | Visual commit density, profile clarity progress |
@@ -181,7 +183,7 @@ Every page in the application must reflect the Vibed personality. Here's the com
## 1. The Vision
-Vibed Coding should feel like taking a personality quiz that actually knows you — except it's based on real data from your commits. Think BuzzFeed meets Spotify Wrapped for developers. The user should laugh, nod in recognition, screenshot something, and text it to a coworker within 5 minutes of their first analysis.
+Vibe Coding Profile should feel like taking a personality quiz that actually knows you, except it's based on real data from your commits. Think BuzzFeed meets Spotify Wrapped for vibe coders. The user should laugh, nod in recognition, screenshot something, and text it to a coworker within 5 minutes of their first analysis.
**What we're NOT building:**
- A performance review tool
@@ -231,9 +233,9 @@ The entire first-time experience should take under 5 minutes from landing to sha
### 3.1 First Contact (Landing Page) — `/`
-**Current state:** Technical explanation of what Vibed does, developer-focused language.
+**Current state:** Technical explanation of what Vibe Coding Profile does, developer-focused language.
-**Vibed state:**
+**Vibe Coding Profile state:**
```
[Hero Section]
@@ -261,7 +263,7 @@ what kind of builder you are — with receipts.
**Current state:** Standard "Sign in with GitHub" flow
-**Vibed state:**
+**Vibe Coding Profile state:**
```
[Minimal screen]
@@ -288,7 +290,7 @@ file counts. Never your actual code.
**Current state:** List of repos with Connect/Analyze buttons
-**Vibed state:**
+**Vibe Coding Profile state:**
```
[Header]
@@ -326,7 +328,7 @@ the clearer your vibe comes through.
**Current state:** Technical job status with progress indicators
-**Vibed state:**
+**Vibe Coding Profile state:**
```
[Animated illustration of commits being "read"]
@@ -446,7 +448,7 @@ Share your coding vibe
│ │
│ THE VIBE PROTOTYPER │
│ 23-day streak · Friday 4-7pm zone │
-│ vibed.coding │
+│ vibecoding.profile │
└─────────────────────────────────────────┘
[Download image] [Copy link]
@@ -462,7 +464,7 @@ Share your coding vibe
Pre-written caption:
"Just discovered I'm a Vibe Prototyper — I build to
think. What's your coding personality?
-vibed.coding #CodingVibe"
+vibecoding.profile #VCP"
```
**Share asset requirements (v1):**
@@ -475,7 +477,7 @@ vibed.coding #CodingVibe"
- Headline must wrap to max 2 lines with ellipsis if needed.
- Subhead must wrap to max 2 lines with ellipsis if needed.
- Metrics display must remain readable when scaled down to 50% (mobile feed).
-- Include a subtle watermark (e.g. `vibed.coding`) that survives cropping.
+- Include a subtle watermark (e.g. `vibecoding.profile`) that survives cropping.
- Never include raw commit SHAs, file paths, or repo names in share assets by default.
**Share copy requirements (v1):**
@@ -495,7 +497,7 @@ vibed.coding #CodingVibe"
**Current state:** Analysis list with technical job details
-**Vibed state:**
+**Vibe Coding Profile state:**
```
[Header]
@@ -535,7 +537,7 @@ Something changed."
**Current state:** Stats cards showing "Connected repos: 3", "Finished analyses: 5", "Queued or running: 1" with technical job status
-**Vibed state — Profile-centric:**
+**Vibe Coding Profile state — Profile-centric:**
```
[Header — warm, personal greeting]
@@ -601,13 +603,13 @@ Something changed."
**Current state:** Dense policy text
-**Vibed state:**
+**Vibe Coding Profile state:**
```
[Header]
"Your code stays yours"
-We built Vibed to be paranoid about privacy.
+We built Vibe Coding Profile to be paranoid about privacy.
Here's exactly what we access and what we don't.
[Visual trust checklist]
@@ -635,7 +637,7 @@ Here's exactly what we access and what we don't.
▸ How do I delete my data?
[Footer]
-Questions? security@vibed.coding
+Questions? security@vibecoding.profile
```
### 3.10 First-Time Onboarding (Inline, Not Separate Wizard)
@@ -675,7 +677,7 @@ we found. It can change as your style evolves!"
**Current:** "Error: Job failed with status code 500"
-**Vibed:**
+**Vibe Coding Profile:**
```
[Friendly error card]
@@ -687,7 +689,7 @@ we found. It can change as your style evolves!"
│ │
│ [Try again] [Pick different project] │
│ │
-│ Still stuck? Let us know: help@vibed.coding │
+│ Still stuck? Let us know: help@vibecoding.profile │
└─────────────────────────────────────────────────────┘
```
@@ -754,13 +756,13 @@ This one has 23 — check back when you've shipped more!
**Technical (old):**
> "Analysis complete. 847 commits processed. Persona classification: Iterative Prototyper with 78% confidence based on commit frequency and file scope metrics."
-**Vibed (new):**
+**Vibe Coding Profile (new):**
> "We read 847 commits and the vibe is clear — you're a Prototyper. You build to think, and code is your sketchpad. We're 78% sure about this, based on how often you ship and how focused your changes are."
**Technical (old):**
> "Your average inter-commit time is 4.2 hours with peak activity between 16:00-19:00 on Fridays."
-**Vibed (new):**
+**Vibe Coding Profile (new):**
> "Friday afternoon is your creative playground. When 4pm hits, something clicks and the commits start flowing."
### 4.3 Persona Voice Examples
@@ -807,7 +809,7 @@ Each persona has a tagline and a "vibe description":
## 6. Mobile Experience
-The entire Vibed experience should work on mobile because:
+The entire Vibe Coding Profile experience should work on mobile because:
1. People share from their phones
2. Someone might get a link and open it on mobile
3. The "show a coworker" moment often happens in person with a phone
@@ -843,7 +845,7 @@ The entire Vibed experience should work on mobile because:
/analysis/550e8400-e29b-41d4-a716-446655440000
```
-**Vibed (friendly):**
+**Vibe Coding Profile (friendly):**
```
/vibe/acme-dashboard-jan-2026
```
@@ -875,7 +877,7 @@ Keep old `/analysis/[jobId]` routes working via redirect:
Share links should be short and memorable:
```
-vibed.coding/v/abc123
+vibecoding.profile/v/abc123
```
Short code resolves to full vibe page with optional "shared view" mode (read-only, no auth required if owner enabled sharing).
@@ -886,7 +888,7 @@ Short code resolves to full vibe page with optional "shared view" mode (read-onl
### Phase 1: Foundation & Flow (Week 1-2)
-**Goal:** Complete first-time user flow works end-to-end with Vibed UX
+**Goal:** Complete first-time user flow works end-to-end with Vibe Coding Profile UX
| Page | Priority | Changes |
|------|----------|---------|
diff --git a/docs/PRD-vibed.md b/docs/PRD-vibed.md
index 5d2c091..33182f3 100644
--- a/docs/PRD-vibed.md
+++ b/docs/PRD-vibed.md
@@ -1,22 +1,40 @@
-
+
-# PRD: Vibed Narrative Layer
+# PRD: Vibe Coding Profile Narrative Layer
+
+**Status:** Mostly Implemented — See `docs/implementation-trackers/vibed.md` for details
+
+> **Reference Documentation:** For technical details on how analysis works, see:
+> - [How Vibe Coding Profile Works](./how-vibed-works.md) — Product-friendly overview
+> - [Vibe Coding Profile Analysis Pipeline](./architecture/vibed-analysis-pipeline.md) — Technical architecture
## 1. Context & Vision
-Bolokono now surfaces “vibe coding profiles” through the Vibed experience: a playful, reflective interpretation of commit history modeled on Spotify Wrapped. The goal is to make craftsmanship visible—narrative cards first, data second—while preserving trust through evidence-backed insights. This PRD defines the UX, UI, API, database, and worker investments required to deliver deterministic insights, persona-aware storytelling, and share-ready outputs.
+Bolokono now surfaces “vibe coding profiles” through the Vibe Coding Profile experience: a playful, reflective interpretation of commit history modeled on Spotify Wrapped. The goal is to make craftsmanship visible—narrative cards first, data second—while preserving trust through evidence-backed insights. This PRD defines the UX, UI, API, database, and worker investments required to deliver deterministic insights, persona-aware storytelling, and share-ready outputs.
## 2. Product Goals
-1. **Craftsmanship-first narrative**: Provide a high-level Vibed layer that highlights streaks, preferred rhythms, chunkiness, persona clues, and confidence-fueled observations.
+1. **Craftsmanship-first narrative**: Provide a high-level Vibe Coding Profile layer that highlights streaks, preferred rhythms, chunkiness, persona clues, and confidence-fueled observations.
2. **Deterministic insights**: Move Wrapped computation server-side so the client only renders consistent cards regardless of locale or UI refactors.
3. **Share & reflect**: Enable share cards (PNG/SVG) and copy-ready summaries tied to the same deterministic data.
4. **Evidence & depth**: Keep a deep-dive layer with raw metrics, commit samples, evidence SHAs, and persona signal explanations for power users.
5. **Persona-aware confidence**: Surface persona tags + confidence metadata so insights stay observational (“You tend to…”, “Often, etc.”).
-## 3. Supporting Research Summary
+## 3. Supporting Research & Attribution
+
+### External Sources
+- **"Vibe coding"**: Term coined by [Andrej Karpathy](https://x.com/karpathy/status/1886192184808149383) (February 2025), [Collins Dictionary Word of the Year 2025](https://en.wikipedia.org/wiki/Vibe_coding).
+- **Orchestrator vs Conductor patterns**: [Addy Osmani's agentic coding research](https://addyosmani.com/blog/future-agentic-coding/).
+- **Code analytics**: [GitClear developer productivity studies](https://www.gitclear.com/).
+- **Copilot workflows**: [GitHub Copilot documentation](https://docs.github.com/en/copilot).
+
+### Internal Research
+- [AI-Era Coding Personas](./research/ai-era-coding-personas.md)
+- [AI-Era Developer Personas (ChatGPT synthesis)](./research/ai-era-developer-personas-chatgpt.md)
+- [Persona Insight Synthesis](./research/persona-insight-synthesis.md)
-Sources: `docs/research/ai-era-coding-personas.md`, `docs/research/ai-era-developer-personas-chatgpt.md`, `docs/research/persona-insight-synthesis.md`. Personas to detect: Spec-Driven Architect / Cautious Traditionalist, Test-First Validator / Pragmatic Augmenter, Iterative Prototyper / Vibe Coder, Multi-Agent Orchestrator / Autonomous AI Orchestrator, Specialist Consultant, Infrastructure Architect, Hands-On Debugger / Rapid Risk-Taker. Insights should mention craft behavior, guardrails, and risk tolerance.
+### Personas Detected
+Spec-Driven Architect, Test-First Validator, Vibe Prototyper, Agent Orchestrator, Hands-On Debugger, Rapid Risk-Taker, Reflective Balancer. Insights emphasize craft behavior, guardrails, and risk tolerance.
## 4. Metrics & Signals to Capture
@@ -38,11 +56,11 @@ Sources: `docs/research/ai-era-coding-personas.md`, `docs/research/ai-era-develo
## 5. Experience Requirements
-### Vibed Highlights layer (UX)
+### Vibe Coding Profile Highlights layer (UX)
- Hero cards: “Your longest coding streak”, “Friday evenings are your sweet spot”, “Fixes are your most common commits”, “You deliver X features for every fix”, “Chunky commit average” etc.
- Each card shows persona-aligned service message + confidence level + evidence reference metadata (metric + sample SHA).
- Provide filters for time range (Last 30 days, 90 days, Year, All time).
-- Share section: generate share cards (PNG/SVG) plus copyable summary text; underlying data identical to vibed cards; include persona badge/confidence.
+- Share section: generate share cards (PNG/SVG) plus copyable summary text; underlying data identical to VCP cards; include persona badge/confidence.
- Embed “Show me the data” toggle to reveal deep dive table (timeline, metrics, evidence, persona signals).
### Deep Dive layer
@@ -87,7 +105,7 @@ Sources: `docs/research/ai-era-coding-personas.md`, `docs/research/ai-era-develo
1. Finalize insights schema and persona classifier (per `persona-insight-synthesis` document).
2. Extend worker to compute insights, persist to new table, and feed narrative generation.
3. Update API routes + Supabase RLS to expose insights and share payloads.
-4. Build Vibed UI (high-level cards, share layer, toggles for data). Ensure persona confidence badges/wording.
+4. Build Vibe Coding Profile UI (high-level cards, share layer, toggles for data). Ensure persona confidence badges/wording.
5. Expand documentation (research, persona map) and implement tests for deterministic logic + share card assets.
6. Optional: add share card export job (server side) or pre-render from reusable component.
diff --git a/docs/PRD.md b/docs/PRD.md
index c97dfdc..be1a2fd 100644
--- a/docs/PRD.md
+++ b/docs/PRD.md
@@ -1,10 +1,16 @@
-# PRD: Bolokono
+# PRD: Bolokono (Vibe Coding Profile)
-**Product name:** Bolokono
+**Product name:** Bolokono (publicly branded as **Vibe Coding Profile**)
**Author:** Abou Kone
-**Status:** Draft v4 (Monorepo architecture)
+**Status:** Draft v4 (Monorepo architecture) — Core features implemented
**Audience:** Product, Engineering (human + AI agents)
+> **Terminology Note:** This PRD uses the original "Bolokono" terminology. In the codebase and user-facing product, this has been rebranded to **Vibe Coding Profile**. "Bolokono types" are now called **Vibe Personas**, and "Bolokono profiles" are now **Vibe Coding Profiles (VCP)**. See `docs/PRD-vibed-ux.md` for the current UX direction.
+
+> **Reference Documentation:** For a complete explanation of how analysis works, see:
+> - [How Vibe Coding Profile Works](./how-vibed-works.md) — Product-friendly overview
+> - [Vibe Coding Profile Analysis Pipeline](./architecture/vibed-analysis-pipeline.md) — Technical architecture with diagrams
+
---
## 1. Product Story
@@ -153,22 +159,22 @@ They summarize computed facts and cite evidence.
**Includes:**
- [x] Authentication (GitHub OAuth via Supabase)
-- [ ] GitHub repository listing and selection
-- [ ] Database schema with RLS
-- [ ] Repository sync (metadata only)
-- [ ] Analysis job queue and state machine
-- [ ] Edge Function analyzer (commit metadata extraction)
-- [ ] Metrics computation and persistence
-- [ ] Minimal Bolokono profile generation
-- [ ] Basic report UI
+- [x] GitHub repository listing and selection
+- [x] Database schema with RLS
+- [x] Repository sync (metadata only)
+- [x] Analysis job queue and state machine
+- [x] Inngest/Worker analyzer (commit metadata extraction)
+- [x] Metrics computation and persistence
+- [x] Minimal Vibe profile generation
+- [x] Basic report UI
-**Deliverable:**
+**Deliverable:** ✓
- One private repo analyzed end-to-end
-- One Bolokono profile rendered with:
+- One Vibe profile rendered with:
- At least 5 computed metrics
- Build category timeline
- - One assigned Bolokono type with evidence
+ - One assigned Vibe persona with evidence
**Exit criteria:**
@@ -222,7 +228,7 @@ vibed-coding/
│
├── supabase/
│ ├── migrations/ # SQL migrations
-│ ├── functions/ # Edge Functions (optional)
+│ ├── functions/ # Edge Functions (legacy, see inngest-integration.md)
│ └── config.toml
│
└── docs/
@@ -1372,91 +1378,91 @@ Each task depends on the previous.
**Output:** Monorepo structure with apps and packages.
-#### Task 0.2: Supabase Setup
+#### Task 0.2: Supabase Setup ✓
- [x] Create Supabase project (production)
- [x] Set up local development with Supabase CLI
-- [ ] Create initial migration with full schema
-- [ ] Implement RLS policies
-- [ ] Create `claim_analysis_job` function
-- [ ] Test RLS policies with different user contexts
+- [x] Create initial migration with full schema
+- [x] Implement RLS policies
+- [x] Create `claim_analysis_job` function
+- [x] Test RLS policies with different user contexts
**Output:** Database with schema, RLS, and job claiming function.
-#### Task 0.3: Authentication
+#### Task 0.3: Authentication ✓
-- [ ] Configure GitHub OAuth provider in Supabase
-- [ ] Create GitHub OAuth app (for additional scopes)
-- [ ] Implement Supabase Auth in `apps/web`
-- [ ] Create sign-in page
-- [ ] Create auth callback handler
-- [ ] Store GitHub token in `github_accounts` (encrypted)
-- [ ] Create protected route middleware
+- [x] Configure GitHub OAuth provider in Supabase
+- [x] Create GitHub OAuth app (for additional scopes)
+- [x] Implement Supabase Auth in `apps/web`
+- [x] Create sign-in page
+- [x] Create auth callback handler
+- [x] Store GitHub token in `github_accounts` (encrypted)
+- [x] Create protected route middleware
**Output:** User can sign in with GitHub, token stored securely.
-#### Task 0.4: Repository Sync
+#### Task 0.4: Repository Sync ✓
-- [ ] Create GitHub API client in `apps/web`
-- [ ] Implement `POST /api/github/sync-repos` endpoint
-- [ ] Create repo selection UI
-- [ ] Implement repo connection flow (`user_repos`)
-- [ ] Implement repo disconnection with data cleanup
+- [x] Create GitHub API client in `apps/web`
+- [x] Implement `POST /api/github/sync-repos` endpoint
+- [x] Create repo selection UI
+- [x] Implement repo connection flow (`user_repos`)
+- [x] Implement repo disconnection with data cleanup
**Output:** User can see their repos and connect/disconnect them.
-#### Task 0.5: Analysis Job System
+#### Task 0.5: Analysis Job System ✓
-- [ ] Create `POST /api/analysis/start` endpoint
-- [ ] Set up Supabase Realtime subscription for job status
-- [ ] Create job status UI component with live updates
-- [ ] Implement basic worker loop in `apps/worker`
-- [ ] Test job claiming with `FOR UPDATE SKIP LOCKED`
+- [x] Create `POST /api/analysis/start` endpoint
+- [x] Set up Supabase Realtime subscription for job status
+- [x] Create job status UI component with live updates
+- [x] Implement Inngest-based job processing (primary) and worker fallback
+- [x] Test job claiming with `FOR UPDATE SKIP LOCKED`
**Output:** Jobs can be queued and status tracked in real-time.
-#### Task 0.6: Commit Analysis (Worker)
+#### Task 0.6: Commit Analysis (Worker) ✓
-- [ ] Implement GitHub API client in `apps/worker`
-- [ ] Implement commit fetching (paginated, batched)
-- [ ] Implement commit classification (`packages/core`)
-- [ ] Implement metrics computation (`packages/core`)
-- [ ] Store metrics in `analysis_metrics`
-- [ ] Handle rate limits gracefully
-- [ ] Handle large repos (time-distributed sampling + notice)
+- [x] Implement GitHub API client in `apps/worker`
+- [x] Implement commit fetching (paginated, batched)
+- [x] Implement commit classification (`packages/core`)
+- [x] Implement metrics computation (`packages/core`)
+- [x] Store metrics in `analysis_metrics`
+- [x] Handle rate limits gracefully
+- [x] Handle large repos (time-distributed sampling + notice)
**Output:** Worker fetches commits and computes metrics.
-#### Task 0.7: Report Generation (Worker)
+#### Task 0.7: Report Generation (Worker) ✓
-- [ ] Implement Bolokono type matching (`packages/core`)
-- [ ] Create Claude API client in `apps/worker`
-- [ ] Implement narrative generation with structured output
-- [ ] Implement narrative validation (banned phrases, SHA checks)
-- [ ] Store reports in `analysis_reports`
-- [ ] Handle generation failures gracefully
+- [x] Implement Vibe persona matching (`packages/core`)
+- [x] Create multi-provider LLM client (Anthropic, OpenAI, Gemini)
+- [x] Implement narrative generation with structured output
+- [x] Implement narrative validation (banned phrases, SHA checks)
+- [x] Store reports in `analysis_reports`
+- [x] Handle generation failures gracefully with fallback narratives
**Output:** Worker generates and stores reports.
-#### Task 0.8: Report UI
+#### Task 0.8: Report UI ✓
-- [ ] Create report page layout in `apps/web`
-- [ ] Implement Bolokono type display with badge
-- [ ] Implement metrics cards (key stats)
-- [ ] Implement narrative sections with evidence
-- [ ] Create build timeline visualization
-- [ ] Handle loading/error/insufficient-data states
+- [x] Create report page layout in `apps/web`
+- [x] Implement Vibe persona display with badge
+- [x] Implement metrics cards (key stats)
+- [x] Implement narrative sections with evidence
+- [x] Create build timeline visualization
+- [x] Handle loading/error/insufficient-data states
-**Output:** User can view their Bolokono profile.
+**Output:** User can view their Vibe profile.
-### Phase 0 Exit Criteria
+### Phase 0 Exit Criteria ✓
-- [ ] End-to-end flow works for private repo
-- [ ] All RLS policies enforced
-- [ ] Worker processes jobs correctly
-- [ ] Analysis completes in < 60s for < 1000 commits
-- [ ] Report displays type, metrics, and narrative
-- [ ] User can disconnect repo and data is deleted
+- [x] End-to-end flow works for private repo
+- [x] All RLS policies enforced
+- [x] Worker processes jobs correctly (via Inngest + fallback)
+- [x] Analysis completes in < 60s for < 1000 commits
+- [x] Report displays persona, metrics, and narrative
+- [x] User can disconnect repo and data is deleted
---
@@ -1480,7 +1486,7 @@ Each task depends on the previous.
### Phase 1D: Multi-Repo Aggregation
#### Summary
-**Goal:** A single **Vibed Coding Profile** per user that becomes more accurate as more repos are analyzed.
+**Goal:** A single **Unified VCP** per user that becomes more accurate as more repos are analyzed.
**Non-goal:** A “persona per repository” product. Repo-level personas can exist as *facets*, but the primary experience is the **user-level aggregate profile**.
@@ -1623,7 +1629,7 @@ APIs:
#### Success Criteria
-- After analyzing N repos, user sees exactly one “Current Vibed Profile persona” reflecting aggregate signals.
+- After analyzing N repos, user sees exactly one “Current Unified VCP persona” reflecting aggregate signals.
- Adding a new repo analysis updates the profile and appends to the history timeline.
- User can still open any specific repo run and see the repo facet/persona without losing the main profile thread.
diff --git a/docs/WorkflowJournal.md b/docs/WorkflowJournal.md
index 66d611d..36f42a4 100644
--- a/docs/WorkflowJournal.md
+++ b/docs/WorkflowJournal.md
@@ -74,11 +74,13 @@ Format:
### 2025-01-16 - [DECISION] Edge Functions vs External Worker
**Context:** Need to run analysis jobs
-**Action:** Chose Supabase Edge Functions for Phase 0-1, with option to move to external worker for Phase 2+
+**Action:** Initially chose Supabase Edge Functions for Phase 0-1, with option to move to external worker for Phase 2+
**Time spent:** N/A (part of PRD)
**Automation opportunity:** None
**Notes:** Edge Functions have 60s timeout. Fine for < 1000 commits. Will need worker for large repos.
+**UPDATE (2026-01):** Migrated to **Inngest** as primary job processor with standalone worker as fallback. Inngest provides better observability, automatic retries, and serverless scaling. See `docs/architecture/inngest-integration.md`.
+
---
### 2025-01-16 - [INSIGHT] Workflow templates should be portable
diff --git a/docs/architecture/inngest-integration.md b/docs/architecture/inngest-integration.md
index aedb58e..d760f7c 100644
--- a/docs/architecture/inngest-integration.md
+++ b/docs/architecture/inngest-integration.md
@@ -1,6 +1,6 @@
# Inngest Integration
-Background job processing for Vibed Coding using [Inngest](https://www.inngest.com/).
+Background job processing for Vibe Coding Profile using [Inngest](https://www.inngest.com/).
## Why Inngest?
diff --git a/docs/architecture/llm-narrative-workflow.md b/docs/architecture/llm-narrative-workflow.md
new file mode 100644
index 0000000..c156234
--- /dev/null
+++ b/docs/architecture/llm-narrative-workflow.md
@@ -0,0 +1,391 @@
+# LLM Narrative Workflow Documentation
+
+This document describes how LLM narratives are generated, stored, and aggregated across individual repository analyses and aggregated user profiles.
+
+---
+
+## Overview
+
+Vibe Coding Profile generates two types of narratives:
+
+1. **Individual Repo Narrative** — Generated per analysis job, stored in `analysis_reports.narrative_json`
+2. **Profile Narrative** — Generated from aggregated data across all repos, stored in `user_profiles.narrative_json`
+
+Each narrative can be:
+- **LLM-generated** — Uses Claude, GPT, or Gemini to create personalized, engaging text
+- **Fallback (deterministic)** — Uses templated text based on metrics (no LLM call)
+
+---
+
+## Database Schema
+
+### Individual Report Narrative
+
+Table: `analysis_reports`
+- `narrative_json` (JSONB, NOT NULL) — The structured narrative
+- `llm_model` (TEXT, default 'none') — e.g., "claude-sonnet-4", "gpt-4o", "none"
+- `llm_key_source` (TEXT) — "platform", "user", "sponsor", "none"
+
+Schema: `supabase/migrations/0001_init_core_schema.sql`
+
+### Profile Narrative
+
+Table: `user_profiles` (columns added in migration 0017)
+- `narrative_json` (JSONB, nullable) — Profile-level narrative
+- `llm_model` (TEXT) — Model used for profile narrative
+- `llm_key_source` (TEXT) — Key source for profile narrative
+
+Schema: `supabase/migrations/0017_add_profile_narrative.sql`
+
+> **NOTE:** The TypeScript types in `packages/db/src/database.types.ts` have not been regenerated to include `narrative_json`, `llm_model`, and `llm_key_source` columns for `user_profiles`. These columns exist in the database but are not typed.
+
+---
+
+## LLM Resolution Logic
+
+### For Individual Repo Analysis
+
+Function: `resolveLLMConfig(userId, repoId)`
+Location: `apps/web/src/lib/llm-config.ts` (lines 365-401)
+
+Resolution order:
+1. **Check opt-in** — User must have `users.llm_narrative_opt_in = true`
+2. **Check platform disabled** — `llm_configs.llm_disabled` for platform scope
+3. **User's own key** — Check `llm_configs` with `scope='user'` and `scope_id=userId`
+4. **Platform key with limit** — Check `llm_usage` for per-repo limit (default: 1 free LLM per repo)
+5. **Fallback** — Return `{ config: null, source: "none" }`
+
+### For Profile Aggregation
+
+Function: `resolveProfileLLMConfig(userId)`
+Location: `apps/web/src/lib/llm-config.ts` (lines 483-519)
+
+Resolution order:
+1. **Check opt-in** — User must have opted in
+2. **Check platform disabled**
+3. **User's own key** — Always allowed for profile
+4. **Platform key with profile limit** — Check if `countReposWithLlmReports(userId) <= profileLlmRepoLimit` (default: 3)
+5. **Fallback** — Return `{ config: null, source: "none" }`
+
+---
+
+## Current Workflow
+
+### Step 1: User Triggers Analysis
+
+User clicks "Run Analysis" on a repository.
+
+### Step 2: Inngest Job Runs
+
+Function: `analyze-repo`
+Location: `apps/web/src/inngest/functions/analyze-repo.ts`
+
+The job executes these steps:
+1. Fetch commits from GitHub
+2. Process commit metadata
+3. Compute vibe metrics
+4. Classify vibe/persona
+5. Generate and save report (includes narrative)
+6. Update user profile (includes profile narrative)
+
+### Step 3: Individual Report Narrative (Step 5)
+
+Location: `apps/web/src/inngest/functions/analyze-repo.ts` (lines 1040-1138)
+
+Process:
+1. Always compute fallback narrative first via `toNarrativeFallback()`
+2. Resolve LLM config via `resolveLLMConfig(userId, repoId)`
+3. If LLM available, try to generate via `generateNarrativeWithLLM()`
+4. Use whichever narrative was generated (LLM or fallback)
+5. Save to `analysis_reports` with `llm_model` and `llm_key_source` tracking
+
+### Step 4: Profile Aggregation (Step 6)
+
+Location: `apps/web/src/inngest/functions/analyze-repo.ts` (lines 1192-1595)
+
+Process:
+1. Fetch all completed jobs for user (excluding disconnected repos)
+2. Fetch vibe_insights for each job
+3. Build `RepoInsightSummary[]` array
+4. Call `aggregateUserProfile()` to compute weighted axes and persona
+5. Resolve LLM config for profile via `resolveProfileLLMConfig(userId)`
+6. Generate profile narrative (LLM via `generateProfileNarrativeWithLLM()` or fallback via `toProfileNarrativeFallback()`)
+7. Upsert `user_profiles` with narrative and LLM metadata
+8. Save snapshot to `user_profile_history` with LLM metadata
+
+---
+
+## Scenario Analysis
+
+### Scenario 1: Full LLM User (BYOK with Opt-In)
+
+**User state:**
+- `users.llm_narrative_opt_in = true`
+- Has valid API key in `llm_configs` with `scope='user'`
+
+**Behavior:**
+- Every repo analysis generates LLM narrative (using user's key)
+- Every profile aggregation generates LLM narrative (using user's key)
+- No usage limits apply (user's own key)
+
+**Result:**
+- `analysis_reports.llm_model = "claude-sonnet-4"` (or similar)
+- `analysis_reports.llm_key_source = "user"`
+- `user_profiles.llm_model = "claude-sonnet-4"`
+- `user_profiles.llm_key_source = "user"`
+
+---
+
+### Scenario 2: No LLM User (No Opt-In, No Key)
+
+**User state:**
+- `users.llm_narrative_opt_in = false` (or never set)
+- No API key configured
+
+**Behavior:**
+- All repo analyses use fallback narrative
+- Profile uses fallback narrative
+- Metrics are still computed; only narrative text is deterministic
+
+**Result:**
+- `analysis_reports.llm_model = "none"`
+- `analysis_reports.llm_key_source = "none"`
+- `user_profiles.llm_model = null`
+- `user_profiles.llm_key_source = "none"`
+
+---
+
+### Scenario 3: Platform Free Tier User (Opted In, No Key)
+
+**User state:**
+- `users.llm_narrative_opt_in = true`
+- No personal API key
+- Using platform's free tier
+
+**Platform limits (defaults):**
+- Per-repo limit: 1 free LLM analysis per repo
+- Profile LLM repo limit: 3 repos
+
+**Behavior for first 3 repos:**
+1. **Repo 1, Run 1:** LLM narrative (platform key)
+2. **Repo 1, Run 2:** Fallback narrative (limit exhausted for this repo)
+3. **Repo 2, Run 1:** LLM narrative (platform key)
+4. **Repo 3, Run 1:** LLM narrative (platform key)
+5. **Profile after Repo 3:** LLM profile narrative (3 repos with LLM ≤ limit of 3)
+
+**Behavior for 4th repo onwards:**
+6. **Repo 4, Run 1:** LLM narrative (platform key, this is first run on this repo)
+7. **Profile after Repo 4:** Fallback profile narrative (4 repos with LLM > limit of 3)
+
+---
+
+### Scenario 4: Mixed Mode — Free Tier Exhausted, Then Non-LLM
+
+**User state:**
+- `users.llm_narrative_opt_in = true`
+- No personal API key
+- Already used free LLM on 3 repos
+
+**Sequence:**
+1. User has 3 repos with LLM-generated reports
+2. User analyzes a 4th repo (first run) → LLM report (platform key)
+3. User analyzes a 5th repo (first run) → LLM report (platform key)
+4. User re-analyzes Repo 1 (second run) → Fallback report (per-repo limit exhausted)
+
+**Profile after each step:**
+- After step 1: LLM profile (3 repos ≤ 3)
+- After step 2: Fallback profile (4 repos > 3)
+- After step 3: Fallback profile (5 repos > 3)
+- After step 4: Fallback profile (still 5 repos with successful LLM)
+
+**Key insight:** The profile LLM decision is based on the **count of unique repos with successful LLM reports**, not the count of jobs or total LLM calls.
+
+---
+
+### Scenario 5: Downgrade — Had Key, Key Removed/Expired
+
+**User state (before):**
+- Opted in, had personal API key
+- 5 repos all with LLM narratives
+
+**User state (after):**
+- Key removed or expired
+- Still opted in
+
+**Behavior on next analysis:**
+1. User runs analysis on Repo 1 (second time)
+2. `resolveLLMConfig` checks: no user key, check platform limit
+3. Platform limit for Repo 1 already exhausted → Fallback narrative
+4. Profile: 5 repos have LLM history > 3 → Fallback profile narrative
+
+**Result:** New analyses fall back, but existing LLM narratives are preserved in history.
+
+---
+
+### Scenario 6: Upgrade — Was Free Tier, Added Key
+
+**User state (before):**
+- Opted in, no personal key
+- 4 repos: Repo 1-3 have LLM, Repo 4 has fallback
+
+**User state (after):**
+- Added personal API key
+
+**Behavior on next analysis:**
+1. User runs analysis on Repo 4 (second time)
+2. `resolveLLMConfig` finds user key → LLM narrative
+3. Profile: User has key → LLM profile narrative (no limit with own key)
+
+**Result:** Adding a key immediately unlocks LLM for all future analyses.
+
+---
+
+## Resolved Issues
+
+The following issues have been fixed:
+
+### Issue 1: TypeScript Types Updated (FIXED)
+
+The `packages/db/src/database.types.ts` file has been manually updated to include:
+- `user_profiles.narrative_json`
+- `user_profiles.llm_model`
+- `user_profiles.llm_key_source`
+- `user_profiles.regenerating`
+- `user_profile_history.version_number`
+- `user_profile_history.llm_model`
+- `user_profile_history.llm_key_source`
+
+### Issue 2: Profile Narrative Displayed in UI (FIXED)
+
+Location: `apps/web/src/app/page.tsx`
+
+**What was done:**
+- Updated profile queries to include `narrative_json`, `llm_model`, `llm_key_source`
+- Profile insight section now shows LLM narrative headline when available
+- Falls back to deterministic `generateCrossRepoInsight()` when no LLM narrative
+- Shows "AI-generated" badge when narrative comes from LLM
+- Displays full narrative paragraphs and highlights when available
+
+### Issue 3: Version Selector for Reports (FIXED)
+
+Location: `apps/web/src/app/analysis/[jobId]/AnalysisClient.tsx`
+
+**What was done:**
+- Added `repoHistory` computed value that filters history by the current repo
+- Added a version selector dropdown that appears when multiple runs exist for the same repo
+- Dropdown shows date, persona label, and navigation to switch between versions
+- Styled consistently with the rest of the analysis page
+
+### Issue 4: Profile History Accessible (FIXED)
+
+Location: `apps/web/src/components/ProfileVersionSelector.tsx`
+
+**What was done:**
+- Created new `ProfileVersionSelector` client component
+- Shows "Profile History" collapsible section on the profile page
+- Fetches from `/api/profile/history` when expanded
+- Version selector dropdown to browse historical profile states
+- Shows persona, commit counts, repo counts, LLM model for each version
+- Displays trigger repo name for context
+
+### Issue 5: Visual Indicator for Profile Aggregation (FIXED)
+
+Location: `apps/web/src/app/admin/jobs/page.tsx` and `apps/web/src/app/admin/actions.ts`
+
+**What was done:**
+- Updated `getAllJobs` action to query `user_profile_history` for profile updates triggered by jobs
+- Added "Profile" column to the admin jobs table
+- Shows "Updated" badge with checkmark for jobs that triggered a profile update
+- Badge shows persona name on hover
+
+---
+
+## Aggregation Logic Details
+
+### How Profile Narrative Changes with Mixed LLM/Non-LLM Reports
+
+The profile narrative is **regenerated fresh on every job completion**. It doesn't "merge" LLM narratives from individual reports.
+
+**What happens:**
+1. Job completes
+2. `aggregateUserProfile()` runs on all completed jobs (LLM and non-LLM alike)
+3. `resolveProfileLLMConfig()` determines if profile gets LLM narrative
+4. Either LLM generation or fallback is called
+5. Result stored in `user_profiles.narrative_json`
+
+**Key insight:** Individual report narratives don't affect the profile narrative decision. The profile LLM decision is solely based on:
+1. User opt-in status
+2. User's own API key (if any)
+3. Count of repos with successful LLM reports (for platform key limit)
+
+### What Data Goes Into Profile Narrative
+
+Whether LLM or fallback, the profile narrative is generated from:
+- `AggregatedProfile.persona` — The aggregated persona
+- `AggregatedProfile.axes` — Weighted average of all repo axes
+- `AggregatedProfile.totalCommits` — Total commits across all repos
+- `AggregatedProfile.totalRepos` — Number of repos analyzed
+- `AggregatedProfile.repoBreakdown` — Per-repo persona breakdown
+
+**NOT included:** Individual repo narratives. The profile narrative is a fresh generation based on aggregated data.
+
+---
+
+## Accessing Historical Versions
+
+### Current Capabilities
+
+**For Repo Reports:**
+- Each analysis creates a new `analysis_jobs` row with unique ID
+- All jobs are stored; nothing is overwritten
+- API: `GET /api/analysis/history` returns all insights with job metadata
+- UI: Timeline in `AnalysisClient` shows up to 8 previous runs (lines 1137-1163)
+
+**For Profile:**
+- Each job completion creates a `user_profile_history` snapshot
+- Snapshots include `version_number` (auto-incremented via trigger)
+- API: `GET /api/profile/history` lists all versions
+- API: `GET /api/profile/history/[id]` fetches specific version
+
+### What's Missing
+
+1. **Repo Version Selector:** A dropdown in the analysis page to switch between versions
+2. **Profile Version Selector:** A dropdown on the profile page to view historical states
+3. **Diff View:** Compare two versions side-by-side
+
+---
+
+## Key File Locations
+
+| Component | Location |
+|-----------|----------|
+| LLM resolution (repo) | `apps/web/src/lib/llm-config.ts` → `resolveLLMConfig()` |
+| LLM resolution (profile) | `apps/web/src/lib/llm-config.ts` → `resolveProfileLLMConfig()` |
+| Repo narrative generation | `apps/web/src/inngest/functions/analyze-repo.ts` (lines 1040-1138) |
+| Profile aggregation | `apps/web/src/inngest/functions/analyze-repo.ts` (lines 1192-1595) |
+| Profile narrative generation | `apps/web/src/lib/profile-narrative.ts` |
+| Fallback narrative (repo) | `packages/core/src/narrative.ts` → `toNarrativeFallback()` |
+| Fallback narrative (profile) | `apps/web/src/lib/profile-narrative.ts` → `toProfileNarrativeFallback()` |
+| Aggregation algorithm | `packages/core/src/vibe.ts` → `aggregateUserProfile()` |
+| Profile history API | `apps/web/src/app/api/profile/history/route.ts` |
+| Analysis history API | `apps/web/src/app/api/analysis/history/route.ts` |
+
+---
+
+## Summary Table
+
+| Scenario | Repo LLM | Profile LLM | Notes |
+|----------|----------|-------------|-------|
+| No opt-in | Never | Never | Must opt in first |
+| Opted in + own key | Always | Always | No limits |
+| Opted in + platform key, <3 repos with LLM | First run per repo | Yes | Platform limits |
+| Opted in + platform key, ≥4 repos with LLM | First run per repo | No | Profile limit exceeded |
+| Key removed after use | Fallback | Fallback | Existing reports preserved |
+
+---
+
+## Related Documentation
+
+- [Vibe Coding Profile Analysis Pipeline](./vibed-analysis-pipeline.md) — Full pipeline architecture
+- [PRD: Profile Aggregation](../PRD-profile-aggregation.md) — Original design
+- [LLM Setup Guide](../llm-setup.md) — User-facing LLM configuration
diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md
index 6950d42..30d82a9 100644
--- a/docs/architecture/overview.md
+++ b/docs/architecture/overview.md
@@ -1,21 +1,26 @@
-# Vibed Coding Architecture Overview
+# Vibe Coding Profile Architecture Overview
-This document explains how the Vibed Coding stack fits together, how data flows from a logged-in user through Supabase, the worker, and the UI, and which pieces of the codebase (web, worker, packages) own each responsibility. The diagrams use Mermaid syntax so you can paste them into any renderer, but the textual explanations also narrate every interaction.
+This document explains how the Vibe Coding Profile stack fits together, how data flows from a logged-in user through Supabase, the worker, and the UI, and which pieces of the codebase (web, worker, packages) own each responsibility. The diagrams use Mermaid syntax so you can paste them into any renderer, but the textual explanations also narrate every interaction.
+
+> **Related Documentation:**
+> - [How Vibe Coding Profile Works](../how-vibed-works.md) — Product-friendly explanation
+> - [Vibe Coding Profile Analysis Pipeline](./vibed-analysis-pipeline.md) — Detailed analysis algorithms and data flow
+> - [Inngest Integration](./inngest-integration.md) — Background job processing
## High-level Components
-1. **Next.js 16 (App Router) Web App** (`apps/web`)
+1. **Next.js 14+ (App Router) Web App** (`apps/web`)
- Hosts the public marketing homepage (`/`) plus authenticated areas (`/repos`, `/analysis/[jobId]`, `/login`, `/auth/callback`, etc.).
- Handles Supabase Auth via `proxy.ts`, guarding protected routes and synchronizing auth cookies.
- Calls Supabase Route Handlers (`/api/analysis/*`, `/api/github/sync-repos`, etc.) to start jobs, list repos, fetch shared insights, export share cards, and run other backend flows.
- - Renders the “Vibed” experience by reading `analysis_insights`, `analysis_metrics`, `analysis_reports`, and job metadata into interactive and shareable dashboards.
+ - Renders the “Vibe Coding Profile” experience by reading `analysis_insights`, `analysis_metrics`, `analysis_reports`, and job metadata into interactive and shareable dashboards.
2. **Supabase Backend (Postgres + Auth + Edge)** (`supabase`)
- Stores all domain data in tables such as `users`, `repos`, `analysis_jobs`, `analysis_metrics`, `analysis_reports`, `analysis_insights`, `github_accounts`, etc. The `supabase` folder tracks migrations and seeds.
- Enforces row-level security so each user only sees their records; Supabase Auth is the entrypoint for GitHub OAuth (handled in the Next app via the `/auth/callback` route).
- Provides RPCs such as `claim_analysis_job` to coordinate workers.
-3. **Vibed Coding Worker** (`apps/worker`)
+3. **Vibe Coding Profile Worker** (`apps/worker`)
- Polls `analysis_jobs` (via `claim_analysis_job` RPC) and processes each job sequentially.
- Fetches commits through the GitHub API, decrypts stored tokens, calculates metrics with `@vibed/core`, assigns a Vibe persona, and writes structured results back to Postgres (`analysis_metrics`, `analysis_reports`, `analysis_insights`, updates job status).
- Hosts a health endpoint so orchestration (e.g., Docker Compose, Supervisord, or manual runs) can monitor liveness.
@@ -62,7 +67,7 @@ graph TD
- **Polling**: The worker polls Postgres every `POLL_INTERVAL_MS` (5 s) and claims jobs via `claim_analysis_job`.
- **Commit retrieval**: It uses the GitHub token (stored encrypted in `github_accounts`) to fetch the most recent commit list and details.
- **Computation**: `computeAnalysisMetrics`, `assignVibeType`, and `computeAnalysisInsights` generate the numbers, persona, tech signals, share template, and confidence data.
-- **Persistence**: Each result writes to a dedicated table, ensuring the UI can render both “Vibed summary cards” and “Deep dive evidence” without recompute.
+- **Persistence**: Each result writes to a dedicated table, ensuring the UI can render both “VCP summary cards” and “Deep dive evidence” without recompute.
## API & UI Interaction Map
@@ -71,7 +76,7 @@ graph TD
3. `/api/analysis/start` – Inserts new `analysis_jobs`.
4. `/api/analysis/history` – Lists past jobs (drives persona evolution views).
5. `/api/analysis/[jobId]/share` – Renders share-card exports (PNG/SVG) using `share_template` colors and text from `analysis_insights`.
-6. `/analysis/[jobId]` – Next.js page that reads `analysis_insights`, `analysis_metrics`, and `analysis_reports` to build the “Vibed” experience with highlights and deep dives.
+6. `/analysis/[jobId]` – Next.js page that reads `analysis_insights`, `analysis_metrics`, and `analysis_reports` to build the “Vibe Coding Profile” experience with highlights and deep dives.
## Historical Persona Tracking
@@ -97,7 +102,7 @@ Combined with the `/api/analysis/history` endpoint, the UI can plot persona evol
## Bringing It Together
-- **Frontend**: Next.js renders both marketing copy and the authenticated “Vibed” dashboard. It relies on shared data structures from `packages/db`.
+- **Frontend**: Next.js renders both marketing copy and the authenticated “Vibe Coding Profile” dashboard. It relies on shared data structures from `packages/db`.
- **Backend**: Supabase stores user, repo, job, metric, report, and insight data; the worker writer uses `packages/core` to derive AI-assisted personas and insights and writes them back.
- **UX interplay**: The UI reads from the same insights JSON the worker produces, which means the data rendering layer is deterministic and traceable (the worker is the single source of truth for persona claims).
diff --git a/docs/architecture/vibed-analysis-pipeline.md b/docs/architecture/vibed-analysis-pipeline.md
new file mode 100644
index 0000000..7f7c2f4
--- /dev/null
+++ b/docs/architecture/vibed-analysis-pipeline.md
@@ -0,0 +1,852 @@
+# Vibe Coding Profile Analysis Pipeline — Technical Architecture
+
+> **Last Updated:** January 2026
+> **Maintainer:** Update this document when analysis logic, data models, or algorithms change.
+
+This document provides a comprehensive technical reference for how Vibe Coding Profile analyzes repositories and generates Repo VCPs and Unified VCPs. It covers data flow, algorithms, type definitions, and links to source code.
+
+---
+
+## Table of Contents
+
+1. [High-Level Architecture](#high-level-architecture)
+2. [Data Flow](#data-flow)
+3. [Repository Analysis Pipeline](#repository-analysis-pipeline)
+4. [Metrics Computation](#metrics-computation)
+5. [Vibe Axes System](#vibe-axes-system)
+6. [Persona Detection](#persona-detection)
+7. [Insights Generation](#insights-generation)
+8. [LLM Narrative Generation](#llm-narrative-generation)
+9. [Profile Aggregation](#profile-aggregation)
+10. [Database Schema](#database-schema)
+11. [Source Code References](#source-code-references)
+
+---
+
+## High-Level Architecture
+
+```mermaid
+flowchart TB
+ subgraph Client["Web Client (Next.js)"]
+ UI[Analysis UI]
+ API[API Routes]
+ end
+
+ subgraph Processing["Job Processing"]
+ Inngest[Inngest Function]
+ Worker[Fallback Worker]
+ end
+
+ subgraph External["External Services"]
+ GitHub[GitHub API]
+ LLM[LLM Provider]
+ end
+
+ subgraph Storage["Supabase"]
+ DB[(PostgreSQL)]
+ Auth[Supabase Auth]
+ end
+
+ subgraph Core["@vibed/core"]
+ Metrics[Metrics Computation]
+ Axes[Vibe Axes]
+ Persona[Persona Detection]
+ Insights[Insights Generation]
+ end
+
+ UI --> API
+ API --> DB
+ API --> Inngest
+ Inngest --> GitHub
+ Inngest --> Core
+ Inngest --> LLM
+ Inngest --> DB
+ Worker --> GitHub
+ Worker --> Core
+ Worker --> LLM
+ Worker --> DB
+```
+
+### Components
+
+| Component | Location | Responsibility |
+|-----------|----------|----------------|
+| Web Client | `apps/web` | UI, API routes, job triggering |
+| Inngest Function | `apps/web/src/inngest/functions/analyze-repo.ts` | Primary job processor |
+| Fallback Worker | `apps/worker/src/index.ts` | Self-hosted fallback |
+| @vibed/core | `packages/core/src/` | All analysis logic |
+| Database | `supabase/migrations/` | Persistent storage |
+
+---
+
+## Data Flow
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant API as API Route
+ participant DB as Supabase
+ participant Inngest
+ participant GitHub
+ participant Core as @vibed/core
+ participant LLM
+
+ User->>API: POST /api/analysis/start
+ API->>DB: Create analysis_job (status=queued)
+ API->>Inngest: Fire repo/analyze.requested event
+ API-->>User: Return job_id
+
+ Inngest->>DB: Claim job (status=running)
+ Inngest->>GitHub: Fetch commits (paginated)
+ Inngest->>GitHub: Fetch PRs (if available)
+
+ Inngest->>Core: filterAutomationCommits()
+ Inngest->>Core: computeAnalysisMetrics()
+ Inngest->>Core: computeAnalysisInsights()
+ Inngest->>Core: computeVibeFromCommits()
+ Inngest->>Core: detectVibePersona()
+
+ Inngest->>DB: Store analysis_metrics
+ Inngest->>DB: Store analysis_insights
+ Inngest->>DB: Store vibe_insights
+
+ opt LLM Available
+ Inngest->>LLM: Generate narrative
+ Inngest->>DB: Store analysis_reports
+ end
+
+ Inngest->>Core: aggregateUserProfile()
+ Inngest->>DB: Update user_profiles
+ Inngest->>DB: Update job (status=done)
+
+ User->>API: GET /api/analysis/:id
+ API->>DB: Fetch results
+ API-->>User: Return analysis data
+```
+
+---
+
+## Repository Analysis Pipeline
+
+### Step 1: Job Creation
+
+```typescript
+// API creates job record
+interface AnalysisJob {
+ id: string;
+ user_id: string;
+ repo_id: string;
+ status: "queued" | "running" | "done" | "error";
+ created_at: string;
+ started_at: string | null;
+ completed_at: string | null;
+ error_message: string | null;
+}
+```
+
+### Step 2: Commit Fetching
+
+```mermaid
+flowchart LR
+ A[Get Repo Metadata] --> B{Repo Age}
+ B -->|< 1 year| C[Fetch Recent 300]
+ B -->|> 1 year| D[Time-Distributed Sampling]
+ D --> E[Split into 6-24 buckets]
+ E --> F[Sample evenly per bucket]
+ C --> G[Commit List]
+ F --> G
+ G --> H[Fetch Details per Commit]
+ H --> I[CommitEvent Array]
+```
+
+**Sampling Strategy:**
+- Repos are divided into time buckets (6-24 based on size)
+- Each bucket is sampled evenly to capture patterns across the repo's lifetime
+- Maximum 300 commits per analysis
+- Commit details (file paths, stats) fetched in parallel batches
+
+### Step 3: Automation Filtering
+
+```typescript
+// packages/core/src/index.ts
+function isAutomationCommit(event: CommitEvent): boolean {
+ // Filters: dependabot, renovate, release-please, github-actions, etc.
+ // Based on email patterns and commit message patterns
+}
+
+function filterAutomationCommits(events: CommitEvent[]): CommitEvent[]
+```
+
+**Filtered Patterns:**
+- Bot emails: `dependabot`, `renovate`, `github-actions`, `[bot]`
+- Message patterns: `chore(release):`, `bump version`, `[automated]`
+
+---
+
+## Metrics Computation
+
+### Input Type
+
+```typescript
+interface CommitEvent {
+ sha: string;
+ message: string;
+ author_date: string;
+ committer_date: string;
+ author_email: string;
+ files_changed: number;
+ additions: number;
+ deletions: number;
+ parents: string[];
+ file_paths?: string[]; // For subsystem detection
+}
+```
+
+### Output Type
+
+```typescript
+interface AnalysisMetrics {
+ // Volume
+ total_commits: number;
+ total_additions: number;
+ total_deletions: number;
+ total_files_changed: number;
+
+ // Timing
+ first_commit_date: string;
+ last_commit_date: string;
+ active_days: number;
+ span_days: number;
+
+ // Commit Size Distribution
+ commit_size_p50: number;
+ commit_size_p90: number;
+ commit_size_mean: number;
+ commit_size_stddev: number;
+
+ // Frequency
+ commits_per_active_day_mean: number;
+ commits_per_active_day_max: number;
+
+ // Rhythm
+ hours_between_commits_p50: number;
+ hours_between_commits_p90: number;
+ burstiness_score: number; // -1 (steady) to +1 (bursty)
+
+ // Message Analysis
+ message_length_p50: number;
+ message_length_p90: number;
+ conventional_commit_ratio: number;
+
+ // Iteration Patterns
+ fix_commit_ratio: number;
+ fixup_sequence_count: number; // fix-after-feature sequences
+
+ // Build Sequence
+ category_first_occurrence: Record;
+ category_distribution: Record;
+
+ // Merge Behavior
+ merge_commit_ratio: number;
+
+ // Confidence
+ data_quality_score: number; // 0-100
+}
+```
+
+### Build Category Classification
+
+```typescript
+type BuildCategory =
+ | "setup" | "auth" | "feature" | "test" | "infra"
+ | "docs" | "refactor" | "fix" | "style" | "chore" | "unknown";
+
+function classifyCommit(message: string): BuildCategory {
+ // Priority 1: Conventional commit prefixes (feat:, fix:, test:, etc.)
+ // Priority 2: Scope overrides (auth, setup)
+ // Priority 3: Keyword matching
+ // Priority 4: Default to "feature" for substantive commits
+}
+```
+
+### Burstiness Score Algorithm
+
+```typescript
+function calculateBurstiness(timestamps: Date[]): number {
+ // Burstiness = (stddev - mean) / (stddev + mean)
+ // Range: -1 (perfectly regular) to +1 (highly bursty)
+ const intervals = timestamps.map((t, i) =>
+ i > 0 ? (t - timestamps[i-1]) / (1000 * 60 * 60) : 0
+ ).slice(1);
+
+ const mean = avg(intervals);
+ const stddev = std(intervals);
+ return (stddev - mean) / (stddev + mean);
+}
+```
+
+---
+
+## Vibe Axes System
+
+Six deterministic axes scored 0-100:
+
+```mermaid
+graph TD
+ subgraph Axes["Six Vibe Axes"]
+ A["A: Automation Heaviness"]
+ B["B: Guardrail Strength"]
+ C["C: Iteration Loop Intensity"]
+ D["D: Planning Signal"]
+ E["E: Surface Area per Change"]
+ F["F: Shipping Rhythm"]
+ end
+
+ subgraph Inputs["Input Signals"]
+ I1[Commit Size Stats]
+ I2[PR Chunkiness]
+ I3[First-Touch Percentiles]
+ I4[Category Distribution]
+ I5[Fix Sequences]
+ I6[Conventional Commits]
+ I7[Issue Linking]
+ I8[Episode Subsystems]
+ I9[Burstiness Score]
+ end
+
+ I1 --> A
+ I2 --> A
+ I3 --> B
+ I4 --> B
+ I4 --> C
+ I5 --> C
+ I6 --> D
+ I7 --> D
+ I8 --> E
+ I9 --> F
+```
+
+### Axis Computation
+
+```typescript
+interface AxisValue {
+ score: number; // 0-100
+ level: "low" | "medium" | "high";
+ why: string[]; // Evidence IDs
+}
+
+interface VibeAxes {
+ automation_heaviness: AxisValue;
+ guardrail_strength: AxisValue;
+ iteration_loop_intensity: AxisValue;
+ planning_signal: AxisValue;
+ surface_area_per_change: AxisValue;
+ shipping_rhythm: AxisValue;
+}
+```
+
+### Axis A: Automation Heaviness
+
+**Signals:**
+- Average files changed per commit
+- p90 commit size (additions + deletions)
+- p90 PR changed files
+
+**Formula:**
+```
+Score = 0.5 × scoreFromZ(avgFiles, mid=4, spread=4)
+ + 0.3 × scoreFromZ(p90Commit, mid=400, spread=400)
+ + 0.2 × scoreFromZ(prP90, mid=25, spread=25)
+```
+
+### Axis B: Guardrail Strength
+
+**Signals:**
+- First-touch percentile for tests/CI/docs (earlier = stronger)
+- Guardrail commit density (tests + docs + chore + CI)
+- PR checklist presence rate
+
+**Formula:**
+```
+Score = 0.5 × (1 - avgFirstTouch) × 100
+ + 0.3 × guardrailDensity × 200
+ + 0.2 × prChecklistRate × 100
+```
+
+### Axis C: Iteration Loop Intensity
+
+**Signals:**
+- Fix-after-feature sequence count
+- Fix commit ratio
+- Episode fix density
+
+**Formula:**
+```
+Score = 0.5 × scoreFromZ(fixSeq/total, mid=0.12, spread=0.12)
+ + 0.3 × fixRatio × 100
+ + 0.2 × episodeFixDensity × 100
+```
+
+### Axis D: Planning Signal
+
+**Signals:**
+- Conventional commit ratio
+- PR issue linking rate
+- Docs-before-feature pattern
+
+**Formula:**
+```
+Score = 0.35 × issueLinkRate × 100
+ + 0.35 × conventionalRatio × 100
+ + 0.30 × docsFirst × 100
+```
+
+### Axis E: Surface Area per Change
+
+**Signals:**
+- Median subsystems touched per work episode
+
+**Formula:**
+```
+Score = scoreFromZ(medianSubsystems, mid=3, spread=2)
+```
+
+### Axis F: Shipping Rhythm
+
+**Signals:**
+- Burstiness score
+- p90 commits per episode
+
+**Formula:**
+```
+Score = 0.6 × ((burstiness + 1) / 2) × 100
+ + 0.4 × scoreFromZ(episodeP90, mid=6, spread=6)
+```
+
+---
+
+## Persona Detection
+
+```mermaid
+flowchart TD
+ Axes[Six Axis Scores] --> Rules[Persona Rules]
+ Rules --> Strict{Strict Match?}
+ Strict -->|Yes| StrictPersona[Matched Persona]
+ Strict -->|No| Loose{Loose Match?}
+ Loose -->|≥50% conditions, score≥60| LoosePersona[Nearest Persona]
+ Loose -->|No| Fallback[Balanced Builder]
+
+ StrictPersona --> Output[VibePersona]
+ LoosePersona --> Output
+ Fallback --> Output
+```
+
+### Persona Rules
+
+```typescript
+type VibePersonaId =
+ | "prompt_sprinter" // Vibe Prototyper
+ | "guardrailed_viber" // Test-First Validator
+ | "spec_first_director" // Spec-Driven Architect
+ | "vertical_slice_shipper" // Agent Orchestrator
+ | "fix_loop_hacker" // Hands-On Debugger
+ | "rapid_risk_taker" // Rapid Risk-Taker
+ | "balanced_builder"; // Reflective Balancer (fallback)
+```
+
+| Persona | Conditions |
+|---------|------------|
+| **Vibe Prototyper** | A≥70, C≥65, B<40, D<45 |
+| **Test-First Validator** | A≥65, B≥65, C≥40 |
+| **Spec-Driven Architect** | D≥70, B≥55, A≥40 |
+| **Agent Orchestrator** | E≥70, A≥60 |
+| **Hands-On Debugger** | C≥80, F≥60 |
+| **Rapid Risk-Taker** | A≥65, B<45, D<50, F≥40 |
+| **Balanced Builder** | Fallback when no rules match |
+
+### Confidence Calculation
+
+```typescript
+function getConfidence(meta: { commitCount, prCount, dataQualityScore }): Confidence {
+ const commitOk = meta.commitCount >= 200;
+ const prOk = meta.prCount >= 15;
+ const qualityOk = meta.dataQualityScore >= 70;
+
+ if ((commitOk || prOk) && qualityOk && personaScore >= 75) return "high";
+ if ((meta.commitCount >= 80 || meta.prCount >= 6) && personaScore >= 65) return "medium";
+ return "low";
+}
+```
+
+---
+
+## Insights Generation
+
+### AnalysisInsights Structure
+
+```typescript
+interface AnalysisInsights {
+ version: string;
+ timezone: "UTC";
+ generated_at: string;
+
+ totals: { commits: number };
+
+ streak: {
+ longest_days: number;
+ start_day: string | null;
+ end_day: string | null;
+ confidence: Confidence;
+ evidence_shas: string[];
+ };
+
+ timing: {
+ top_weekdays: Array<{ weekday: number; count: number }>;
+ peak_weekday: number | null;
+ peak_hour: number | null;
+ peak_window: "mornings" | "afternoons" | "evenings" | "late_nights" | null;
+ confidence: Confidence;
+ evidence_shas: string[];
+ };
+
+ commits: {
+ top_category: BuildCategory | null;
+ category_counts: Record;
+ features: number;
+ fixes: number;
+ features_per_fix: number | null;
+ confidence: Confidence;
+ evidence_shas: string[];
+ };
+
+ chunkiness: {
+ avg_files_changed: number | null;
+ label: "slicer" | "mixer" | "chunker" | null;
+ confidence: Confidence;
+ evidence_shas: string[];
+ };
+
+ multi_agent_signals: {
+ co_author_count: number;
+ ai_trailer_count: number;
+ ai_keyword_count: number;
+ pr_squash_merge_rate: number | null;
+ pr_template_rate: number | null;
+ pr_checklist_rate: number | null;
+ pr_linked_issue_rate: number | null;
+ confidence: Confidence;
+ evidence_shas: string[];
+ };
+
+ artifact_traceability: ArtifactTraceability;
+
+ persona: AnalysisInsightPersona;
+ share_template: AnalysisInsightShareTemplate;
+ persona_delta: AnalysisInsightPersonaDelta[];
+ tech_signals: AnalysisInsightTechSignals;
+
+ sources: string[];
+ disclaimers: string[];
+}
+```
+
+### Chunkiness Classification
+
+```typescript
+function classifyChunkiness(avgFilesChanged: number): "slicer" | "mixer" | "chunker" {
+ if (avgFilesChanged <= 3) return "slicer"; // Focused changes
+ if (avgFilesChanged <= 8) return "mixer"; // Balanced
+ return "chunker"; // Wide scope
+}
+```
+
+### Multi-Agent Signal Detection
+
+```mermaid
+flowchart LR
+ Commits[Commit Messages] --> Trailers[Parse Trailers]
+ Trailers --> CoAuthor[Co-authored-by Count]
+ Trailers --> AITrailer[AI Trailer Count]
+
+ Messages[Message Keywords] --> AIKeywords[Agent/Copilot/Claude mentions]
+
+ PRs[Pull Requests] --> SquashRate[Squash Merge Rate]
+ PRs --> TemplateRate[Template Usage Rate]
+ PRs --> ChecklistRate[Checklist Rate]
+ PRs --> IssueRate[Issue Link Rate]
+
+ CoAuthor --> Signals[Multi-Agent Signals]
+ AITrailer --> Signals
+ AIKeywords --> Signals
+ SquashRate --> Signals
+ TemplateRate --> Signals
+ ChecklistRate --> Signals
+ IssueRate --> Signals
+```
+
+---
+
+## LLM Narrative Generation
+
+### When LLM Is Used
+
+```mermaid
+flowchart TD
+ Start[Analysis Complete] --> Check{LLM Available?}
+ Check -->|No| Fallback[Metrics-Only Narrative]
+ Check -->|Yes| Resolve[Resolve LLM Config]
+
+ Resolve --> UserKey{User API Key?}
+ UserKey -->|Yes| UseUser[Use User Key]
+ UserKey -->|No| FreeTier{Free Tier Available?}
+ FreeTier -->|Yes| UsePlatform[Use Platform Key]
+ FreeTier -->|No| Fallback
+
+ UseUser --> Generate[Generate Narrative]
+ UsePlatform --> Generate
+ Generate --> Store[Store in analysis_reports]
+```
+
+### LLM Input (Privacy-Safe)
+
+The LLM receives:
+- Metrics JSON (no commit content)
+- Insights JSON (persona, timing, patterns)
+- Episode summary (timestamps, categories, sizes)
+- Commit lines: `timestamp sha category size files` (NO message content)
+
+**Never sent to LLM:**
+- Commit message text
+- File contents
+- Project/product names
+- Business domain information
+
+### Supported Providers
+
+| Provider | Models | Config |
+|----------|--------|--------|
+| Anthropic | claude-sonnet-4, claude-haiku | `ANTHROPIC_API_KEY` |
+| OpenAI | gpt-4o, gpt-4o-mini | `OPENAI_API_KEY` |
+| Google | gemini-2.0-flash, gemini-1.5-flash | `GEMINI_API_KEY` |
+
+---
+
+## Profile Aggregation
+
+### Multi-Repo to Single Profile
+
+```mermaid
+flowchart TD
+ subgraph Repos["Individual Repo Analyses"]
+ R1[Repo A: 500 commits]
+ R2[Repo B: 200 commits]
+ R3[Repo C: 100 commits]
+ end
+
+ subgraph Aggregation["Aggregation Logic"]
+ W[Weight by Commit Count]
+ M[Merge Metrics]
+ A[Average Axes]
+ P[Re-detect Persona]
+ end
+
+ subgraph Profile["User Profile"]
+ UP[Aggregated Profile]
+ N[Profile Narrative]
+ end
+
+ R1 --> W
+ R2 --> W
+ R3 --> W
+ W --> M
+ M --> A
+ A --> P
+ P --> UP
+ UP --> N
+```
+
+### Aggregation Algorithm
+
+```typescript
+function aggregateUserProfile(repoInsights: RepoInsightSummary[]): UserProfile {
+ // 1. Calculate total commits for weighting
+ const totalCommits = sum(repoInsights.map(r => r.commit_count));
+
+ // 2. Weight each repo's contribution
+ const weights = repoInsights.map(r => r.commit_count / totalCommits);
+
+ // 3. Aggregate metrics (weighted average)
+ const aggregatedMetrics = weightedAverage(repoInsights, weights);
+
+ // 4. Aggregate axes (weighted average)
+ const aggregatedAxes = weightedAverageAxes(repoInsights, weights);
+
+ // 5. Re-detect persona from aggregated axes
+ const persona = detectVibePersona(aggregatedAxes, {
+ commitCount: totalCommits,
+ prCount: sum(repoInsights.map(r => r.pr_count)),
+ });
+
+ // 6. Generate profile narrative (if LLM available)
+ const narrative = generateProfileNarrative(aggregatedMetrics, persona);
+
+ return { metrics: aggregatedMetrics, axes: aggregatedAxes, persona, narrative };
+}
+```
+
+### Profile Confidence
+
+| Repos Analyzed | Confidence |
+|----------------|------------|
+| 1 repo | Low — snapshot only |
+| 2-3 repos | Medium — patterns emerging |
+| 4+ repos | High — consistent patterns |
+
+---
+
+## Database Schema
+
+### Core Tables
+
+```mermaid
+erDiagram
+ users ||--o{ repos : owns
+ users ||--o{ analysis_jobs : requests
+ users ||--|| user_profiles : has
+ repos ||--o{ analysis_jobs : analyzed_by
+ analysis_jobs ||--|| analysis_metrics : produces
+ analysis_jobs ||--|| analysis_insights : produces
+ analysis_jobs ||--|| analysis_reports : produces
+ analysis_jobs ||--|| vibe_insights : produces
+ repos ||--o{ pull_requests : has
+
+ users {
+ uuid id PK
+ string email
+ timestamp created_at
+ }
+
+ repos {
+ uuid id PK
+ uuid user_id FK
+ string full_name
+ boolean is_private
+ timestamp last_analyzed_at
+ }
+
+ analysis_jobs {
+ uuid id PK
+ uuid user_id FK
+ uuid repo_id FK
+ string status
+ timestamp created_at
+ timestamp completed_at
+ }
+
+ analysis_metrics {
+ uuid id PK
+ uuid job_id FK
+ jsonb metrics_json
+ }
+
+ analysis_insights {
+ uuid id PK
+ uuid job_id FK
+ jsonb insights_json
+ }
+
+ vibe_insights {
+ uuid id PK
+ uuid job_id FK
+ jsonb axes
+ jsonb persona
+ jsonb cards
+ jsonb evidence_index
+ }
+
+ analysis_reports {
+ uuid id PK
+ uuid job_id FK
+ string vibe_type
+ string confidence
+ jsonb narrative
+ }
+
+ user_profiles {
+ uuid id PK
+ uuid user_id FK
+ jsonb aggregated_metrics
+ jsonb aggregated_axes
+ jsonb persona
+ jsonb narrative
+ int repos_analyzed
+ }
+
+ pull_requests {
+ uuid id PK
+ uuid repo_id FK
+ int github_pr_number
+ string title
+ boolean merged
+ string merge_method
+ timestamp created_at
+ }
+```
+
+---
+
+## Source Code References
+
+| Component | File | Key Functions |
+|-----------|------|---------------|
+| Metrics Computation | `packages/core/src/index.ts` | `computeAnalysisMetrics()` |
+| Commit Classification | `packages/core/src/index.ts` | `classifyCommit()`, `isAutomationCommit()` |
+| Insights Generation | `packages/core/src/index.ts` | `computeAnalysisInsights()` |
+| Vibe Axes | `packages/core/src/vibe.ts` | `computeVibeAxes()` |
+| Persona Detection | `packages/core/src/vibe.ts` | `detectVibePersona()` |
+| Episode Building | `packages/core/src/vibe.ts` | `buildWorkEpisodes()` |
+| Subsystem Classification | `packages/core/src/vibe.ts` | `classifySubsystem()` |
+| LLM Client | `packages/core/src/llm/` | `createLLMClient()` |
+| Profile Aggregation | `packages/core/src/index.ts` | `aggregateUserProfile()` |
+| Inngest Function | `apps/web/src/inngest/functions/analyze-repo.ts` | Main job processor |
+| Fallback Worker | `apps/worker/src/index.ts` | Self-hosted processor |
+
+---
+
+## Related Documentation
+
+- [How Vibe Coding Profile Works (Product Overview)](../how-vibed-works.md)
+- [Vibe Metrics v2](./vibe-metrics-v2.md) — Original axis design
+- [Inngest Integration](./inngest-integration.md) — Job processing details
+- [Architecture Overview](./overview.md) — System components
+- [PRD: Multi-Agent Detection](../prd/multi-agent-detection.md) — AI workflow signals
+
+---
+
+## Attribution
+
+This architecture builds on concepts and research from the developer tooling community:
+
+| Concept | Source |
+|---------|--------|
+| "Vibe coding" term | [Andrej Karpathy](https://x.com/karpathy/status/1886192184808149383) (Feb 2025) |
+| Orchestrator vs Conductor patterns | [Addy Osmani](https://addyosmani.com/blog/future-agentic-coding/) |
+| Code analytics methodologies | [GitClear](https://www.gitclear.com/) |
+| Co-author attribution patterns | [GitHub Copilot Docs](https://docs.github.com/en/copilot) |
+
+See [internal research docs](../research/) for persona taxonomy synthesis.
+
+---
+
+## Assumptions & Caveats
+
+1. **GitHub API Only:** Analysis is limited to what GitHub exposes (no IDE telemetry)
+2. **Commit Sampling:** Large repos are sampled; not all commits analyzed
+3. **Bot Filtering:** Heuristic-based; some edge cases may slip through
+4. **Persona Rules:** Deterministic rules may not capture all workflow styles
+5. **LLM Privacy:** Commit content never sent; only metadata and statistics
+6. **Aggregation Weights:** Larger repos have more influence on profile
+
+---
+
+*This document should be updated whenever analysis logic, data models, or algorithms change.*
diff --git a/docs/github-setup.md b/docs/github-setup.md
index 2bba8db..b2f1956 100644
--- a/docs/github-setup.md
+++ b/docs/github-setup.md
@@ -1,12 +1,12 @@
# GitHub Setup
-This document summarizes what you need to do before you can authenticate via GitHub and analyze repositories in Vibed Coding.
+This document summarizes what you need to do before you can authenticate via GitHub and analyze repositories in Vibe Coding Profile.
## 1. Create a GitHub OAuth App
1. Open GitHub → Settings → Developer settings → OAuth Apps → **New OAuth App**.
2. Fill in the required URLs for local development:
- - **Application name:** e.g., `Vibed Coding Local`
+ - **Application name:** e.g., `Vibe Coding Profile Local`
- **Homepage URL:** `http://localhost:8108`
- **Authorization callback URL:** `http://localhost:54421/auth/v1/callback`
3. Save the Client ID and Client Secret; you will store both in your local environment file (`.env.local`).
@@ -16,7 +16,7 @@ This document summarizes what you need to do before you can authenticate via Git
This project relies on Supabase Auth using GitHub OAuth (see `docs/PRD.md`).
1. Start your local Supabase stack if it is not running: `npm run supabase:start`.
-2. Open Supabase Studio at `http://127.0.0.1:54423` and navigate to **Authentication → Providers → GitHub**.
+2. Open Supabase Studio at `http://127.0.0.1:54323` and navigate to **Authentication → Providers → GitHub**.
3. Paste the Client ID and Client Secret from the OAuth app you created above.
4. Select scopes required for private repo access (at least `repo` and `read:user`).
@@ -49,7 +49,7 @@ When you create a new OAuth app or Supabase account (GitHub, Supabase service cr
| Integration | Field | Suggested value / tip |
|-------------|-------|-----------------------|
-| GitHub OAuth App | Application name | "Vibed Coding Local" or "Vibed Coding Dev" |
+| GitHub OAuth App | Application name | "Vibe Coding Profile Local" or "Vibe Coding Profile Dev" |
| | Homepage URL | `http://localhost:8108` (local) / production URL when ready |
| | Authorization callback URL | `http://localhost:54421/auth/v1/callback` |
| | Client ID / Client Secret | Store securely (env file, vault) |
diff --git a/docs/how-vibed-works.md b/docs/how-vibed-works.md
new file mode 100644
index 0000000..48ee1c3
--- /dev/null
+++ b/docs/how-vibed-works.md
@@ -0,0 +1,189 @@
+# How Vibe Coding Profile Works
+
+> **Last Updated:** January 2026
+> **Maintainer:** Update this document when analysis logic, personas, or metrics change.
+
+Vibe Coding Profile analyzes your git history to reveal your vibe coding/ AI Assisted Engineering style and patterns. Think of it as "Spotify Wrapped for vibe coders." We look at *how* you build, not *what* you build.
+
+Note: The term "vibe coding" can be polarizing, but it captures the cultural moment around AI-shaped development. We personally prefer "AI Assisted Engineering" because it’s more explicit about the role of AI. This is a playful side project, so we lean into the pop framing and keep it approachable for non-technical folks.
+
+---
+
+## The Big Picture
+
+```
+Your Git History → Analysis → Vibe Coding Profile (VCP)
+ ↓ ↓ ↓
+ Commits Metrics Persona + Insights
+ PRs Axes Narrative
+ Timing Scores Share Cards
+```
+
+**Key Principle:** Vibe Coding Profile is *observational, not judgmental*. We detect patterns, not quality. Every coding style has strengths.
+
+---
+
+## What We Analyze
+
+### Data Sources (All from GitHub API)
+
+| Source | What We Extract | Privacy Note |
+|--------|-----------------|--------------|
+| **Commits** | Timestamps, file counts, additions/deletions, message structure | We never store or analyze code content |
+| **Pull Requests** | Merge methods, checklists, issue links, templates | Body text parsed for structure only |
+| **File Paths** | Subsystem classification (ui, api, tests, etc.) | Paths only, no file contents |
+
+### What We Don't Access
+- Source code contents
+- Private comments or discussions
+- IDE activity or local history
+- Unpushed commits
+
+---
+
+## The Analysis Pipeline
+
+### Step 1: Fetch Commits
+We retrieve up to 300 commits from your repository via GitHub API, sampling evenly across the repo's lifetime to capture patterns from start to present.
+
+### Step 2: Filter Automation
+Bot commits (dependabot, renovate, release-please, etc.) are automatically filtered out so your analysis reflects *your* work, not automation.
+
+### Step 3: Compute Metrics
+We calculate 25+ metrics across several dimensions:
+
+**Volume & Timing**
+- Total commits, additions, deletions
+- Active days vs span days
+- Commit frequency patterns
+
+**Rhythm & Burstiness**
+- Hours between commits (median, p90)
+- Burstiness score (-1 = steady, +1 = bursty)
+- Peak coding hours and days
+
+**Commit Patterns**
+- Size distribution (p50, p90)
+- Conventional commit ratio
+- Fix-after-feature sequences
+
+**Build Categories**
+- Classification: feature, fix, test, docs, infra, refactor, etc.
+- Category distribution and first-occurrence order
+
+### Step 4: Compute Vibe Axes
+Six deterministic axes (0-100 scores) capture your workflow style:
+
+| Axis | What It Measures |
+|------|------------------|
+| **Automation Heaviness** | How "agentic" your workflow looks: large commits, chunky PRs |
+| **Guardrail Strength** | How much you stabilize with tests, CI, docs early |
+| **Iteration Loop Intensity** | How often you do rapid fix cycles |
+| **Planning Signal** | Structured commits, issue linking, docs-first patterns |
+| **Surface Area per Change** | How many subsystems you touch per work session |
+| **Shipping Rhythm** | Bursty vs steady shipping patterns |
+
+### Step 5: Detect Persona
+Based on your axes, we match you to one of 7 Vibe Personas:
+
+| Persona | Signature Pattern |
+|---------|-------------------|
+| **Vibe Prototyper** | High automation, rapid iteration, minimal guardrails |
+| **Test-First Validator** | Strong guardrails with automation |
+| **Spec-Driven Architect** | High planning signal, early guardrails |
+| **Agent Orchestrator** | Wide surface area, high automation |
+| **Hands-On Debugger** | Intense fix loops, fast shipping |
+| **Rapid Risk-Taker** | High automation, low guardrails, fast shipping |
+| **Reflective Balancer** | Balanced across all axes (fallback) |
+
+### Step 6: Generate Insights
+Deterministic insights are computed server-side:
+
+- **Longest Streak:** Consecutive days with commits
+- **Peak Window:** When you code most (mornings, afternoons, evenings, late nights)
+- **Chunkiness:** Slicer (focused), Mixer (balanced), or Chunker (wide scope)
+- **Tech Signals:** Keywords detected in commit messages
+- **Multi-Agent Signals:** Co-author trailers, AI attribution patterns
+
+### Step 7: LLM Narrative (Optional)
+If LLM is configured, we generate a human-readable narrative about your *engineering patterns*, never about what you built, only how you built it.
+
+**Privacy:** The LLM only sees metadata (timestamps, categories, metrics), never commit message content or code.
+
+---
+
+## Profile Aggregation
+
+### Single Repo Analysis
+One repo gives you a snapshot of your coding style for that project.
+
+### Multi-Repo Unified VCP
+When you analyze multiple repos, Vibe Coding Profile aggregates them into a single **Unified VCP**:
+
+```
+Repo A Analysis ─┐
+Repo B Analysis ─┼─→ Unified VCP
+Repo C Analysis ─┘
+```
+
+**Aggregation Logic:**
+- Metrics are weighted by commit count (larger repos contribute more)
+- Axes are averaged across repos
+- Persona is re-detected from aggregated axes
+- Confidence increases with more repos (3+ repos = stronger signal)
+
+---
+
+## Confidence Levels
+
+Every insight includes a confidence level:
+
+| Level | Meaning |
+|-------|---------|
+| **High** | 200+ commits or 15+ PRs, good data quality |
+| **Medium** | 80+ commits or 6+ PRs |
+| **Low** | Limited data, take insights with a grain of salt |
+
+---
+
+## What Vibe Coding Profile Is NOT
+
+- **Not a productivity tracker:** We don't measure "good" vs "bad"
+- **Not a code quality tool:** We don't analyze code, just patterns
+- **Not AI detection:** We detect workflow patterns, not AI usage
+- **Not surveillance:** You control what repos to analyze, data is yours
+
+---
+
+## Privacy Principles
+
+1. **No code access:** We only read metadata from GitHub API
+2. **No message content to LLM:** Commit messages are classified locally, never sent to AI
+3. **User-controlled:** You choose which repos to analyze
+4. **Deletable:** Disconnect a repo and all analysis data is removed
+
+---
+
+## Attribution
+
+Vibe Coding Profile builds on research and concepts from the developer tooling community:
+
+- **"Vibe coding"**: Term coined by [Andrej Karpathy](https://x.com/karpathy/status/1886192184808149383) in February 2025, later named [Collins Dictionary Word of the Year 2025](https://en.wikipedia.org/wiki/Vibe_coding).
+- **Orchestrator vs Conductor patterns**: From [Addy Osmani's work on agentic coding](https://addyosmani.com/blog/future-agentic-coding/).
+- **Code analytics research**: Informed by [GitClear's developer productivity studies](https://www.gitclear.com/).
+- **Persona taxonomy**: Original synthesis drawing from academic TDD research, [GitHub Copilot documentation](https://docs.github.com/en/copilot), and developer workflow studies.
+
+Our internal research documents are available in [docs/research/](./research/).
+
+---
+
+## Related Documentation
+
+- [Technical Architecture](./architecture/vibed-analysis-pipeline.md): Deep dive with Mermaid diagrams
+- [Vibe Metrics v2](./architecture/vibe-metrics-v2.md): Axis computation details
+- [PRD: Vibe Coding Profile Narrative Layer](./PRD-vibed.md): Product requirements
+- [PRD: Profile Aggregation](./PRD-profile-aggregation.md): Multi-repo aggregation
+
+---
+
+*This document should be updated whenever analysis logic, personas, or metrics change.*
diff --git a/docs/implementation-trackers/multi-agent-pr-metadata-ingestion.md b/docs/implementation-trackers/multi-agent-pr-metadata-ingestion.md
index e03e649..8204bc3 100644
--- a/docs/implementation-trackers/multi-agent-pr-metadata-ingestion.md
+++ b/docs/implementation-trackers/multi-agent-pr-metadata-ingestion.md
@@ -52,5 +52,5 @@ This tracker accompanies `docs/prd/multi-agent-detection.md` (Extension PRD: PR
**Task:** Validate PR ingestion, parsing, and scoring behavior.
**Deliverables:**
- [x] Unit tests for PR parsing (issue linking, checklist detection)
-- [x] Integration test against vibed-coding repo (detected 16 Claude co-authored commits)
+- [x] Integration test against vibe-coding-profile repo (detected 16 Claude co-authored commits)
**Depends on:** F2, F4, F5
diff --git a/docs/implementation-trackers/vibed.md b/docs/implementation-trackers/vibed.md
index e3dd2a2..cc7738f 100644
--- a/docs/implementation-trackers/vibed.md
+++ b/docs/implementation-trackers/vibed.md
@@ -1,54 +1,64 @@
-# Implementation Tracker: Vibed Narrative Layer
+# Implementation Tracker: Vibe Coding Profile Narrative Layer
## Context
-This tracker accompanies `docs/PRD-vibed.md`. It captures the discrete implementation tasks needed to move Bolokono from a metrics-heavy analysis tool to the Vibed narrative experience, covering schema, worker, API, UI, and sharing layers.
+This tracker accompanies `docs/PRD-vibed.md`. It captures the discrete implementation tasks needed to move from a metrics-heavy analysis tool to the Vibe Coding Profile narrative experience, covering schema, worker, API, UI, and sharing layers.
## Tracker
### F1. Insights & Persona Schema
-**Task:** Design `analysis_insights` (and related persona signals) to capture deterministic Vibed outputs.
+**Task:** Design `analysis_insights` (and related persona signals) to capture deterministic Vibe Coding Profile outputs.
**Deliverables:**
-- [ ] Schema defined (longest streak, weekday/time window, chunkiness, persona tag + confidence, tech signals, share template).
-- [ ] Migration file created for `analysis_insights`.
-- [ ] Story for persona evolution + deltas captured (per-job history).
+- [x] Schema defined (longest streak, weekday/time window, chunkiness, persona tag + confidence, tech signals, share template).
+- [x] Migration file created for `analysis_insights` (`0005_add_analysis_insights.sql`, `0006_enrich_analysis_insights.sql`).
+- [x] Vibe insights table added (`0010_add_vibe_insights.sql`).
+- [x] Story for persona evolution + deltas captured (per-job history via `vibe_insights`).
**Blocks:** F2
### F2. Worker & Narrative Pipeline
**Task:** Extend the worker to compute `insights_json`, persist to `analysis_insights`, and feed narratives.
**Deliverables:**
-- [ ] Insight computation logic implemented + unit tested (streak, persona, chunkiness, tech signal detection).
-- [ ] `analysis_insights` row inserted per job with persona/confidence + evidence.
-- [ ] Updated Narrative LLM prompt referencing insights explicitly.
-- [ ] Share-card payload generation (colors/text template) included in worker output.
+- [x] Insight computation logic implemented + unit tested (streak, persona, chunkiness, tech signal detection) in `@vibed/core`.
+- [x] `vibe_insights` row inserted per job with persona/confidence + evidence via Inngest function.
+- [x] Updated Narrative LLM prompt referencing insights explicitly.
+- [x] Share-card payload generation (colors/text template) included in worker output.
**Depends on:** F1
**Blocks:** F3, F4
### F3. API + Supabase Access
**Task:** Expose insights/share data through REST endpoints and Supabase RLS.
**Deliverables:**
-- [ ] `GET /api/analysis/:jobId/insights` returns `analysis_insights` + key metrics.
-- [ ] `GET /api/analysis/:jobId/report` includes persona badge, confidence, and share template.
-- [ ] (Optional) `POST /api/analysis/:jobId/share` triggers share card export or return template data.
-- [ ] Supabase policies mirror `analysis_reports` access.
+- [x] API endpoints expose insights + key metrics (`/api/analysis/[id]/*`).
+- [x] Report includes persona badge, confidence, and share template.
+- [x] Supabase policies mirror `analysis_reports` access.
**Depends on:** F2
**Blocks:** F4
-### F4. Vibed UX/UI + Share Experience
-**Task:** Build the new hero/data UI plus “Show me the data” deep dive and share cards.
+### F4. Vibe Coding Profile UX/UI + Share Experience
+**Task:** Build the new hero/data UI plus "Show me the data" deep dive and share cards.
**Deliverables:**
-- [ ] Vibed highlights layer (streak, rhythms, chunkiness, persona archetype line, confidence text).
-- [ ] Share section offering PNG/SVG export plus copyable summary, reusing precomputed templates.
-- [ ] Persona timeline/history component showing persona evolution per job/repo.
-- [ ] Deep dive panel linking to raw metrics/narrative/evidence and persona signals.
-- [ ] Light theme/sticker aesthetic consistent with Vibed marketing.
+- [x] Vibe Coding Profile highlights layer (streak, rhythms, chunkiness, persona archetype line, confidence text).
+- [x] Persona timeline/history component showing persona evolution per job/repo.
+- [x] Deep dive panel linking to raw metrics/narrative/evidence and persona signals.
+- [ ] Share section offering PNG/SVG export plus copyable summary (partially implemented).
+- [ ] Light theme/sticker aesthetic consistent with Vibe Coding Profile marketing (ongoing polish).
**Depends on:** F3
### F5. Reflection & Testing
**Task:** Ensure deterministic insight generation, persona detection, and share assets behave as expected.
**Deliverables:**
-- [ ] Unit tests covering streak, persona classification outcomes, and chunkiness thresholds.
+- [x] Unit tests covering streak, persona classification outcomes, and chunkiness thresholds (31+ tests in `@vibed/core`).
- [ ] Visual regression or storybook story for share cards.
-- [ ] Documented confidence thresholds + persona hints for designers.
-- [ ] Workflow documentation updated (`docs/research` links, PRD updates).
+- [x] Documented confidence thresholds + persona hints in `vibe.ts`.
+- [x] Workflow documentation updated (`docs/research` links, PRD updates).
**Depends on:** F2, F4
+## Progress Summary
+
+| Phase | Status | Notes |
+|-------|--------|-------|
+| F1. Insights & Persona Schema | ✅ Complete | Multiple migrations applied |
+| F2. Worker & Narrative Pipeline | ✅ Complete | Inngest + fallback worker |
+| F3. API + Supabase Access | ✅ Complete | RLS policies in place |
+| F4. Vibe Coding Profile UX/UI + Share Experience | 🟡 Mostly Complete | Share export needs polish |
+| F5. Reflection & Testing | 🟡 Mostly Complete | Visual regression pending |
+
diff --git a/docs/llm-setup.md b/docs/llm-setup.md
index 5804859..9bbc0b0 100644
--- a/docs/llm-setup.md
+++ b/docs/llm-setup.md
@@ -1,10 +1,10 @@
# LLM Setup Guide
-This guide explains how to configure LLM (Large Language Model) providers for Vibed Coding. LLM powers the AI-generated narrative reports that accompany commit analysis.
+This guide explains how to configure LLM (Large Language Model) providers for Vibe Coding Profile. LLM powers the AI-generated narrative reports that accompany commit analysis.
## Overview
-Vibed Coding supports three LLM providers:
+Vibe Coding Profile supports three LLM providers:
| Provider | Models | Best For |
|----------|--------|----------|
@@ -111,7 +111,7 @@ After the free analysis:
## Running Without LLM
-Vibed Coding works without any LLM configuration. When no LLM is available:
+Vibe Coding Profile works without any LLM configuration. When no LLM is available:
1. All narratives use the **metrics-based fallback** (no AI generation)
2. The UI shows "LLM narrative unavailable" with the reason
@@ -126,7 +126,7 @@ This is useful for:
## Processing Architecture
-Vibed Coding has two job processing paths with different LLM capabilities:
+Vibe Coding Profile has two job processing paths with different LLM capabilities:
### Inngest (Primary - Recommended)
@@ -173,7 +173,7 @@ A standalone background worker for self-hosted deployments without Inngest.
## Self-Hosting Checklist
-When self-hosting Vibed Coding, configure LLM based on your needs:
+When self-hosting Vibe Coding Profile, configure LLM based on your needs:
### Minimal (No LLM)
```bash
diff --git a/docs/prd-llm-provider-abstraction.md b/docs/prd-llm-provider-abstraction.md
index c7db301..631a75f 100644
--- a/docs/prd-llm-provider-abstraction.md
+++ b/docs/prd-llm-provider-abstraction.md
@@ -9,7 +9,7 @@
## Overview
-Vibed Coding uses LLM (currently Anthropic Claude) to generate narrative reports from commit analysis. Currently, the platform bears all LLM costs using a single hardcoded API key. This PRD introduces:
+Vibe Coding Profile uses LLM (currently Anthropic Claude) to generate narrative reports from commit analysis. Currently, the platform bears all LLM costs using a single hardcoded API key. This PRD introduces:
1. **Provider-agnostic LLM layer** - Support multiple LLM providers (Anthropic, OpenAI, etc.)
2. **User-provided API keys** - Users can supply their own keys to unlock LLM features
diff --git a/docs/prd-multi-platform-integration.md b/docs/prd-multi-platform-integration.md
index 2b244b0..b0a78f5 100644
--- a/docs/prd-multi-platform-integration.md
+++ b/docs/prd-multi-platform-integration.md
@@ -52,12 +52,12 @@ This PRD is designed to be implemented by an AI coding agent. Key principles:
## Overview
-Vibed Coding currently supports GitHub as the sole platform for authentication and repository analysis. This PRD outlines the integration of **GitLab** and **Bitbucket** as additional platforms, enabling users to:
+Vibe Coding Profile currently supports GitHub as the sole platform for authentication and repository analysis. This PRD outlines the integration of **GitLab** and **Bitbucket** as additional platforms, enabling users to:
1. Sign in via OAuth using any of the three platforms
2. Connect additional platforms as repo sources (independent of login)
3. Analyze repos from all connected platforms
-4. Maintain a single, unified Vibed profile aggregated across all platforms
+4. Maintain a single, unified VCP aggregated across all platforms
---
@@ -66,7 +66,7 @@ Vibed Coding currently supports GitHub as the sole platform for authentication a
| Goal | Description |
|------|-------------|
| **Broader reach** | Support developers who primarily use GitLab or Bitbucket |
-| **Unified identity** | One Vibed profile per user, regardless of where their code lives |
+| **Unified identity** | One Unified VCP per user, regardless of where their code lives |
| **Flexible connections** | Users can connect/disconnect platforms independently of their login method |
| **Consistent UX** | Same analysis flow and profile experience across all platforms |
@@ -100,7 +100,7 @@ Vibed Coding currently supports GitHub as the sole platform for authentication a
### Unified Profile
-8. **As a user**, my Vibed profile aggregates commits from all analyzed repos across all platforms.
+8. **As a user**, my Unified VCP aggregates commits from all analyzed repos across all platforms.
9. **As a user**, I can see per-repo breakdowns by platform in my analysis history.
@@ -302,7 +302,7 @@ GET /api/profile # Unified profile (all platforms)
3. Redirect to GitLab OAuth (with state containing user_id)
4. Callback validates state, confirms logged-in user
5. Check if GitLab account already linked to another user
- - If yes: error "This GitLab account is linked to another Vibed account"
+ - If yes: error "This GitLab account is linked to another Vibe Coding Profile account"
- If no: continue
6. Create `platforms` row (is_primary = false)
7. Redirect back to settings with success
@@ -327,7 +327,7 @@ GET /api/profile # Unified profile (all platforms)
```
┌─────────────────────────────────────┐
│ │
-│ Sign in to Vibed Coding │
+│ Sign in to Vibe Coding Profile │
│ │
│ ┌─────────────────────────────┐ │
│ │ Continue with GitHub │ │
@@ -377,7 +377,7 @@ GET /api/profile # Unified profile (all platforms)
│ [All] [GitHub (12)] [GitLab (3)] │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
-│ │ 🐙 devakone/vibed-coding [View vibe] │ │
+│ │ 🐙 devakone/vibe-coding-profile [View VCP] │ │
│ │ GitHub · Private · Updated Jan 18 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
@@ -433,7 +433,7 @@ The dashboard shows unified metrics with optional platform breakdown:
```
┌─────────────────────────────────────────────────────────┐
-│ Your Vibed Profile │
+│ Your Unified VCP │
│ │
│ Rapid Risk-Taker │
│ High confidence · 698 commits across 8 repos │
diff --git a/docs/prd/multi-agent-detection.md b/docs/prd/multi-agent-detection.md
index 6a2cdcf..958bf75 100644
--- a/docs/prd/multi-agent-detection.md
+++ b/docs/prd/multi-agent-detection.md
@@ -2,13 +2,13 @@
## Overview
-**Status:** Planning
+**Status:** Implemented (Phases 1-5 complete)
**Priority:** High
**Owner:** TBD
**Created:** January 2026
### Problem Statement
-Vibed currently has limited detection of multi-agent workflows and AI-assisted development patterns. As vibe coding becomes mainstream (25% of YC W25 batch has 95% AI-generated codebases), we need stronger signals to identify and characterize these workflows.
+Vibe Coding Profile currently has limited detection of multi-agent workflows and AI-assisted development patterns. As vibe coding becomes mainstream (25% of YC W25 batch has 95% AI-generated codebases), we need stronger signals to identify and characterize these workflows.
### Goal
Implement three high-ROI detection capabilities that require no new data ingestion:
diff --git a/docs/prd/share-experience-improvements.md b/docs/prd/share-experience-improvements.md
new file mode 100644
index 0000000..a259649
--- /dev/null
+++ b/docs/prd/share-experience-improvements.md
@@ -0,0 +1,617 @@
+# PRD: Share Experience & Metrics Surfacing Improvements
+
+**Status:** Draft
+**Author:** AI Assistant (with human direction)
+**Priority:** P0 (Core to product value)
+**Depends on:** Core analysis pipeline (complete), Profile aggregation (complete)
+
+---
+
+## Problem Statement
+
+Vibe Coding Profile generates rich insights but buries them. The share experience—our primary viral growth mechanism—is hidden behind a collapsible section, and the Unified VCP page has no share functionality at all.
+
+**Current issues:**
+
+1. **Share is hidden on repo analysis page** — buried in a `` section labeled "Share, timeline, and details"
+2. **No share on Unified VCP page** — profile aggregation is complete but share is "deferred to P7"
+3. **Rich metrics computed but not displayed** — multi-agent signals, tech signals, timing patterns are computed but invisible
+4. **LLM narratives unused on profile** — generated and stored but never rendered
+5. **Share card is an afterthought** — not the hero experience it should be
+
+**Why this matters:**
+
+The core loop is: Analyze → Discover → Share → Friend sees → Friend analyzes.
+
+If sharing is hard or hidden, the loop breaks. Every friction point in sharing costs us viral growth.
+
+---
+
+## Success Criteria
+
+| Metric | Current | Target |
+|--------|---------|--------|
+| Share action rate (repo analysis) | Unknown (hidden) | >25% of completed analyses |
+| Share action rate (profile) | 0% (no feature) | >15% of profile views |
+| Time from analysis complete → share | >60s (hunting for button) | <15s |
+| Share card visibility | Below fold, collapsed | Above fold, hero |
+
+---
+
+## Design Principles
+
+### 1. Share is the Product, Not a Feature
+
+The share card isn't something you do after viewing your analysis—it IS the analysis. The card should be designed first, then the rest of the page supports it.
+
+### 2. One Glance, One Story
+
+A share card must tell a complete story in 3 seconds. Persona + 3-4 key metrics + visual identity. No scrolling, no clicking, no reading paragraphs.
+
+### 3. Platform-Native Sharing
+
+Different platforms have different needs:
+- Twitter/X: Punchy text + link + optional image
+- LinkedIn: Professional framing + insight card
+- Instagram: Story-sized visual asset
+- Copy link: For Slack, Discord, direct messages
+
+### 4. LLM Enhances, Doesn't Gate
+
+Non-LLM users should get a great experience. LLM adds flavor but isn't required for a compelling share.
+
+---
+
+## Scope
+
+### In Scope
+
+1. **Repo Analysis Page (`/analysis/[jobId]`):**
+ - Move share card to hero position (above fold)
+ - Surface additional computed metrics
+ - Improve LLM vs non-LLM narrative handling
+
+2. **Unified VCP Page (`/` when authenticated):**
+ - Add share card for Unified VCP
+ - Surface LLM narrative if available
+ - Create cohesive single-card story
+
+3. **Share Components:**
+ - Extract reusable `` component
+ - Extract reusable `` component
+ - Consistent design language across both pages
+
+4. **Metrics Surfacing:**
+ - Multi-agent signals (when detected)
+ - Tech signals (top technologies)
+ - Enhanced timing patterns
+
+### Out of Scope
+
+- Public profile pages (future feature)
+- OG image generation API (future enhancement)
+- Team/organization profiles
+- Historical profile comparison
+
+---
+
+## Detailed Requirements
+
+### 1. Repo Analysis Page Improvements
+
+#### 1.1 Share Card as Hero
+
+**Current structure:**
+```
+[Persona Card] → [Metrics Grid] → [Axes] → [Narrative] → [Collapsible: Share, timeline...]
+```
+
+**New structure:**
+```
+[Share Card (hero, full-width, gradient)] → [Share Actions (prominent)] → [Metrics + Axes] → [Narrative] → [Details]
+```
+
+**Share Card Design:**
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ [Gradient background: persona colors] │ │
+│ │ │ │
+│ │ MY VIBE CODING STYLE │ │
+│ │ │ │
+│ │ 🎭 The Vibe Prototyper │ │
+│ │ "You build to think — code is your sketchpad" │ │
+│ │ │ │
+│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
+│ │ │ 23 days │ │ Friday │ │ Focused │ │ 2.1:1 │ │ │
+│ │ │ Streak │ │ 4-7pm │ │ Surgeon │ │ Build/Fix│ │ │
+│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
+│ │ │ │
+│ │ 847 commits · 42 active days · vibecoding.profile │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ [Copy text] [Copy link] [Download PNG ▾] [Twitter] [LinkedIn] │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+**Requirements:**
+- [ ] Share card is the first thing users see after analysis completes
+- [ ] Card is designed to look good when screenshotted
+- [ ] Share actions are immediately visible, not hidden
+- [ ] Download dropdown offers: OG (1200×630), Square (1080×1080), Story (1080×1920)
+
+#### 1.2 Additional Metrics to Surface
+
+Currently displayed: Streak, Peak Day, Focus, Build vs Fix, Scope
+
+**Add these when detected:**
+
+| Metric | When to Show | Display |
+|--------|--------------|---------|
+| Multi-agent signals | AI co-author count > 0 OR AI trailers detected | "🤖 AI-Assisted: 45% of commits show collaboration patterns" |
+| Tech signals | Top 3 tech terms detected | "Your stack: React, TypeScript, Supabase" |
+| Night owl / Early bird | Peak hour is before 9am or after 6pm | "🌙 Night Owl: 68% of commits after 6pm" |
+| Weekend warrior | >20% of commits on Sat/Sun | "Weekend coder: 24% of your commits" |
+| PR workflow style | When artifact traceability data exists | "Orchestrator: High PR coverage, structured workflow" |
+
+**Conditional display logic:**
+- Show max 2 additional signals beyond the core 5
+- Prioritize signals with strongest values
+- Only show if data quality is sufficient
+
+#### 1.3 LLM vs Non-LLM Narrative Handling
+
+**Current state:**
+- Shows "Generated with {model}" or "Generated from metrics"
+- Regenerate button available
+- Error message if LLM fails
+
+**Improved state:**
+
+For LLM narrative:
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ YOUR STORY [AI-generated ✨] │
+│ │
+│ "Your commits tell the story of a builder who thinks through │
+│ code. March was intense—23 consecutive days, mostly evenings. │
+│ You started with scaffolding and iterated rapidly..." │
+│ │
+│ [Regenerate] │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+For non-LLM narrative:
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ YOUR PATTERNS │
+│ │
+│ Based on 847 commits over 42 active days, you show strong │
+│ Prototyper tendencies. Your longest streak was 23 days in │
+│ March, and you're most productive on Friday afternoons. │
+│ │
+│ [✨ Get AI-enhanced story] ← Only if user hasn't opted in │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+**Requirements:**
+- [ ] Visual distinction between LLM and deterministic narratives
+- [ ] LLM badge is subtle but present ("AI-generated ✨")
+- [ ] Non-LLM users see CTA to opt in (if not already opted in)
+- [ ] Both narrative types are valuable and complete
+
+---
+
+### 2. Profile Page Share Card
+
+#### 2.1 Profile Share Card Design
+
+The profile page should have a share card that aggregates across all repos.
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ ┌─────────────────────────────────────────────────────────┐ │
+│ │ [Gradient background] │ │
+│ │ │ │
+│ │ MY UNIFIED VCP │ │
+│ │ │ │
+│ │ 🎭 The Vibe Prototyper │ │
+│ │ "You build to think — code is your sketchpad" │ │
+│ │ High confidence │ │
+│ │ │ │
+│ │ ┌──────────────────────────────────────────────────┐ │ │
+│ │ │ 5 repos · 2,341 commits · 78% clarity │ │ │
+│ │ └──────────────────────────────────────────────────┘ │ │
+│ │ │ │
+│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
+│ │ │ Automation│ │ Guardrails│ │ Rhythm │ │ │
+│ │ │ 72 │ │ 45 │ │ 68 │ │ │
+│ │ └──────────┘ └──────────┘ └──────────┘ │ │
+│ │ │ │
+│ │ vibecoding.profile │ │
+│ └─────────────────────────────────────────────────────────┘ │
+│ │
+│ [Copy text] [Copy link] [Download PNG ▾] [Twitter] [LinkedIn] │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+**Requirements:**
+- [ ] Profile page has a share card as prominent as repo analysis
+- [ ] Card includes: persona, tagline, confidence, repo count, commit count, top 3 axes
+- [ ] Same share actions as repo analysis page
+- [ ] Card is designed to look good when shared
+
+#### 2.2 Profile Narrative (LLM)
+
+**Current state:** LLM narratives are generated and stored in `user_profiles.narrative_json` but never displayed.
+
+**New state:**
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ INSIGHT [AI-generated ✨] │
+│ │
+│ "Across 5 projects, you consistently show Prototyper │
+│ patterns—but with a twist. On acme/dashboard you lean │
+│ heavily into rapid iteration, while on acme/api you show │
+│ more Guardian tendencies. Your style adapts to context." │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+**For non-LLM users:**
+Use the existing `generateCrossRepoInsight()` function but with improved copy.
+
+**Requirements:**
+- [ ] Fetch `narrative_json` from `user_profiles` if it exists
+- [ ] Display LLM narrative with badge if available
+- [ ] Fall back to deterministic insight if no LLM narrative
+- [ ] Offer opt-in CTA for non-LLM users
+
+---
+
+### 3. Reusable Share Components
+
+#### 3.1 `` Component
+
+A reusable component for both repo analysis and profile pages.
+
+```tsx
+interface ShareCardProps {
+ variant: 'repo' | 'profile';
+ persona: {
+ id: string;
+ label: string;
+ tagline: string;
+ confidence: string;
+ };
+ metrics: Array<{
+ label: string;
+ value: string;
+ }>;
+ footer: {
+ left: string; // e.g., "vibecoding.profile"
+ right: string; // e.g., "847 commits · 42 days"
+ };
+ colors: {
+ primary: string;
+ accent: string;
+ };
+ avatar?: string;
+}
+```
+
+**Requirements:**
+- [ ] Single component used by both pages
+- [ ] Accepts variant prop for minor layout differences
+- [ ] Gradient background using persona colors
+- [ ] Responsive design (works on mobile)
+- [ ] Exportable to PNG/SVG
+
+#### 3.2 `` Component
+
+Reusable share action bar.
+
+```tsx
+interface ShareActionsProps {
+ shareUrl: string;
+ shareText: string;
+ shareTemplate: ShareTemplate;
+ onDownload: (format: 'og' | 'square' | 'story') => void;
+}
+```
+
+**Requirements:**
+- [ ] Copy text button with feedback
+- [ ] Copy link button with feedback
+- [ ] Download dropdown (OG, Square, Story)
+- [ ] Social buttons: Twitter, LinkedIn, Facebook, WhatsApp, Reddit
+- [ ] Native share (on mobile)
+- [ ] Consistent styling across pages
+
+---
+
+### 4. Data Requirements
+
+#### 4.1 Repo Analysis Data
+
+Already available in `AnalysisClient.tsx`:
+- `wrapped.streak`, `wrapped.timing`, `wrapped.commits`, `wrapped.chunkiness`
+- `shareTemplate` from API
+- `metricsJson` with full metrics
+
+**Needs to be added to UI:**
+- Multi-agent signals from `insights_json.multi_agent_signals`
+- Tech signals from `insights_json.tech_signals`
+- Artifact traceability from `insights_json.artifact_traceability`
+
+#### 4.2 Profile Data
+
+Current fetch in `page.tsx`:
+```sql
+SELECT persona_name, persona_tagline, persona_confidence,
+ total_repos, total_commits, axes_json, repo_personas_json,
+ updated_at, job_ids
+FROM user_profiles
+WHERE user_id = $1
+```
+
+**Needs to be added:**
+```sql
+SELECT ..., narrative_json, llm_model, llm_key_source
+FROM user_profiles
+```
+
+---
+
+### 5. Share Template Enhancements
+
+#### 5.1 Profile Share Template
+
+Currently `share_template` only exists for repo analysis. Add profile equivalent.
+
+```typescript
+interface ProfileShareTemplate {
+ colors: {
+ primary: string;
+ accent: string;
+ };
+ headline: string; // "My Unified VCP"
+ personaLabel: string; // "The Vibe Prototyper"
+ tagline: string; // "You build to think..."
+ confidence: string; // "High confidence"
+ stats: {
+ repos: number;
+ commits: number;
+ clarity: number;
+ };
+ topAxes: Array<{
+ name: string;
+ score: number;
+ }>;
+}
+```
+
+#### 5.2 Share Image Generation
+
+Enhance `createShareSvg` to support profile cards:
+
+```typescript
+function createShareSvg(
+ template: ShareTemplate | ProfileShareTemplate,
+ format: ShareFormat,
+ variant: 'repo' | 'profile'
+): string
+```
+
+---
+
+## Implementation Plan
+
+### Phase 1: Share Card Promotion (Repo Analysis)
+
+**Goal:** Make share the hero on repo analysis page
+
+| Task | Effort | Priority |
+|------|--------|----------|
+| Extract `` component from inline code | M | P0 |
+| Extract `` component | S | P0 |
+| Move share card to top of results | S | P0 |
+| Make share actions visible by default (not collapsed) | S | P0 |
+| Update page layout hierarchy | M | P0 |
+
+**Deliverable:** Share card is first thing users see on analysis page
+
+### Phase 2: Additional Metrics Surfacing
+
+**Goal:** Show more of the computed insights
+
+| Task | Effort | Priority |
+|------|--------|----------|
+| Add multi-agent signals display | M | P1 |
+| Add tech signals display | S | P1 |
+| Add timing personality (night owl, etc.) | S | P1 |
+| Conditional display logic | M | P1 |
+
+**Deliverable:** Repo analysis shows 2-3 additional contextual signals
+
+### Phase 3: Profile Share Card
+
+**Goal:** Profile page has share experience parity with repo analysis
+
+| Task | Effort | Priority |
+|------|--------|----------|
+| Create profile share template type | S | P0 |
+| Add share card to profile page | M | P0 |
+| Fetch and display LLM narrative | M | P1 |
+| Profile share image generation | M | P1 |
+| Add share actions to profile | S | P0 |
+
+**Deliverable:** Profile page has shareable card with download/copy/social
+
+### Phase 4: LLM Narrative Polish
+
+**Goal:** Consistent, attractive LLM handling across both pages
+
+| Task | Effort | Priority |
+|------|--------|----------|
+| Visual differentiation for LLM vs deterministic | S | P1 |
+| Opt-in CTA for non-LLM users | S | P2 |
+| Improved non-LLM narrative copy | S | P2 |
+
+**Deliverable:** Both LLM and non-LLM users get polished narratives
+
+---
+
+## UI Mockups
+
+### Repo Analysis Page (New Layout)
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ ← Back to profile │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ ┌───────────────────────────────────────────────────────────┐ │
+│ │ [SHARE CARD - GRADIENT BACKGROUND] │ │
+│ │ │ │
+│ │ MY VIBE CODING STYLE [avatar if available] │ │
+│ │ │ │
+│ │ 🎭 The Vibe Prototyper │ │
+│ │ "You build to think — code is your sketchpad" │ │
+│ │ 78% confidence │ │
+│ │ │ │
+│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
+│ │ │23 days │ │ Friday │ │ Focused │ │ 2.1:1 │ │ │
+│ │ │ Streak │ │ 4-7pm │ │ Surgeon │ │Build/Fix│ │ │
+│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
+│ │ │ │
+│ │ acme/dashboard · 847 commits · vibed.coding │ │
+│ └───────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌───────────────────────────────────────────────────────────┐ │
+│ │ [📋 Copy] [🔗 Link] [⬇️ Download ▾] [𝕏] [in] [f] [Share] │ │
+│ └───────────────────────────────────────────────────────────┘ │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ ADDITIONAL SIGNALS │
+│ │
+│ 🤖 AI-Assisted: 45% of commits show AI collaboration │
+│ 🌙 Night Owl: 68% of commits after 6pm │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ YOUR AXES │
+│ [6 axis cards with scores] │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ YOUR STORY [AI-generated ✨] │
+│ [Narrative text] │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ ▸ Timeline, metrics, and details │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### Profile Page (New Layout)
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ YOUR UNIFIED VCP [Add repo] [VCPs] │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ ┌───────────────────────────────────────────────────────────┐ │
+│ │ [SHARE CARD - GRADIENT BACKGROUND] │ │
+│ │ │ │
+│ │ MY UNIFIED VCP │ │
+│ │ │ │
+│ │ 🎭 The Vibe Prototyper │ │
+│ │ "You build to think — code is your sketchpad" │ │
+│ │ High confidence │ │
+│ │ │ │
+│ │ 5 repos · 2,341 commits · 78% clarity │ │
+│ │ │ │
+│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
+│ │ │Automation│ │Guardrails│ │ Rhythm │ │ │
+│ │ │ 72 │ │ 45 │ │ 68 │ │ │
+│ │ └──────────┘ └──────────┘ └──────────┘ │ │
+│ │ │ │
+│ │ vibecoding.profile │ │
+│ └───────────────────────────────────────────────────────────┘ │
+│ │
+│ ┌───────────────────────────────────────────────────────────┐ │
+│ │ [📋 Copy] [🔗 Link] [⬇️ Download ▾] [𝕏] [in] [f] [Share] │ │
+│ └───────────────────────────────────────────────────────────┘ │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ INSIGHT [AI-generated ✨] │
+│ │
+│ "Across 5 projects, you consistently show Prototyper │
+│ patterns. On acme/dashboard you lean into rapid iteration, │
+│ while on acme/api you show more Guardian tendencies." │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ YOUR AXES │
+│ [6 axis cards with scores and bars] │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ YOUR REPOS │
+│ [Repo breakdown with per-repo personas] │
+│ │
+├─────────────────────────────────────────────────────────────────┤
+│ ▸ How we calculated this │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Open Questions
+
+1. **Should share card include repo name?** For repo analysis, showing the repo name makes the card less portable (user might not want to share which repo). Consider making it optional or using generic "my project" language.
+
+2. **Profile share: include top repo?** Should the profile share card mention the dominant repo, or stay fully aggregated?
+
+3. **Social platform priority?** Which platforms should be most prominent? Twitter/X and LinkedIn seem highest priority for developers.
+
+4. **Share tracking?** Should we track share actions for analytics? Need to balance insights with privacy.
+
+5. **Public profiles?** This PRD focuses on owner-facing share. Public profile pages (viewable without auth) are a future feature that would build on this work.
+
+---
+
+## Appendix: Current vs. Proposed Component Structure
+
+### Current (Inline)
+
+```
+apps/web/src/app/analysis/[jobId]/AnalysisClient.tsx
+├── Share card rendered inline (~100 lines)
+├── Share actions rendered inline (~150 lines)
+├── createShareSvg() function (~80 lines)
+└── All share state managed locally
+```
+
+### Proposed (Componentized)
+
+```
+apps/web/src/components/share/
+├── ShareCard.tsx # Reusable card component
+├── ShareActions.tsx # Share button bar
+├── ShareImageGenerator.ts # SVG/PNG generation
+├── types.ts # Shared types
+└── index.ts # Exports
+
+apps/web/src/app/analysis/[jobId]/AnalysisClient.tsx
+└── Uses and
+
+apps/web/src/app/page.tsx
+└── Uses and
+```
+
+---
+
+*This PRD focuses on the share experience layer. See `PRD.md` for core analysis and `PRD-vibed-ux.md` for overall UX direction.*
diff --git a/docs/research/ai-era-coding-personas.md b/docs/research/ai-era-coding-personas.md
index 243676b..1c00a68 100644
--- a/docs/research/ai-era-coding-personas.md
+++ b/docs/research/ai-era-coding-personas.md
@@ -32,7 +32,7 @@ The UI is doing interpretation work. This holds short-term but should move to th
3. **Tech signals** – Collect GitHub language breakdowns or extension counts (no file contents) so we can say “TypeScript-heavy” vs. “backend focus”.
4. **Craftsmanship language** – Focus on how work unfolds (stabilize before expand, revisit quickly after shipping) using fix-after-feature, inter-commit timing, and refactor density.
5. **Confidence language** – Each insight should include claim + confidence + evidence refs (`insight { claim, confidence, evidence_refs[] }`) to stay observational and probabilistic.
-6. **Theming** – Keep the playful “Vibed” theme for Wrapped/analysis while leaving other routes neutral; later consider toggles or recap pages.
+6. **Theming** – Keep the playful “Vibe Coding Profile” theme for Wrapped/analysis while leaving other routes neutral; later consider toggles or recap pages.
## Signals for Classification
@@ -42,7 +42,16 @@ The UI is doing interpretation work. This holds short-term but should move to th
## Research Sources
-Primary sources include Addy Osmani’s AI workflows, Cursor 2.0 docs, GitClear 2024 research, academic TDD benchmarks (arxiv 2025), Axur Engineering, humanwhocodes.com persona guides, Cursor/Replit/Qodo tooling guides, GitHub Copilot materials, and developer persona frameworks.
+Primary sources:
+
+- [Addy Osmani: The Future of Agentic Coding](https://addyosmani.com/blog/future-agentic-coding/) — Orchestrator vs conductor patterns
+- [Andrej Karpathy's "Vibe Coding" tweet](https://x.com/karpathy/status/1886192184808149383) — Origin of the term (Feb 2025)
+- [GitClear 2024 Code Quality Report](https://www.gitclear.com/) — Code churn and developer productivity metrics
+- [GitHub Copilot Documentation](https://docs.github.com/en/copilot) — Co-author attribution, agent workflows
+- [Cursor Documentation](https://cursor.com/docs) — AI-assisted development patterns
+- [Vibe Coding Wikipedia](https://en.wikipedia.org/wiki/Vibe_coding) — Collins Dictionary Word of the Year 2025
+- Academic TDD benchmarks (arxiv 2025)
+- Developer persona frameworks from humanwhocodes.com
## Final Thought
diff --git a/docs/research/multi-agent-vibe-coding-patterns.md b/docs/research/multi-agent-vibe-coding-patterns.md
index 920c68c..18f3db6 100644
--- a/docs/research/multi-agent-vibe-coding-patterns.md
+++ b/docs/research/multi-agent-vibe-coding-patterns.md
@@ -2,13 +2,13 @@
## Research Summary
**Date:** January 2026
-**Goal:** Document how Vibed currently detects “agentic” workflows, what multi-agent/worktree workflows look like in Git signals, and what’s missing to detect them reliably.
+**Goal:** Document how Vibe Coding Profile currently detects “agentic” workflows, what multi-agent/worktree workflows look like in Git signals, and what’s missing to detect them reliably.
-Multi-agent workflow (for Vibed) means: multiple autonomous tools/agents working in parallel with human steering via PR review, commit trailers, structured instructions, and/or branch management. It is adjacent to “automation” but not the same as bot maintenance (Dependabot/Renovate).
+Multi-agent workflow (for Vibe Coding Profile) means: multiple autonomous tools/agents working in parallel with human steering via PR review, commit trailers, structured instructions, and/or branch management. It is adjacent to “automation” but not the same as bot maintenance (Dependabot/Renovate).
---
-## 1) What Vibed Can Observe Today (Repo Audit)
+## 1) What Vibe Coding Profile Can Observe Today (Repo Audit)
### What data we actually ingest
@@ -54,7 +54,7 @@ Reference: [vibe-metrics-v2.md](file:///Users/devakone/Projects/vibed-codingn/do
## 2) Worktrees: What We Can and Cannot Detect
-**Hard limitation:** local Git worktrees are not visible to GitHub APIs. Worktree metadata lives in `.git/worktrees/*` locally, so Vibed cannot “see worktrees” for GitHub-hosted analysis unless we ingest local repo state (we do not).
+**Hard limitation:** local Git worktrees are not visible to GitHub APIs. Worktree metadata lives in `.git/worktrees/*` locally, so Vibe Coding Profile cannot “see worktrees” for GitHub-hosted analysis unless we ingest local repo state (we do not).
Practical implication: “worktree usage” must be inferred indirectly via GitHub-visible artifacts:
- branch naming conventions
@@ -143,7 +143,7 @@ Worktree workflows typically imply:
- frequent short-lived branches
- predictable prefixes like `copilot/`, `agent/`, `cursor/`, `claude/`, `wt/`
-GitHub exposes branch refs and PR metadata, but Vibed would need to fetch and persist those.
+GitHub exposes branch refs and PR metadata, but Vibe Coding Profile would need to fetch and persist those.
### C) Commit-to-PR mapping (enabler)
@@ -158,7 +158,7 @@ Reference architecture: [vibe-metrics-v2.md](file:///Users/devakone/Projects/vib
## 5) Patterns We’re Missing (Beyond Multi-Agent)
-These are “emerging or consolidating” Git-visible patterns that align with vibed-coding behaviors, and can be measured without reading code contents.
+These are “emerging or consolidating” Git-visible patterns that align with vibe-coding behaviors, and can be measured without reading code contents.
### A) Provenance and accountability conventions
- Git trailers indicating AI assistance or review structure (pair-programming, agent authorship, supervisor co-authorship).
diff --git a/docs/research/persona-archetypes.md b/docs/research/persona-archetypes.md
index bbc0fed..ad128d2 100644
--- a/docs/research/persona-archetypes.md
+++ b/docs/research/persona-archetypes.md
@@ -1,6 +1,6 @@
-
+
-# Vibed Persona Archetypes (Inclusive Pop Culture References)
+# Vibe Coding Profile Persona Archetypes (Inclusive Pop Culture References)
Each persona that Bolokono surfaces can borrow the vibe of a pop-culture figure who embodies the mindset without reinforcing a single demographic. These references keep the experience playful while remaining inclusive and respectful.
diff --git a/docs/research/persona-insight-synthesis.md b/docs/research/persona-insight-synthesis.md
index ea5fe73..e429de1 100644
--- a/docs/research/persona-insight-synthesis.md
+++ b/docs/research/persona-insight-synthesis.md
@@ -5,7 +5,7 @@
This document cross-references the Perplexity-based `AI-Era Coding Personas` research and the ChatGPT-derived `AI-Era Developer Personas` research. Together they provide:
1. A unified persona taxonomy for Bolokono.
-2. Product signals we want to surface in the “Vibed” narrative layer.
+2. Product signals we want to surface in the “Vibe Coding Profile” narrative layer.
3. Architectural guidance for what data to capture and how to compute metrics.
## Unified Persona Taxonomy
@@ -20,7 +20,7 @@ This document cross-references the Perplexity-based `AI-Era Coding Personas` res
| Infrastructure Architect | Perplexity | Governance docs, consistent patterns, cross-service rules | Use for compliance/craft insight layers. |
| Hands-On Debugger / Rapid Risk Taker | Perplexity / ChatGPT | Hotfix spikes, direct commits to main, odd-hour pushes | Show “fix ratio” vs “velocity” and confidence disclaimers. |
-## Product Insights (Vibed Layer)
+## Product Insights (Vibe Coding Profile Layer)
- **Narrative cards** should mention persona-aligned observations (e.g., “You lay down tests before code, which matches your Validator rhythm,” “Friday evening prototypes keep your creative streak alive”).
- **Confidence language** sourced from persona confidence levels—high-confidence personas can have bolder observations; medium ones are framed probabilistically.
@@ -45,4 +45,4 @@ Next steps (confirmed by both docs):
## Closing Thought
-Both research streams converge on the idea that Bolokono should celebrate diverse craftsmanship paths while grounding every claim in observable data. Implementing the data captures above keeps the product aligned with this vision—narrative cards for the Vibed layer, deep-dive evidence for trust, and architecture-level JSON that future APIs or share cards can reuse.
+Both research streams converge on the idea that Bolokono should celebrate diverse craftsmanship paths while grounding every claim in observable data. Implementing the data captures above keeps the product aligned with this vision—narrative cards for the Vibe Coding Profile layer, deep-dive evidence for trust, and architecture-level JSON that future APIs or share cards can reuse.
diff --git a/packages/db/src/database.types.ts b/packages/db/src/database.types.ts
index da50750..87580dd 100644
--- a/packages/db/src/database.types.ts
+++ b/packages/db/src/database.types.ts
@@ -374,23 +374,32 @@ export type Database = {
Row: {
created_at: string | null
id: string
+ llm_key_source: string | null
+ llm_model: string | null
profile_snapshot: Json
trigger_job_id: string | null
user_id: string
+ version_number: number | null
}
Insert: {
created_at?: string | null
id?: string
+ llm_key_source?: string | null
+ llm_model?: string | null
profile_snapshot: Json
trigger_job_id?: string | null
user_id: string
+ version_number?: number | null
}
Update: {
created_at?: string | null
id?: string
+ llm_key_source?: string | null
+ llm_model?: string | null
profile_snapshot?: Json
trigger_job_id?: string | null
user_id?: string
+ version_number?: number | null
}
Relationships: [
{
@@ -416,11 +425,15 @@ export type Database = {
created_at: string | null
id: string
job_ids: string[]
+ llm_key_source: string | null
+ llm_model: string | null
+ narrative_json: Json | null
persona_confidence: string
persona_id: string
persona_name: string
persona_score: number
persona_tagline: string | null
+ regenerating: boolean
repo_personas_json: Json
total_commits: number
total_repos: number
@@ -433,11 +446,15 @@ export type Database = {
created_at?: string | null
id?: string
job_ids?: string[]
+ llm_key_source?: string | null
+ llm_model?: string | null
+ narrative_json?: Json | null
persona_confidence: string
persona_id: string
persona_name: string
persona_score?: number
persona_tagline?: string | null
+ regenerating?: boolean
repo_personas_json?: Json
total_commits?: number
total_repos?: number
@@ -450,11 +467,15 @@ export type Database = {
created_at?: string | null
id?: string
job_ids?: string[]
+ llm_key_source?: string | null
+ llm_model?: string | null
+ narrative_json?: Json | null
persona_confidence?: string
persona_id?: string
persona_name?: string
persona_score?: number
persona_tagline?: string | null
+ regenerating?: boolean
repo_personas_json?: Json
total_commits?: number
total_repos?: number