URL: https://mkdocs.justinsforge.com/memory/handoffs/links-app-v2-build-2026-07-11/
Links app v2 build, 2026-07-11¶
Branch feat/links-app-v2 in creatortrack (10 commits ahead of main, all typechecked, merged from
3 parallel worker branches feat/links-v2-{assets,render,editor}). NOT merged to main, NOT deployed,
no prod migrations run. Live dev slot for review: http://100.97.43.104:3062/link-in-bio
(worktree ~/forge-suite-wt/feat/links-app-v2, DB clone ct_feat_links_app_v2, log
/tmp/ct-agent-feat_links_app_v2.log).
What was built¶
Foundation (contract): lib/links/theme.ts (PageSettings, LinksBlockData, LinkItem,
capture fields, asset refs, pv linkIds), links added to BlockKind, PageTheme.settings
(additive jsonb, no migration).
Assets + analytics (c0d5359): links.assets table (bytea, RLS) + migration; POST/GET
/api/links/assets (4MB cap, mime allowlist); reusable ImagePicker.tsx (upload from computer +
drag-drop + URL paste); resolveAssetRefs inlines asset:<id> as data URIs at publish (15MB cap,
app.links_publish_ws GUC for RLS); listPages fills views/linkCount; getLinkClickSeries +
GET /api/links/stats?linkId=; stats page gains Page views tile; pv rows excluded from click rollups.
Render engine (b0e5ed1): links group block with 4 layouts (classic/carousel/grid/card),
imagePlacement/textAlign/textSize/outline/shadow, title+subtitle, collapsed mode, block+item
visibility, schedule windows gated client-side on the static page (no republish needed);
theme-css.ts (fonts incl. Google Fonts publish-only, colors, button radius/outline/shadow,
backgrounds: solid/gradient/image blur+dim/4 textures); effects.ts (aquarium bubbles + stars,
per-link bubble bursts, prefers-reduced-motion); headerless pages render no head section (page
name is internal only); footer CreatorTrack anchor to creatortrack.ai; capture form renders
configurable fields (name/phone/birthday/instagram/note) and POSTs fields{}; pv beacon
fetch('/r/<pv>'); desktop media query >=720px.
Editor UX (d6b7d19, f16bd83): in-app sidebar (Links/Stats + per-page Editor/Page settings),
pages list kebab menu (Open/Rename/Delete) + "N views, N links" metrics, Beacons-style AddBlockModal
with searchable kinds + 4 layout tiles, LinkItemsEditor (add/remove/drag items, per-item visibility
eye, schedule datetime, effect, subtitle, image), eye toggle on every block row, email-capture
signup-fields matrix, PageSettingsPanel (fonts/sizes/colors/radius/backgrounds/effects, debounced
autosave), LinkAnalyticsPeek (per-link click series side panel), copy-link + open-in-new-tab
buttons, phone/desktop preview toggle.
Migrations on the branch (applied to the dev clone ONLY):
20260712T000310_..._links_assets_table.sql, 20260712T001646_..._kind_check_widen...sql
(adds 'links' to blocks_kind_check; without it group blocks cannot insert).
Verification¶
Each worker verified E2E on its own dev slot (render: 45/45 assertion script; editor: full
click-through; assets: upload byte-roundtrip, cross-workspace 404, seeded stats). Post-merge
integration verified on :3062 via Playwright: list page metrics, sidebar tabs, Page settings
panel, AddBlockModal, carousel links block created through the UI (CHECK constraint proven),
dry-run publish HTML contains pv beacon + linkMap entry, CreatorTrack anchor, links-group CSS.
Zero console errors. Screenshots: forge/logs/site-screenshots/links-v2-{editor,settings,addblock}.png.
Go-live steps (when approved; NOT done)¶
- Review + merge
feat/links-app-v2to main. - Apply both migrations to prod lifeos (forge_workspace_migrate.py) BEFORE deploying.
- Deploy creatortrack-prod (cap Node heap per reference_creatortrack_deploy_oom_typecheck).
- Republish live pages so they pick up pv beacon + new rendering.
Wave 2 (same evening, merged into feat/links-app-v2 at 0086d90)¶
Three more workers: growth (lead magnet on capture blocks with on-page unlock + Mautic
tag delivery, After-signup funnel handoff, per-link vanity aliases /go/<name> + UTM toggle,
SEO/OG + Meta/TikTok pixels in page settings, audience analytics: countries/devices/recent-visitor
journeys with email-to-click-trail join), funnels (links.funnels table + Funnels sidebar tab +
step builder with drop-off strip + edge publish to page:<host>:f:<slug>:<n>), and edge
(forge branch feat/links-v2-edge, NOT deployed: _lvid visitor cookie, country/device logging,
capture fields storage, /go/* alias + UTM redirect, /f/* funnel serving, sync mirrors +
leadmagnet: Mautic tagging; deploy checklist in infra/links-edge/README.md).
Two more CT migrations on the branch (funnels table, audience columns), applied to the dev clone
only. Integration verified live on :3062 (funnel created via UI appears in the capture block's
After-signup picker; stats sections render seeded audience data; pv/fpv rows excluded from click
rollups). Research: clickfunnels-funnel-mechanics-2026-07-11.
Resilience answer: capture/click DATA is edge-durable (D1); only Mautic email SENDING pauses in a
home outage (on-page lead magnet unlock covers the critical path; edge ESP like Resend is the
documented follow-up if instant email matters).
Wave 3 (after midnight, merged into feat/links-app-v2; branch now 28 commits ahead of main)¶
Two workers per Justin's feedback: shell (sidebar rebuilt on the shared components/apps/AppSidebar.tsx
primitive the Tasks app uses, so collapse/resize/mobile come free; "Links" list renamed to
"Pages" with per-page categories (link-in-bio/email-signup/event/product/landing/other,
links.pages.category migration) and a filter chip row with ?cat= URL persistence; pv SQL
regex widened for A/B ids) and funnels2 (ClickFunnels implementation: 4 create templates
incl. default Affiliate Bridge whose step 1 is an offer card; per-step sticky A/B variants
hashed off the visitor id with per-variant beacons; per-funnel pixels firing Meta Lead /
TikTok CompleteRegistration only on the post-capture step; funnel:<slug> Mautic tag +
24h UTM stash landing on the contact; step conversion report with continuation %, sub-40%
"rewrite this card" flags, and a value-per-entry rollup from configured subscriber value +
EPC; per-button UTM passthrough toggles). Two more migrations (pages.category,
funnels.settings) applied to the dev clone only. Verified live on :3062 after a clean dev
server restart (stale webpack build initially masked the new FunnelsClient; wiping .next fixed
it). Deferred ClickFunnels recs: evergreen countdown (rec 8), email re-entry tokens (rec 9).
Known gaps / follow-ups¶
- Edge Worker (
forge/infra/links-edge) stores only email/brand/source/pageId/tags: extended capturefields{}are sent but dropped until capture.ts + a D1 column land (small forge-repo follow-up). - ImagePicker is wired in assets code but BlockEditor image fields still use URL inputs with a commented ImagePicker usage ready to switch on (one-line swaps, left off to avoid cross-worker file conflicts).
- Views only start counting after a republish (pv beacon must be in the deployed page HTML).
- Parity research report with prioritized gap list (theme presets, lead magnets, pixels, SEO/OG fields, UTM tagging, geo/device analytics): links-parity-beacons-linktree-2026-07-11.