···12121313## What Twisted Does
14141515-**Reader and social companion** for Tangled. Focused on discovery, browsing, and lightweight interactions.
1515+**Reader and social companion** for Tangled. Focused on browsing, known-handle lookup, and lightweight interactions.
16161717- Browse repos, files, READMEs, issues, PRs
1818-- Discover trending/recent repos and users
1919-- Activity feed (global and personalized)
1818+- Jump to profiles and repos from a known AT Protocol handle
1919+- Explore and Activity placeholders until search/feed work lands
2020- Sign in via AT Protocol OAuth
2121- Star repos, follow users, react to content
2222- Offline-capable with cached data
···6363| ----- | ------------------------------------------------------------------------ | ------------------------------------ | ------------------------------------ |
6464| 1 | Project shell, tabs, mock data, design system | [specs/phase-1.md](specs/phase-1.md) | [tasks/phase-1.md](tasks/phase-1.md) |
6565| 2 | Public browsing — repos, files, profiles, issues, PRs | [specs/phase-2.md](specs/phase-2.md) | [tasks/phase-2.md](tasks/phase-2.md) |
6666-| 3 | Search, discovery, activity feed | [specs/phase-3.md](specs/phase-3.md) | [tasks/phase-3.md](tasks/phase-3.md) |
6666+| 3 | Deferred search/feed placeholders and Home-first public browsing | [specs/phase-3.md](specs/phase-3.md) | [tasks/phase-3.md](tasks/phase-3.md) |
6767| 4 | OAuth sign-in, star, follow, react, personalized feed | [specs/phase-4.md](specs/phase-4.md) | [tasks/phase-4.md](tasks/phase-4.md) |
6868| 5 | Offline persistence, performance, bundle optimization | [specs/phase-5.md](specs/phase-5.md) | [tasks/phase-5.md](tasks/phase-5.md) |
6969| 6 | Write features (issues, comments, profile edit), BFF, push notifications | [specs/phase-6.md](specs/phase-6.md) | [tasks/phase-6.md](tasks/phase-6.md) |
···75752. **Tangled lexicon handling in one module boundary** (`src/services/tangled/`) — don't scatter `sh.tangled.*` awareness across pages.
76763. **Read-first** — the primary product is a fast reader. Social mutations are a controlled second layer.
77774. **Thin BFF when needed** (Phase 6+) for search indexing, personalized feeds, push notifications, and unstable procedure wrapping.
7878-5. **Mobile-first, not desktop-forge-first** — prioritize discovery, readability, feed-driven interactions, small focused actions.
7878+5. **Mobile-first, not desktop-forge-first** — prioritize readability, direct browsing, and small focused actions before broader discovery surfaces.
+9-4
docs/specs/phase-2.md
···2233## Goal
4455-Replace mock data with live Tangled API calls. Users can browse repos, profiles, file trees, README content, issues, and pull requests without signing in.
55+Replace mock data on the shippable public-browsing surface with live Tangled API calls. Users can browse repos, profiles, file trees, README content, issues, and pull requests without signing in. Public entry points are intentionally scoped down for now: Home is a known-handle jump surface, while Explore and Activity remain clearly labeled placeholders until their dedicated work lands.
6677## Protocol Stack
88···5858| Appview | `tangled.org` | HTTP (HTML, HTMX) | Profile pages, repo listings, timeline, search |
5959| Knots | `us-west.tangled.sh`, etc. | XRPC (`/xrpc/sh.tangled.*`) | Git data — trees, blobs, commits, branches, diffs |
60606161-For Phase 2, git data comes from knots via XRPC. Profile and repo metadata comes from the appview. The service layer must route requests to the correct host based on the operation.
6262-6363-> **Open question**: The appview serves HTML, not JSON API responses. We may need to scrape, use AT Protocol PDS queries (`com.atproto.repo.getRecord`), or discover if the appview exposes a JSON API. This must be validated early in Phase 2.
6161+For Phase 2, git data comes from knots via XRPC. Profile and repo metadata come from PDS records queried through `com.atproto.repo.getRecord` and `com.atproto.repo.listRecords`, not from the HTML appview. The service layer must route requests to the correct host based on the operation.
64626563## Features
6664···7977- View user profile: avatar, bio, links, pronouns, location, pinned repos
8078- Profile data comes from `sh.tangled.actor.profile` record (key: `self`) on the user's PDS
8179- List user's repos
8080+8181+### Public Discovery (scoped down)
8282+8383+- Home acts as the temporary public entry point: enter a known AT Protocol handle, then jump to profile or browse that handle's repos
8484+- Explore remains visible as a placeholder for future search work, but should not pretend global search already exists
8585+- Activity remains visible as a placeholder for future feed work, but should not pretend a public timeline already exists
8686+- Unsupported global search/trending behavior should be omitted or clearly labeled as future work, never filled with silent mock data
82878388### Pull Requests (read-only)
8489
+42-51
docs/specs/phase-3.md
···11-# Phase 3 — Search & Activity Feed
11+# Phase 3 — Deferred Search and Activity
2233## Goal
4455-Add repository/user search and a public activity feed so unauthenticated users can discover content and follow what's happening across Tangled.
55+Preserve honest product boundaries before search is implemented as a separate project. Public browsing continues through known AT Protocol handles on Home, while Explore and Activity stay visible as clearly labeled in-progress placeholders.
6677-## Search
77+## Current Product Shape
8899-### Discovery Problem
99+### Home
10101111-Tangled's appview serves HTML — there is no documented public JSON search API. Search implementation must be validated against one of these strategies:
1111+Home is the temporary public entry point for unauthenticated browsing:
12121313-1. **Appview JSON endpoint** — check if `tangled.org` exposes a search query endpoint (undocumented but possible)
1414-2. **AT Protocol relay/firehose indexing** — build a lightweight search index from ingested records (requires backend)
1515-3. **Client-side PDS enumeration** — impractical at scale
1616-4. **Scrape appview HTML** — fragile, last resort
1313+- Enter a known AT Protocol handle
1414+- Open that user's profile directly
1515+- Resolve the handle to DID + PDS via AT Protocol identity
1616+- List that user's public Tangled repos inline and open one directly
17171818-**Recommended approach**: Start with strategy 1 (probe for JSON endpoints). If unavailable, implement curated discovery (trending, recent) from cached data and defer full search to Phase 6 with a backend.
1818+This keeps public browsing fully real without implying that global discovery already exists.
19192020-### Search UI
2020+### Explore
21212222-- Search bar at top of Explore tab
2323-- Segmented results: Repos | Users
2424-- Recent searches (persisted locally)
2525-- Debounced input (300ms)
2626-- Empty state with suggested queries
2222+Explore remains a tab-level placeholder:
27232828-### Discovery Sections (fallback if search API unavailable)
2424+- No global repo search
2525+- No global user search
2626+- No curated fallback discovery pretending to be search
2727+- Empty state should explicitly say search is in progress
29283030-- Trending repos (most stars in recent window)
3131-- Recently created repos
3232-- Active repos (recent commits)
3333-- Suggested users
2929+### Activity
34303535-## Activity Feed
3131+Activity also remains a tab-level placeholder:
36323737-### Data Source
3333+- No public timeline yet
3434+- No curated public feed fallback
3535+- Empty state should explicitly say activity is in progress
38363939-Activity is derived from AT Protocol records created by users. The appview's `/timeline` page shows this data. Options for the mobile client:
3737+## Identity and Routing
40384141-1. **Appview timeline endpoint** — check if there's a JSON variant
4242-2. **Jetstream subscription** — `@atcute/jetstream` can subscribe to the AT Protocol event stream and filter for `sh.tangled.*` record types
4343-3. **PDS record queries** — poll known users' PDS for recent records
3939+The Home handle flow continues to use the existing AT Protocol resolution path:
44404545-**Recommended approach**: Try option 1 first. Fall back to option 2 (Jetstream) for a real-time feed. Option 3 is too slow for a general feed.
4141+1. Resolve `handle -> DID` via `com.atproto.identity.resolveHandle`
4242+2. Fetch the DID document and extract the PDS endpoint
4343+3. Query the user's PDS for `sh.tangled.repo` records via `com.atproto.repo.listRecords`
4444+4. Route to existing profile and repo detail screens
46454747-### Feed Item Types
4646+No backend search index, feed service, or additional dependency is introduced in this phase.
48474949-Map these AT Protocol record creations to activity cards:
4848+## UI Expectations
50495151-| Record Type | Activity Kind | Display |
5252-| -------------------------------------- | ------------- | -------------------------------- |
5353-| `sh.tangled.repo` created | repo_created | "{actor} created {repo}" |
5454-| `sh.tangled.feed.star` created | repo_starred | "{actor} starred {repo}" |
5555-| `sh.tangled.graph.follow` created | user_followed | "{actor} followed {target}" |
5656-| `sh.tangled.repo.pull` created | pr_opened | "{actor} opened PR on {repo}" |
5757-| `sh.tangled.repo.pull.status` → merged | pr_merged | "{actor} merged PR on {repo}" |
5858-| `sh.tangled.repo.issue` created | issue_opened | "{actor} opened issue on {repo}" |
5959-| `sh.tangled.repo.issue.state` → closed | issue_closed | "{actor} closed issue on {repo}" |
6060-| `sh.tangled.feed.reaction` created | reaction | "{actor} reacted to {target}" |
5050+- Home shows one handle input plus explicit actions for profile jump and repo browsing
5151+- Home shows loading, invalid-handle, no-repos, and resolved-repo-list states
5252+- Explore shows a static in-progress empty state
5353+- Activity shows a static in-progress empty state
5454+- Profile remains unchanged
61556262-### Feed UI
5656+## Deferred Work
63576464-- Filter chips: All, Repos, PRs, Issues, Social
6565-- Infinite scroll with cursor-based pagination
6666-- Pull-to-refresh
6767-- Activity cards: actor avatar + verb + target + relative timestamp
6868-- Tap card → navigate to repo/profile/PR/issue detail
5858+The following work is intentionally deferred out of this phase:
69597070-### Feed Caching
6060+- Search indexing and ranking
6161+- Search result UI and recent searches
6262+- Trending or suggested discovery sections
6363+- Public activity feed ingestion, pagination, and caching
6464+- Jetstream or appview timeline investigation
71657272-- Cache last 100 feed items in IndexedDB
7373-- Show cached feed immediately, refresh in background (stale-while-revalidate)
7474-- Stale time: 1 min
7575-- Persist across app restarts
6666+These capabilities will be revisited when search and feed work are scheduled independently.
+2-2
docs/tasks/phase-2.md
···57575858- [x] Configure TanStack Query stale/gc times per data type (see spec)
5959- [x] Set up IndexedDB query persister for offline reads
6060-- [ ] Verify stale-while-revalidate behavior: cached data shows immediately, refreshes in background
61606261## Quality
63626464-- [ ] Replace all mock data usage with live queries (remove or gate mocks behind a flag)
6363+- [x] Replace default mock-backed Home/Explore/Activity surfaces with scoped-down curated live discovery/activity
6464+- [ ] Verify stale-while-revalidate behavior: cached data shows immediately, refreshes in background
6565- [ ] Test with real Tangled repos (e.g., `tangled.org/core`)
6666- [ ] Verify error states render correctly: 404, network failure, empty repos
6767- [ ] Test on slow network (throttled devtools) — verify skeleton → content transition