WorldHopper is an interactive archipelago for learning skills and pursuing personal ambitions. Each island offers one practice goal at a time. Completing goals earns XP and develops the island. Related skills can lead to new islands, and Explore lets you browse public and sample worlds, follow explorers, and adopt a starter roadmap into your own archipelago.
The integration branch combines My world and Social Explore. The integrated Explore currently runs against labelled sample personas. Roadmap sharing in this mode is saved in the browser and only visible inside the demo.
| Document | What it covers |
|---|---|
| Original brief | The complete initial WarwickHack prompt, including product intent, feature phases, and technical constraints. |
| Status | What is live and verified, plus the next social workstream. |
| Agent instructions | Branches, file ownership, integration rules, and verification. |
| Specification and plan | The first-slice scope and initial implementation plan. Use Status for the current state. |
| Social module guide | Contracts, Supabase RLS schema, fixture/Supabase clients, and mountSocialExplore API. |
| Social Graph & Network Plan | Architecture and review notes for graph-ranked surrounding neighbours, dummy account switching, and Following/Followers solo archipelago view. |
Use Node.js 20 or newer. Run node --test from the repository root. For a browser preview, serve the repository over HTTP with python -m http.server 8000 and open http://localhost:8000.
To reset your world, click the Reset personal islands button in the footer at the bottom of the page and confirm. This clears the current browser’s personal islands and progress, returning to an uncharted archipelago. Explore’s sample and community islands remain available.
Start both workstreams from worldhopper/first-island, the branch behind the shared preview. Use a separate checkout per agent and target PRs back to this branch.
| Workstream | Branch | Ownership |
|---|---|---|
| Codex learning and app integration | worldhopper/learning |
The app shell, map renderer, goals, XP, difficulty feedback, skill recommendations, and their tests. |
| Antigravity social and public Explore | worldhopper/social |
src/social/**, supabase/**, and tests/social*.test.mjs. |
The Explore tab transforms social discovery into an interactive, pannable shared ocean map backed by a graph-based mentor/interest recommender, a multi-persona dummy account switcher, a Following & Followers network with solo archipelago inspection, and an optional Supabase + Row-Level Security (RLS) backend.
Instead of showing a static list of profiles or filling an arbitrary fixed quota of 8 neighbours, rankNeighborsByGraph recommends between 0 and 8 surrounding neighbours who genuinely share skills or interests with the active viewer and prioritises complementary (mentor/mentee) skill level gaps.
normalizeIslandSkill(island)andnormalizeInterestToken(token)map islands and profile interest tags into canonicalskillKeynodes (e.g.,guitar,songwriting,photography,japanese,spanish,french,origami,chess,shogi,public-speaking,visual-storytelling,meeting-people,urban-sketching) and optional broadthemeKeynodes (language,board,speaking,visual,people,craft,music).- Exact skills remain distinct (
JapanesevsSpanish,ChessvsShogi), and generic'compass'/'custom'artwork fallback themes are ignored so unrelated custom islands never create false graph edges.
- A candidate explorer is only eligible to appear as a recommended neighbour if they have at least one public island (
publicIslands.length > 0) and share at least one exactskillKeywith the viewer's islands (either via one of the candidate's public islands or via their declaredinterests). - If the viewer has no islands yet, or if only 3 candidates share an interest with the viewer,
rankNeighborsByGraphreturns0or3neighbours respectively—never padding the ocean with unrelated profiles just to hit8.
For every shared skill
-
Inverse-Frequency Skill Rarity (Adamic–Adar style):
$$\text{rarity}(s) = \frac{18}{\ln(1.7 + \text{df}(s))}$$ where$\text{df}(s)$ is the number of candidate explorers who have skill$s$ . Rare shared skills carry higher weight than ubiquitous ones. -
Scale-Independent Level Complementarity (
levelComplementarity):$$\text{complementarity}(L_{\text{viewer}}, L_{\text{candidate}}) = \frac{|L_{\text{viewer}} - L_{\text{candidate}}|}{\max(L_{\text{viewer}}, L_{\text{candidate}})} \in [0, 1)$$ -
Example (Guitar Lv 10 viewer):
- Candidate A at Guitar Lv 1:
$\text{complementarity} = \frac{|10 - 1|}{10} = \mathbf{0.90}$ (strong mentor/mentee pairing) - Candidate B at Guitar Lv 7:
$\text{complementarity} = \frac{|10 - 7|}{10} = \mathbf{0.30}$ (closer levels) - Candidate C at Guitar Lv 10:
$\text{complementarity} = \frac{|10 - 10|}{10} = \mathbf{0.00}$ (same-level peer)
- Candidate A at Guitar Lv 1:
- If the match comes from a declared profile interest tag without a public island level (
theirLevel == null), a neutral complementarity of0.30is used.
-
Example (Guitar Lv 10 viewer):
-
Per-Skill Score:
$$\text{skillScore}(s) = \text{rarity}(s) \times \bigl(0.35 + 1.65 \times \text{complementarity}(L_{\text{viewer}}, L_{\text{candidate}})\bigr)$$ Because the complementarity multiplier scales the weight from0.35x(equal levels) up to2.00x(maximum level gap), large mentor/mentee level gaps are strongly prioritised over near-equal skill levels on the same skill, while multi-skill overlap also accumulates across all shared skills:$$\text{sharedScore} = \sum_{s \in \text{sharedSkills}} \text{skillScore}(s)$$
To break ties and reward broader neighbourhood cohesion among already-eligible candidates, buildInterestGraph and computePersonalizedPageRank run a 24-iteration Random Walk with Restart (Personalized PageRank, user:viewer across a multipartite graph containing:
-
User nodes:
user:vieweranduser:<profile.id> -
Skill nodes:
skill:<skillKey>(weighted by island level:$3.0 + 0.3 \times L$ for viewer,$2.5 + 0.25 \times L$ for candidates,$1.1$ for declared interest tags) -
Curated 1-hop related-skill bridges (
RELATED_SKILLS): e.g.guitar <-> songwriting,japanese <-> origami,chess <-> shogi,photography <-> visual-storytelling -
Theme bridges:
skill:<skillKey> <-> theme:<themeKey> -
Social follow edges: directed edges between
user:viewerand followed/follower candidates
The final composite score for each eligible candidate is:
score (with deterministic alphabetical tie-breaking on profile.id).
To avoid over-concentrating recommendations on a single topic when multiple candidates share it (for example, four Origami explorers filling all visible slots), rankNeighborsByGraph enforces a diversity cap: at most 2 candidates sharing the exact same primary skill/focus occupy the recommendation slots. Candidates with diverse, complementary, or secondary shared skills fill the remaining slots up to limit = 8.
Each recommended neighbour receives an explicit, human-readable relationship badge and explanation based on their highest-scoring shared skill:
- Mentor (
theirLevel > viewerLevel): e.g.Mentor · Origami Lv 4 (you Lv 1) - You could mentor (
theirLevel < viewerLevel): e.g.You could mentor · Guitar Lv 2 (you Lv 4) - Peer (
theirLevel === viewerLevel): e.g.Peer · Chess Lv 3 - Shared interest (interest tag match): e.g.
Shared interest · Photography
- Home Waters at Center: The active viewer's islands are rendered at the center of the pannable ocean canvas, surrounded by a dashed territory ring.
- 8 Compass Sectors (
NW,N,NE,E,SE,S,SW,W): Up to 8 recommended neighbours are placed around Home Waters. Ranks1..4(closest matches) pull slightly closer to the center (radialScale = 0.93), while ranks5..8sit on the outer ring (radialScale = 1.07). - Stable Sector Assignment: Each neighbour's compass sector is persisted in
assignedSectorsByProfileIdfor the current viewer persona so clicking Follow / Following in the inspector never causes islands to jump to a different sector on the map. - Adaptive Island Clusters & Bounded Containers: Within each neighbour's cluster, public islands are laid out cleanly with adaptive scale tiers (
data-island-count). Clusters with 3+ or 5+ islands scale down their footprints and pins, and the cluster container enforces a bounded max-height with scroll containment so clusters never overflow when explorers publish many islands. - Clean Wayfinding: Wayfinding is handled seamlessly through the map camera, cluster badges, and inspector jump links without redundant compass labels or map beacons.
- Drag-to-Pan & Quick-Jump Controls: Users can click-and-drag (or touch-drag) anywhere on the ocean, click any neighbour cluster to pan smoothly to it and open the bottom-right Selected Archipelago Inspector, or use the top overlay pills (
Home Waters+ neighbour pills with relationship badges) to jump directly to any cluster.
- No Login Required & 64 Sample Profiles: Explore controls lets you switch between My world and 13 demo personas. Explore features 64 labelled sample profiles spanning 28 diverse skills and topics (including Driving, Python, Cooking, Bouldering, Running, Origami, Urban Sketching, Ceramics, Music Production, Gardening, Astronomy, Italian, Creative Writing, Coffee Brewing, Swimming, Woodworking, and Chess), each with distinct shared-skill recommendations and adoptable roadmaps. Demo fixtures
- Live Persona Switching: Clicking any dummy persona:
- Places that persona's islands at the center of the ocean (
Home Waters), - Excludes that persona from the candidate pool (
viewerId), - Re-runs
rankNeighborsByGraphso you immediately see how different skill sets and levels produce different mentor/mentee recommendations and neighbour counts, - Loads that persona's own Following and Followers relationships.
- Places that persona's islands at the center of the ocean (
- Dedicated Network Sections (
listNetworkProfiles): Following and Followers in Explore controls open separate sections in the Explorer log. Both lists use the full profile dataset, independently of the neighbours shown on the map. - Zero-Island Follower Support: Followers who have
0public islands (such asLin Zhou) still appear in Followers with a clearNo public islands shared yetnote, while remaining excluded from the ocean map clusters. - Individual (Solo) Archipelago Map View: Clicking View archipelago ↗ on any profile card in Following/Followers (or Solo archipelago view ↗ in the map inspector) fetches that explorer's world via
getPublicWorld(profileId)and switches the canvas todata-map-mode="solo".- In solo mode, the canvas renders only that explorer's public islands laid out cleanly via
layoutIslands, along with a banner (Viewing <Name>'s Archipelago (Solo Map View)), a Follow / Following toggle, an Adopt starter roadmap action for any selected island, and a ← Back to neighbourhood button that restores the shared ocean view and camera focus.
- In solo mode, the canvas renders only that explorer's public islands laid out cleanly via
- Social Compass Drawer: Social Compass opens explainable recommendations for the active map neighbours. The map filters control which neighbours are shown, and each recommendation can jump to that explorer’s cluster.
- Adopt Starter Roadmap: Selecting any neighbour's island lets the viewer click Adopt starter roadmap, which clones a fresh Lv 1 (
xp: 0) starter island with that skill's roadmap into the viewer's own world without copying private state. - Sample roadmaps: Open Connections & matches from Home Waters to browse six starter roadmaps, including guitar, driving, Python and cooking, even when the map has no matches.
6. Supabase Schema & Row-Level Security (supabase/migrations/20261107000000_social_schema.sql, src/social/supabase-client.mjs)
- Fixture and Supabase clients: The integrated shell selects the local fixture client.
createSupabaseSocialClientis available for an explicitly configured backend. Real publishing and following need an authenticated viewer and a mapping from local islands to owner-controlled Supabase rows. - Strict Public/Private Separation:
public.profiles,public.islands(is_publicdefaults tofalse),public.island_private_state(owner-only RLS), andpublic.follows.follows_select_publicRLS requires bothfollower_idandfollowing_idto belong to public profiles unlessauth.uid()is a participant in the follow row.
To eliminate blocking latency when charting new islands or updating roadmaps:
- Instant First Milestone (generateStarterRoadmap): When a user creates or updates a custom island, Gemini Nano generates the initial milestone goal immediately. The onboarding dialog closes instantly, and the island is rendered with Goal 1 active and ready for practice.
- Asynchronous Background Roadmap Queue (generateRemainingRoadmapTasks): The remaining 7 intermediary tasks (XP_AWARDS.length = 8) are generated sequentially in the background via generateContinuation and chained with proper prerequisites via �ppendRoadmapGoal.
- Non-blocking Reactive UI: The Long-term plan summary displays milestone generation progress (✦ Generating remaining exercises in background…) and milestone completion indicators without interrupting learner practice. Background generation gracefully halts if an island is reset, demoted, or revised.