Build Log
Closing the duplication sweep — and finding Hearth's real severity
July 17, 2026
Fixed both wiring gaps and extracted the two threshold-crossed patterns (org-auth, rate limiter) from last session's sweep, then investigated Hearth's flagged rate-limiting gap and found it was a real cross-tenant IDOR affecting ~26 files, not a missing rate limiter.
Timeline
What shipped
Known's lib/db/index.ts now imports createDb from forgekit-core/db instead of duplicating the lazy-proxy pattern
Leashline's Button/Card/Badge now re-export from forgekit-ui instead of carrying byte-identical local copies
packages/forgekit-core/src/org-auth.ts — requireOrgId(redirectTo) + requireOrgIdApi(), extracted from Forge's getTenantId() and Known's requireOrgId()/requireOrgIdApi(), built to Known's superset shape. Tested: 5 passing tests.
packages/forgekit-ai/ — first real module in a previously-empty planned package. createRateLimiter(windowMs?, max?) factory extracted from 3 real duplicate implementations. Tested: 5 passing tests.
PilotLight and Happenstance added to root package.json workspaces (were standalone Vercel projects outside the monorepo build) — required to safely consume forgekit-ai without breaking their production builds
Stray per-app package-lock.json files removed from PilotLight and Happenstance now that root lockfile governs them
products/hearth/app/(dashboard)/week/actions.ts fixed by hand as the reference pattern — moveWeekItem, markWeekItemDone, hideFromLookahead, updateWeekItem, addToTodayRoute all had zero or hardcoded-only household scoping
products/hearth/lib/calendar-token-manager.ts — getValidAccessToken() changed from a zero-arg function using a hardcoded constant to taking householdId as a required parameter
24 additional Hearth files migrated from HOUSEHOLD_ID = 'household-1' to real Clerk-derived household scoping via lib/access.ts's getHouseholdForUser()
9 additional zero-ownership-check bugs found and fixed beyond the simple constant swap, including a cross-household data-loss bug (meals/grocery/route.ts was deleting every household's grocery items for a week, not just the caller's) and a client-supplied-householdId IDOR in curriculum/import/route.ts
2 more household-1 literals found and fixed after the agent's pass (GroceryList.tsx, RecipeBook.tsx — client-side optimistic-UI placeholders, not a real security gap but worth correcting)
forgekit-os/patterns.md and extraction-backlog.md updated with grep evidence for every status change — npm run verify:reuse shows 0 broken claims across all 53 registry rows
“ok. all good for now. where are we at with the duplication check. is that all resolved?”