the build, in depth

The stack, briefly

For anyone who wants the headline before the internals.

Framework
SvelteKit 2 on Svelte 5, written with runes throughout.
Language
TypeScript in strict mode. No any; unknown when honest.
Build
Vite, prerendered to static HTML, deployed on Vercel (nodejs22.x).
Content
Typed TypeScript objects. No CMS, no markdown, no database.
Metrics
Drift: a decoupled git-metrics engine with a JSON Schema output contract, running on Bun.
Colour
Reasonable Colors, behind semantic tokens, with a no-flash dark theme.

Drift

bespoke tooling

Commit counts and churn figures are the easiest things on a portfolio to quietly inflate. So I do not write them. A bespoke CLI measures them against a schema that decides what it is allowed to say.

The split

Two things with a contract between them. The engine measures; the integration layer presents.

Drift started as a single script that did everything: walked the repos, measured them, wrote the manifest and understood how the site would render every figure. Measurement was tangled with presentation, so neither could move without the other.

It is now two things with a contract between them. The engine (scripts/check-drift.js) is a framework-agnostic Bun script: it fingerprints repos, owns the four data files, and knows nothing about Svelte. The integration layer (src/lib/data/) is build-time SvelteKit code: it reads those files as static JSON imports and assembles the typed Project objects the site is built from. The engine could be lifted out as a standalone package and nothing on the site would notice.

Core engine scripts/check-drift.js Framework-agnostic. Fingerprints repos, owns the data files, knows nothing about Svelte.
Schema contract scripts/sources.schema.json JSON Schema draft-07, additionalProperties: false. The engine validates every record before writing.
Integration layer src/lib/data/ Build-time registry. Reads files as static JSON, assembles typed Project objects for the site.
The engine owns measurement; the integration layer owns presentation. The schema is the seam.
Before
check-drift.js
  • fingerprint repos
  • write manifest
  • render output
  • know the site's data shape
After
engine
  • fingerprint repos
  • write manifest
integration
  • assemble Projects
  • render output

The contract

JSON Schema draft-07 with additionalProperties: false. A violation throws and writes nothing.

Between the engine and the integration layer sits sources.schema.json: a JSON Schema draft-07 definition with additionalProperties: false. The engine validates every assembled record against it before writing anything. A violation is a programming error in the engine, not a user-data problem, so the response is blunt: throw, write nothing. A half-correct manifest never reaches disk.

This makes adding a new metric a deliberate three-step act. Declare the property in the schema. Add it to the SyncedSource interface in index.ts. Return it from getFingerprint in the engine. Miss one and the build tells you, either at bun run check or when the engine throws on its next sync. The boundary is not a convention I am trusting myself to respect; it is enforced.

// Validate the fully-assembled manifest against the engine's public schema
// before the single sanctioned write. A violation is a programming error:
// throw and write nothing (fail-closed).
const violations = validateManifest(manifest);
if (violations.length > 0) {
	for (const v of violations) {
		process.stderr.write(`drift: schema violation: ${v}\n`);
	}
	throw new Error(
		`sources.json failed validation (${violations.length}); nothing written.`
	);
}

writeJson(sourcesPath, manifest); // the only write to sources.json
scripts/check-drift.js: validation gate

Measurement

Every figure is measured against the canonical commit, not the working tree, via a bounded concurrent worker pool.

For each repo, getFingerprint fans a set of independent git calls out via Promise.all against the resolved default branch, not whatever happens to be checked out locally. defaultBranch resolves origin/HEAD, then main, then master, falling back to bare HEAD only when none of those exist. The resolved ref is recorded as measuredRef in the manifest, excluded from drift comparisons via DRIFT_SKIP_FIELDS, so a branch rename never registers as drift.

Lines of code and languages are read straight from git blobs via git cat-file --batch, not the working tree, so the measurement is always against the canonical commit. Repos run concurrently across a bounded worker pool (cpus().length slots). A HEAD-plus-TTL cache, keyed on the measured commit's SHA and gitignored, means an unchanged repo is not re-scanned. drift sync and --no-cache bypass it.

Per repo, the fingerprint covers: commit counts on two axes (mine versus all authors, lifetime versus trailing four weeks); line churn on the same axes; lines of code; languages by file count; first and last commit dates; and the runtime, framework and database inferred from manifest files.

