Shipped (2026-05-29, commits d080a3d5 + 50aafb76).
The persistent "Getting Started" widget renders at the top of the main feed column on every /feed view. It tracks the five steps from the OP, awards 50 credits per step (250 total), and hides itself when all five are complete or when the user clicks Dismiss.
Steps tracked (lazy detection on render — no EventBus hooks needed):
profile_completed — users.bio + users.profile_photo both non-empty.first_post — any non-deleted forum_posts row.community_joined — any forum_subscriptions row.friend_added — any friends row with status='accepted' (either direction).photo_uploaded — any photo_album_items row uploaded by the user.
Idempotent credits. The row carries a credits_awarded_mask bitmap (bit 1, 2, 4, 8, 16) so a step that is re-detected never double-awards. Awards write directly to user_credits + credit_transactions with source_type='onboarding_step'.
UI. Progress bar at the top, 5 rows below. Done rows show "+50 credits awarded" in green and strike through. Pending rows link to the relevant page (/profile/edit, /feed, /forums, /friends, /photos). Per-theme overrides for dark / crt-green / vt100.
Schema. New per-tenant table onboarding_progress (one row per user, lazily inserted on first render). Migration 2026-05-29-onboarding-progress.sql applied to all 3 tenants; inlined into schema.sql for fresh provisions.
Endpoint. POST /onboarding/dismiss → AccountController@dismissOnboarding (CSRF-validated, same-origin Referer guard, sets dismissed_at = NOW()).
Live verification on support tenant for user 2: 5/5 steps detected from history, 5 credit-transaction rows (50 each) written, credits_awarded_mask = 31, widget auto-hides because all_done = true.