Skip to content

Cleanup & Follow-ups โ€” 2026-05-14 โ€‹

Status: In progress Scope: Operational housekeeping and small follow-ups accumulated during the secret-manager / onboarding-docs work (#505, #518, #521).

This is a working agenda for today's session. Items get pruned as they ship, not just appended. Real-scope work (#342, #343, etc.) remains in GitHub Issues; this doc is for the in-between.


1. Recently shipped (context) โ€‹

PRTitleMerged
#505feat(env): GCP Secret Manager bootstrap + Cloud Functions secret-injection fix2026-05-11
#518feat(env): auto-fetch VITE_FIREBASE_* via firebase apps:sdkconfig2026-05-13
#521docs(onboarding): focus ENVIRONMENT_SETUP on new-dev day-one, archive provisioning2026-05-14

End state: new-dev onboarding is a 5-line procedure. Bootstrap pulls 25 values automatically; only 3 per-dev values are manual (GH_PAT, GITHUB_OWNER, optional ADC path).


2. In-flight on your side โ€‹

Main checkout is on claude/billing-admin-bq-wiring with apps/admin/src/admin/billing/Billing.jsx open โ€” not my work, staying out of the way unless asked.


3. Small follow-ups (no GH issue worth creating) โ€‹

Bundle these into one PR when picked up:

  • [x] OpenAI dead-code cleanup โ€” done in this PR

    • Deleted apps/web/src/__tests__/workflows/ai-issue-triage-mock.test.js
    • Deleted apps/web/src/__tests__/workflows/ai-issue-triage-real.test.js
    • Removed 'api.openai.com' from tooling/scripts/validate-headers.js EXCLUDED_DOMAINS
    • Deleted dead duplicate scripts/validate-headers.js
    • Updated validate-triage-consistency.js testFiles list
    • Removed test:workflows:real npm script (+ description)
    • Updated 2 docs (HEADERS_VALIDATION_IMPLEMENTATION.md, HEADERS_VALIDATION.md) to reference tooling/scripts/validate-headers.js
  • [x] Pre-existing CSP gap โ€” done in this PR

    • Added 'cloud.google.com' to tooling/scripts/validate-headers.js EXCLUDED_DOMAINS (matches the dash.cloudflare.com / mail.google.com pattern for doc-only references)
    • npm run validate:headers now passes locally
  • [x] Worktree disk cleanup โ€” partial (done in this session)

    • Removed .claude/worktrees/304-audit + branch copilot/add-secret-manager-layer (PR #505 merged)
    • Removed .claude/worktrees/340-version-sdk-to-ci + branch chore/refactor-prefix-labels (PR #495 merged)
    • Left .claude/worktrees/env-bootstrap-firebase in place โ€” this is the current working tree for #522
    • Left .claude/worktrees/privacy-audit-2026-05-11 alone โ€” not mine, looks like a separate session's WIP

4. Watch / decisions deferred โ€‹

  • Prod DISCORD_WEBHOOK_URL โ€” lantern-app-prod Secret Manager doesn't have this yet. Same fix pattern as dev: gcloud secrets create + remove the orphan plain env var from the createfeaturerequest Cloud Run service. Per operator preference, not asking about prod until full migration is initiated.
  • 8 newly-provisioned dev secrets mentioned rotation as a planned follow-up. No urgency.
  • Pre-existing service-API CI gap โ€” services/api/* vitest suites only run via local npm run validate, not in CI. Surfaced during the validate audit; not on critical path yet.

5. Bundled into this PR โ€‹

  • [x] #342 admin stale-bundle bug โ€” done in this PR
    • Diagnosis: admin isn't a PWA (no service worker). The "stale bundle" is a generic SPA freshness issue โ€” open tabs hold the loaded JS in memory and never re-fetch HTML on client-side navigation.
    • Fix: poll /version.json every 60s + on visibility change. Surface a "new version available" banner when buildId changes. User clicks Reload (no auto-reload, persistent banner โ€” matches operator-tool ergonomics).
    • Files: tooling/scripts/generate-version.mjs (added --out=<path> flag so admin can share the existing generator), apps/admin/package.json predev/prebuild chains the generator, apps/admin/public/_headers adds no-cache for /version.json, apps/admin/src/lib/versionCheck.js (new), apps/admin/src/shared/components/NewVersionBanner.jsx (new), wired into apps/admin/src/App.jsx, .gitignore excludes apps/admin/public/version.json.
    • Verified: admin build runs prebuild, generates the version stamp, vite bundle succeeds.

6. #343 surgical fix (de-scoped) โ€‹

  • [x] #343 surgical subset โ€” done in this PR
    • Re-audited #343 against current dev: bootstrap reliably populates .env.local so the "missing env var" risk is mostly gone in dev. Hardcoded Cloud Run dev-URL fallbacks (analyticsApi/docsApi/merchantsApi/assistantApi) still work. venueApiClient.js already throws loudly when env var missing.
    • One real silent landmine: flash.js was logging via devLog.warn (dev-only), so a missing VITE_ANALYTICS_API_URL in prod = events silently discarded with zero console output. Fixed: introduced a warnAnalyticsDisabled() helper that logs once via console.error unconditionally. Touches the 2 call sites (flushBuffer, init). The 3rd site (_loadDynamicEvents) intentionally stays silent โ€” it's a one-time conditional load gate, not an event-dropping path.
    • Deferred: the full registry-based getApiBaseUrl() helper. Its value was mostly prod-misconfig defense, and the operator is staying on dev. Pick up when prod migration starts.

7. #351 openapi drift audit โ€” partial fix โ€‹

  • [x] Service inventory check โ€” confirmed clean.

    • 7 HTTP API services tracked everywhere: packages/shared/services/index.js (API_SERVICES registry), services/api/* dirs, openapi.json per service, deploy-dev.yml jobs. All match. No orphans, no missing entries.
    • Other services in services/: 1 Discord bot, 1 Dataform project, 1 Firebase Functions package, 3 Cloud Run Jobs (cloudflare/github/invoices ingest). Correctly NOT in API_SERVICES (none are HTTP APIs).
  • [x] Linter enhancements โ€” done in this PR

    • Paren-aware app.use parsing: the prior regex [^)]* couldn't span middleware function calls (e.g. requireRole('admin') โ€” the inner ) broke the match), so mount prefixes were never captured when middleware was chained. New paren-balanced scan (findAppUseMounts) handles arbitrary middleware including nested parens, strings, brackets.
    • normalizePath extended: Express splat (*name) now normalizes to {param} same as Express :name and OpenAPI {name}. Resolves docs-api drift end-to-end.
  • [x] 3 services moved to enforced: docs, assistant, venues. Previously reported false-positive drift; now correctly report OK (enforced).

  • [ ] Out of scope (separate effort):

    • analytics-api: 7 real undocumented endpoints in the impl (bq-export-status, bq-schema, trigger/*, taxonomy, events/custom, scheduled/pseudonymize-user-ids). Needs actual spec authoring.
    • auth-api: uses inline express.Router() dispatch in index.js (adminDispatch.post('/signin', ..., adminAuthRoutes)) which the linter doesn't follow. Either teach the linter that pattern or refactor auth's mount style. Documented in the ENFORCED_SERVICES comment.

8. Ratchet + pre-push hook โ€” drift can't grow โ€‹

  • [x] Ratchet baseline โ€” done in this PR

    • New tooling/scripts/openapi-sync-baseline.json freezes the current drift on analytics (7 entries) and auth (53 entries). Anything beyond this list fails the linter.
    • Linter behavior: enforced services fail on any drift; non-enforced services pass if drift โІ baseline, fail if drift grows beyond baseline. Also nudges to tighten the baseline when drift shrinks.
    • npm run openapi-sync:update-baseline โ€” regenerates the file from current state. Use after intentional cleanup or after adding a route + spec entry in tandem.
  • [x] Husky pre-push hook โ€” done in this PR

    • .husky/pre-push runs npm run validate -- --scope openapi,format (~8s) before any git push. Catches openapi drift + format issues locally before they hit the remote, no CI minutes burned.
    • Bypass mechanisms (documented in the hook itself):
      • git push --no-verify โ€” one-shot, built-in
      • LANTERN_SKIP_PREPUSH=1 git push โ€” one-shot via env var (more visible)
      • mv .husky/pre-push .husky/pre-push.disabled โ€” permanent disable
    • Setup is automatic: npm install runs prepare script which installs husky's hook wiring. New devs and new worktrees both work out of the box.

9. #361 CSS tokens consolidation + web dedupe โ€” done in this PR โ€‹

  • [x] Token drift detected + handled

    • Removed from admin's :root (values matched shared theme exactly): --success, --danger, --info, --warning, --accent-500, --accent-600, --accent-700, --accent-foreground, --radius, --radius-sm, --radius-md, --radius-lg, --radius-xl, --glass-gradient.
    • Kept as intentional dark-theme overrides with inline comments: --accent-50: #ffedd5 (theme.css ships #fff7ed โ€” too light on admin's pure-black bg) and --accent-100: #fee2b0 (theme.css ships #ffedd5). Same rationale: light accents need saturation to stay visible against pure black.
    • Re-pointed legacy aliases: --radius-1..4 now resolve to var(--radius-sm/md/lg/xl) so they can't drift from the canonical scale.
    • Verified admin + web both build clean.
  • [x] Token policy comment added at the top of apps/admin/src/shared/styles/styles.css per the issue's follow-up, documenting which tokens belong in shared theme vs. local.

  • [x] Web dedupe (extension of #361): post-merge audit surfaced that the issue's claim "web was already deduped" was outdated โ€” web's styles.css still had 10 duplicate token declarations (--success, --danger, --info, --radius, --glass-gradient, --accent-50, --accent-100, --accent-500, --accent-700, --accent-foreground) matching packages/ui/theme.css exactly. All removed. Web now inherits them from shared theme.

  • [x] Theme palette blocks preserved: web's html[data-theme='warm'/'cool'] and html[data-scheme='light'/'dark'] blocks legitimately override shared tokens when a theme/scheme attr is set on <html>. Those stay โ€” they're intentional, not duplicates.

  • [x] Canonical policy documented in packages/ui/theme.css: the file header now spells out the full workflow โ€” what belongs shared vs. local, how to add new tokens, how to declare intentional overrides, etc. Both apps' styles.css have shorter pointer-comments referencing back to it.

10. Shortlist now empty โ€” no outstanding "easy picks" โ€‹

(Bigger items deferred: tiered auth recovery, Node 24 migration, OfferCards extraction, analytics-api spec authoring, auth-api spec refactor. None are pull-it-off-the-shelf small.)


11. Conventions learned this cycle (now in memory) โ€‹

These live in user-level Claude Code auto-memory at ~/.claude/projects/-home-mechelle-repos-lantern-app/memory/ (not checked into this repo). Names listed here for grepability:

  • feedback_open_prs_as_drafts โ€” gh pr create --draft always; user flips ready when she wants CI to run.
  • feedback_bundle_related_work_into_fewer_prs โ€” drift-repair, label fixes, follow-up cleanups all fold into the same branch unless there's a real reviewability/risk reason to split.

Built with VitePress