{
	"chirpdb": {
		"commitHead": "7c94461",
		"commitsAny": 1202,
		"commitsAnyRecent": 624,
		"commitsMe": 585,
		"commitsMeRecent": 329,
		"commitAnyLast": "2026-08-11",
		"commitAnyRoot": "2026-06-08",
		"detectedLanguages": [
			"Python",
			"SQL",
			"Shell",
			"JavaScript"
		],
		"linesAny": 48255,
		"linesMeAdded": 130538,
		"linesMeRemoved": 82410,
		"linesAnyAdded": 199667,
		"linesAnyRemoved": 107813,
		"linesMeAddedRecent": 55875,
		"linesMeRemovedRecent": 31046,
		"linesAnyAddedRecent": 84544,
		"linesAnyRemovedRecent": 37916,
		"urlRepo": "https://github.com/ZigZag-Technology/CHIRPdb",
		"detectedRuntime": [
			"python"
		],
		"detectedFramework": [
			"fastapi"
		],
		"measuredRef": "main",
		"detectedDatabase": [
			"supabase-py",
			"supabase-postgres"
		],
		"detectedTechFirstSeen": {
			"python": "2026-02-23",
			"fastapi": "2026-02-23"
		},
		"commitsHuman": 1202,
		"authorsDistinct": 8,
		"authorsDistinctHuman": 8,
		"commitMeRoot": false,
		"commitMeLast": "2026-08-11",
		"spanMonthsActive": 3,
		"spanMonthsAll": 3,
		"spanGapMaxDays": 4
	}
}
src/lib/data/sources.json: one entry, every field a measurement

The staging pipeline

In-flight work surfaces on the site before it merges, via a self-healing three-tier precedence chain.

Work that is still on an unmerged branch has no entry in sources.json yet, but it can still surface on the site. A committed in-progress.json holds provisional metrics for in-flight projects: the branch name, a promotion pipeline (ordered merge targets), a visibility flag ('public' surfaces on the site; 'local' stays in the CLI), and per-field tracked values with their baseOnMain counterpart for context.

The integration layer's withSyncedMetrics applies a three-tier precedence across every metric field. Manual overrides win; real synced figures come next; provisional values from in-progress.json are the floor. Once a branch lands and drift sync picks up real numbers, the synced value naturally shadows the provisional one. Promotion is self-healing: no stale figures leak through.

// Precedence: override > synced > provisional.
// prov(field) returns the in-progress tracked value, or undefined.
const prov = (field: keyof ProjectMetrics) =>
	provisional?.tracked?.[field]?.value;

commitsMe:
	ov?.commitsMe?.value ?? synced?.commitsMe ?? prov('commitsMe'),
linesAny:
	ov?.linesAny?.value ?? synced?.linesAny ?? prov('linesAny'),
// ...every metric field follows the same three-tier chain

// Scope stays honest: commitsAny is always all-authors and commitsMe is
// always Jason, whatever the project's role. The role-keyed figure the
// page actually shows is a separate field, so nothing reading a scoped
// fact silently gets the other scope's number.
commitsHeadline: isSolo ? synced?.commitsAny : synced?.commitsMe,
commitsHeadlineScope: isSolo ? 'any' : 'me',
src/lib/data/index.ts: metric precedence chain

The verbs

Each write verb touches exactly one file. Read-only verbs touch nothing at all.

VerbDoesWrites
reportField-level drift for repos whose HEAD moved. --full diffs all; --check exits non-zero; --json for scripts.nothing
snapshotEvery current metric for every repo, one card per project.nothing
syncThe one sanctioned write to the manifest. Backfills all resolvable repos, bypasses the cache, schema-gated.sources.json
keep / keep-allRefresh the baseline behind a manual override without discarding the override value.overrides.json
hideDrop a repo from the public site.excluded.json
promoteGraduate in-flight work off an unmerged branch into the staging pipeline.in-progress.json
authorScaffold projects/<slug>.ts from a commented template if absent, then open in $EDITOR.projects/<slug>.ts
flagflag <slug> --pin | --hide. Set a curation flag in the overlay via TypeScript compiler-API splice.projects/<slug>.ts
auditEditorial-depth scoring (Full / Partial / Thin) across all overlays. Recomputes from live files.nothing
initScaffold the per-machine config files. Interactive prompts when a TTY is present.config files
helpPer-verb help, rendered in gum-formatted markdown.nothing

Every figure you see on this site is a measurement from that manifest, validated against a schema.

Credits

Type
Source Serif 4 for display, IBM Plex Sans for prose, JetBrains Mono for the apparatus; the same three set the OG cards, where a project's data model picks the name's face.
Colour
Reasonable Colors, mapped to semantic aliases.
Hosting
Vercel, from a single static build.
Source
All of it is on GitHub, including this page.