CreatorTrack Studio , Phase 1: Lock & Document the Foundation¶
URL: https://mkdocs.justinsforge.com/memory/handoffs/creatortrack-studio-phase1-foundation-2026-06-18/
Spawned by: home-base session, 2026-06-18, at Justin's request.
Model: Opus 4.8, medium thinking (set thinking to medium; this is audit + convention work, not deep architecture, so don't burn max-think).
Repo: /home/justinwieb/forge-suite (dev at dev.creatortrack.ai, local Next dev server).
Why this exists¶
Justin is about to fan out MULTIPLE parallel workers to build the component library (menus, popovers, scrollbars, pages, etc.) and wire them into dev.creatortrack. Parallel component work only stays collision-free if the foundation is frozen first. That is this job. You are NOT building feature components. You are locking and documenting the ground they stand on.
Required reading (scope)¶
forge/memory/general/reference_suite_ui_kit.md, THE canonical spec for the UI kit, Studio, token store, Showcase, and the existing component inventory. Most of the foundation is ALREADY BUILT (theme.css tokens,/studioharness with Components/Preview/Inspect/Pages tabs,TokenStudioProvidertoken store,Showcasepreview+controls frame,components/uiprimitives). Treat it as ground truth; verify against the actual repo, fix the doc if reality differs.- The repo itself:
app/theme.css,components/ui/,components/studio/,components/kit/,app/studio/page.tsx.
Phase 1 deliverables (in order)¶
-
Audit & freeze the design tokens. Read
app/theme.css. Confirm the token set is complete enough that the Phase 2 components (menus, popovers/modals, scrollbars, pages/layout) can be built using ONLYvar(--token)with zero hardcoded hex/px-that-should-be-a-token. Add any missing tokens (sourced from the Flask measured values in the reference doc). This file is the single retheme knob , get it right, then declare it frozen. -
Verify & document the component-registration path. Trace exactly how a new component gets into Studio today: folder convention under
components/, how it exports, how it appears inComponentsGallery.tsx, howShowcase+Playgroundwire its live preview + controls, and how a control becomes token-bound (token: "--name") vs local. Write it down precisely with file paths. -
Write the authoring convention at repo root:
forge-suite/COMPONENT_AUTHORING.md. This is the contract every Phase 2 worker reads before touching anything. It MUST specify: - One component = one folder under
components/<Name>/. One worker owns one folder. Never two workers in the same folder. - Token rules: read
var(--token), never hardcode; if a token is missing, it was supposed to be added in Phase 1 , flag it, don't invent inline values. - How to register the component in Studio (exact steps + the files touched).
- The shared-file rule: workers do NOT edit the shared barrel/index,
routing, or
theme.css. Each worker exports from its own folder; the integrator does the final wiring as one serialized step. If a worker thinks it needs to touch a shared file, it stops and notes it instead. -
Verification each worker must run:
tscclean + its component renders in/studioComponents tab. -
Verify the harness end-to-end.
tscclean; dev server up;/studio200,/kit307→/studio; the token Save round-trip still works (edit a token → Save → theme.css rewritten in place → HMR). Report actual results, not assumptions. -
Update the reference doc. Fold any corrections from the audit back into
reference_suite_ui_kit.mdand sign[Phase1 Worker].
Done =¶
COMPONENT_AUTHORING.md exists and is accurate; theme.css token set is complete &
declared frozen; harness verified (tsc + /studio + save round-trip, with real
output pasted); reference doc reconciled. Then write a short completion note back
to this handoff file and stop , do NOT start building Phase 2 components.
Hard rules (Forge doctrine)¶
- No em dashes. Robust over quick, no band-aids. Fail loud. Test the path before
claiming done , paste real command output. Append the
URL:line to any new/ modified indexed.md.
PHASE 1 COMPLETE (2026-06-18, [Phase1 Worker])¶
All five deliverables done. No Phase 2 components started (per the stop rule).
1. Tokens audited + frozen. app/theme.css already covered menus/popovers/modals/
scrollbars. Gaps for pages/layout + modals were measured-but-untokenized values: added
--fs-h1: 32px / --fw-h1: 700 / --lh-h1: 1.2 (Notion page title), --topbar-h: 44px
(Notion topbar), --modal-w: 380px (Flask .uidlg). All five also added to
lib/kit-tokens.ts as a new "Page frame" group (allowlisted + tunable). Restored
--slider-h/--slider-w to spec (#c0c0c1/10px) after a live Save-test drift. theme.css
declared frozen (the single retheme knob).
2. Registration path traced. Flow: a <Section> in components/studio/ComponentsGallery.tsx
renders <Playground title controls render> (components/kit/Playground.tsx), which renders
through Showcase (components/studio/Showcase.tsx, registers its preview pane as a token
scope). Controls are LOCAL (preview prop) or TOKEN-BOUND (token: "--name" -> token store
components/kit/token-store.tsx, tracked + saveable + broadcast). Save POSTs dirty tokens to
app/api/kit/tokens/route.ts (dev-only, allowlist from lib/kit-tokens.ts, in-place
--name: value; rewrite preserving comments). Studio shell app/studio/page.tsx;
/kit 307 -> /studio via app/kit/page.tsx.
3. forge-suite/COMPONENT_AUTHORING.md written. One component = one folder
components/<Name>/; var(--token) only (missing token -> flag, don't inline); register via a
studioEntry exported from the folder (typed by new components/studio/types.ts StudioEntry),
integrator does the gallery wiring; shared-file rule (no worker edits to barrel/gallery/shell/
theme.css/manifest/routing); per-worker verification = tsc clean + renders in /studio.
4. Harness verified end-to-end (real output):
- npx tsc --noEmit: clean except one ignorable .next/types/validator.ts error ("Cannot
find module '../../app/page.js'") , a Next 16 type-gen quirk for the route-group index
(/ served by app/(suite)/page.tsx), not source code, not introduced here.
- Routes: /studio -> 200, /kit -> 307.
- Save round-trip: {"--topbar-h":"45px"} -> 200 {"ok":true,"written":["--topbar-h"]},
file rewritten in place, comment preserved; restored to 44px -> 200. Non-allowlisted
--evil -> 400; injection value -> 400. (New token round-trips through the allowlist.)
5. Reference doc reconciled. reference_suite_ui_kit.md updated with the Phase 1 freeze
section, signed [Phase1 Worker].
Files touched (all in /home/justinwieb/forge-suite): app/theme.css (tokens),
lib/kit-tokens.ts (Page frame group), NEW components/studio/types.ts, NEW
COMPONENT_AUTHORING.md. Forge tree: reference_suite_ui_kit.md + this handoff.
Note for the integrator: the Components tab is still a hand-edited monolith
(ComponentsGallery.tsx). The StudioEntry contract lets workers export their entry from
their own folder; wiring each entry into the gallery (and re-exporting true primitives from
the ui barrel) is the serialized integrator step. A future refactor could make the gallery
map over a registry of studioEntry imports to remove even that single shared-file edit.
SECTION 0 (foundational primitives) , IN PROGRESS (2026-06-18, [Phase1 Worker])¶
Justin's call: build Section 0 of COMPONENT_BACKLOG.md + fix the flagged gallery shared-file
collision, BEFORE any Tier 1 fan-out. High thinking for the primitive architecture.
Done + verified:
1. Tech-choice locks installed (package.json): @floating-ui/react (positioning),
@dnd-kit/core+sortable+utilities (DnD), recharts (charts), date-fns (dates), plus
@tanstack/react-virtual (virtualization, picked since the tech table left it unnamed).
Token decision (Justin approved): dark-only, KEEP names, no rename , the tokens are
already intent-named (--bg/--card/--txt/--accent), so a future light mode is an
override block reusing the same names. Zero token churn.
2. Behavioral primitives built in components/primitives/ (barrel @/components/primitives),
all tsc-clean: floating.tsx (useAnchoredFloating over Floating UI), Portal.tsx +
z-layers.ts (single overlay root + Z stacking scale), useFocusTrap.ts, useDismiss.ts,
Transition.tsx (usePresence + Transition), shortcuts.tsx (ShortcutProvider +
useShortcut), Field.tsx (Field/Form/useForm), dnd.tsx (SortableList over
dnd-kit), virtual.ts (useVirtualList over react-virtual).
4. Gallery auto-discovery refactor (the flagged shared-file collision): the Components tab
now MAPS over STUDIO_ENTRIES in components/studio/registry.tsx. Existing kit primitives
are grandfathered entries there; a new component appends one import + one array line. Workers
no longer touch ComponentsGallery.tsx. Verified in-browser: all 15 sections render, 27
controls, zero console errors.
5. Docs: COMPONENT_AUTHORING.md gained a "Foundational primitives" section (how a Tier 1
worker uses floating/portal/dismiss/focus-trap/transition/shortcut/form/DnD/virtualization)
+ the tech-locks + the token decision; registration section updated to the registry.
Verification (real output): tsc --noEmit clean (only the known .next/types route-group
artifact); /studio 200; /kit 307; token Save round-trip 200 (write + restore).
- Overlay refit DONE + browser-verified. Menu/DropdownMenu/ContextMenu/Tooltip/Popover/Modal
in
components/ui/index.tsxnow build on the primitives: floating ones useuseAnchoredFloating FloatingPortal+Z+usePresence(a sharedoverlayAnimanimates the INNER wrapper so it never fights Floating UI's transform positioning); Modal usesPortal+Z.modal+useFocusTrapuseDismiss+ presence. They are the reference impl Tier 1 copies. Verified in Justin's Chrome: Modal opens visible + Escape/scrim/Cancel all close + focus trap finds its 3 focusables; Dropdown + Popover open visible; ContextMenu opens at the cursor on real right-click.
Two real bugs found + fixed during verification (both load-bearing):
- usePresence flipped to the visible "open" state in the SAME effect that triggered mount, so the
single requestAnimationFrame got lost and every overlay mounted stuck at opacity:0 (invisible).
Fix: split into two effects (mount, then flip on a frame keyed on mounted) + a setTimeout(60)
fallback so it can never get stuck. components/primitives/Transition.tsx.
- useFocusTrap used offsetParent !== null to find focusables, but offsetParent is ALWAYS null
inside a position:fixed container (every modal), so it found none and never trapped focus. Fix:
use getClientRects().length > 0. components/primitives/useFocusTrap.ts.
(Verification note: dispatched/synthetic keyboard + contextmenu events do NOT reach React handlers
in the automation harness; use real clicks/keys. Also confirm a normal-size browser window.)
SECTION 0 COMPLETE. Foundation is frozen: tokens + primitives + refit reference overlays + gallery
auto-discovery + docs, all tsc-clean and browser-verified (/studio 200, /kit 307, Save 200). Tier 1
fan-out (one worktree per folder, append studioEntry to components/studio/registry.tsx) can begin.