Build Log
Build Log Ingest API — All 5 Charge Phases
August 2, 2026
Implemented the full v9-approved Build Log Ingest API design end to end: a shared contract package, forgekit-website's ingest endpoints, Alloy's delivery outbox, a real backfill of all 210 historical sessions, and the database-backed read-path cutover — so a Bank-closed Arc appears on forgekits.build/build-log with zero redeploy.
Timeline
What shipped
packages/forgekit-retro-contract — public-retro.schema.ts, ingest-request/response schemas, project-public-retro.ts (fail-closed projector), content-hash.ts (canonical semantic hash), 12 tests
A dedicated Neon Postgres project for forgekit-website (id shy-thunder-87278705), separate from Alloy's own database per design §14
forgekit-website's ingest API: 6 DB tables (publishers, publisher_api_keys, retro_sessions, retro_session_revisions, retro_ingest_receipts, retro_ingest_security_events), 5 route handlers, hashed API-key auth with a non-reversible credentialFingerprint, rate limiting via forgekit-ai's createRateLimiter
Alloy's retro-publication-outbox.ts — the exact 12-step sequence from design §6, wired into bankArc as an additive step after the existing local retro pipeline
forgekit-os/scripts/build-log-phase0-scan.mjs, backfill-retro-sessions.mjs, backfill-exception-manifest.mjs, verify-no-content-regression.mjs — real, re-runnable migration tooling, not one-off scripts
The database-backed /build-log index (app/build-log/page.tsx, rewritten) and new /build-log/[slug] detail route
Retired build-session.mjs's updateIndex() source-text-splice mechanism and its public static HTML write, keeping only the internal full-chapter HTML generation
forgekit-website/lib/retro-ingest/read.test.ts — the Phase 5 regression test Zeb explicitly asked for after the manual verification pass, proving withdrawn-session exclusion and legacy-file preservation as committed coverage
Fixed a real forgekit-website/.gitignore gap found during commit staging — .env* was blanket-ignoring .env.example too, unlike every other app's .gitignore (which has an explicit !.env.example exception)