Coffee journaling on ATProto (alpha) alpha.arabica.social
coffee
14
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 257 lines 11 kB view raw view rendered
1# CLAUDE.md 2 3This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 5## Project Overview 6 7Arabica is a coffee brew tracking application built on AT Protocol. User data 8(beans, brews, cafes, drinks, etc.) lives in each user's Personal Data Server 9(PDS), not locally. The app authenticates via OAuth, then performs CRUD through 10XRPC calls to the user's PDS. 11 12## Build & Development Commands 13 14```bash 15# Run development server (debug logging, moderator config, known-dids backfill) 16just run 17 18# Run tests 19go test ./... 20 21# Run a single test 22go test ./internal/models/... -run TestBeanIsIncomplete 23 24# After editing .templ files — regenerate Go code 25templ generate # all files 26templ generate -f <file> # single file 27 28# Rebuild Tailwind CSS (required after CSS/class changes) 29just style 30 31# Verify after changes 32go vet ./... 33go build ./... 34``` 35 36## Workflow Rules 37 38Do NOT spend more than 2-3 minutes exploring/reading files before beginning 39implementation. If the task is clear, start writing code immediately. Ask 40clarifying questions rather than endlessly reading the codebase. When given a 41specific implementation task, produce code changes in the same session. 42 43## Work Management 44 45This project uses **cells** for task tracking. See `.cells/AGENTS.md` for usage. 46 47- `./cells list` / `./cells list --status open` / `./cells show <cell-id>` 48- Do NOT use `./cells run` (spawns new agent session, humans only) 49 50## Dependencies 51 52Prefer standard library solutions over external dependencies. Only add a 53third-party dependency if stdlib genuinely cannot handle the requirement. 54 55## Task Agents 56 57Hard limit of 3 agents maximum. Each agent must have a clearly scoped 58deliverable. Do not poll agents in a loop. If agents aren't producing results 59within 5 minutes, fall back to doing the work directly. 60 61## Tech Stack 62 63- **Language:** Go 1.21+, stdlib `net/http` with Go 1.22 routing 64- **Storage:** AT Protocol PDS (user data), BoltDB (sessions), SQLite (firehose index) 65- **Frontend:** HTMX + Alpine.js + Tailwind CSS 66- **Templates:** [Templ](https://templ.guide/) (type-safe Go templates) 67- **Logging:** zerolog 68 69## Architecture 70 71### AT Protocol Integration 72 731. User authenticates via OAuth (indigo SDK handles PKCE/DPOP) 742. Handler creates `AtprotoStore` scoped to user's DID + session 753. Store methods make XRPC calls to user's PDS 764. Results rendered via Templ components or returned as JSON 77 78**Collections (NSIDs)** — defined in `internal/atproto/nsid.go`: 79 80- `social.arabica.alpha.bean` — Coffee beans (references roaster) 81- `social.arabica.alpha.roaster` — Roasters 82- `social.arabica.alpha.grinder` — Grinders 83- `social.arabica.alpha.brewer` — Brewing devices 84- `social.arabica.alpha.cafe` — Cafes (references roaster) 85- `social.arabica.alpha.brew` — Brew sessions (references bean, grinder, brewer, recipe) 86- `social.arabica.alpha.drink` — Drinks at cafes (references cafe, bean) 87- `social.arabica.alpha.recipe` — Recipes (references brewer) 88- `social.arabica.alpha.like` — Likes (strongRef to any record) 89- `social.arabica.alpha.comment` — Comments (strongRef to any record, optional parent for threads) 90 91Records reference each other via AT-URIs (`at://did/collection/rkey`). Record 92keys use TID format (timestamp-based identifiers). 93 94### Store Interface 95 96`internal/database/store.go` defines the `Store` interface with CRUD methods for 97all entity types. `AtprotoStore` is the production implementation backed by the 98user's PDS with witness cache and session cache layers. 99 100### Three-Layer Caching 101 1021. **SessionCache** (`internal/atproto/cache.go`) — per-user in-memory cache 103 (2-min TTL). Copy-on-write pattern, invalidated on writes. Dirty-collection 104 tracking skips witness cache after local writes until firehose catches up. 105 1062. **WitnessCache** (`internal/firehose/index.go`) — SQLite-backed local index 107 populated by the Jetstream firehose consumer. Provides fast reads without PDS 108 calls. Used as fallback when session cache misses. 109 1103. **PDS fallback** — direct XRPC calls to the user's PDS when both caches miss. 111 112Write path: PDS write -> write-through to witness cache -> invalidate session 113cache (mark dirty). 114 115### Firehose & Feed Pipeline 116 117`internal/firehose/` subscribes to AT Protocol's Jetstream relay for real-time 118events. Records are indexed into the SQLite feed index. The feed pipeline: 119 1201. **FeedIndex** (`firehose/index.go`) — SQLite store, `recordToFeedItem()` 121 converts indexed records to `FeedItem` structs with resolved references. 1222. **FeedIndexAdapter** (`firehose/adapter.go`) — converts firehose `FeedItem` 123 to feed `FirehoseFeedItem` to avoid import cycles. 1243. **Feed Service** (`feed/service.go`) — converts to `feed.FeedItem`, applies 125 moderation filtering, caching, and pagination. 126 127When adding a new entity type, all three layers need the new fields added. 128 129### Adding a New Entity Type (Checklist) 130 131The full stack for a new entity requires changes across many files. Follow the 132pattern of an existing entity (e.g., roaster for simple entities, brew for 133entities with references): 134 1351. **Lexicon JSON** in `lexicons/` 1362. **NSID constant** in `internal/atproto/nsid.go` 1373. **RecordType constant** in `internal/lexicons/record_type.go` (const + ParseRecordType + DisplayName) 1384. **Model + request types + validation** in `internal/models/models.go` 1395. **Record conversion** (`XToRecord`/`RecordToX`) in `internal/atproto/records.go` 1406. **Store interface methods** in `internal/database/store.go` 1417. **AtprotoStore implementation** in `internal/atproto/store.go` (CRUD + witness + cache) 1428. **Cache fields + Set/Invalidate methods** in `internal/atproto/cache.go` 1439. **OAuth scope** in `internal/atproto/oauth.go` 14410. **Firehose config** (collection list) in `internal/firehose/config.go` 14511. **Firehose FeedItem** fields + `recordToFeedItem` switch case in `internal/firehose/index.go` 14612. **Feed adapter** mapping in `internal/firehose/adapter.go` 14713. **Feed service** `FeedItem` + `FirehoseFeedItem` fields and both mapper sites in `internal/feed/service.go` 14814. **CRUD handlers** in `internal/handlers/entities.go` (also update `HandleManagePartial`, `HandleAPIListAll`, `HandleManageRefresh`) 14915. **View + OG image handlers** in `internal/handlers/entity_views.go` 15016. **Modal handlers** in `internal/handlers/modals.go` 15117. **Routes** in `internal/routing/routing.go` (page views, API CRUD, modals, OG images) 15218. **Templ view page** in `internal/web/pages/` (e.g., `cafe_view.templ`) 15319. **Templ record content** in `internal/web/components/` (e.g., `record_cafe.templ`) 15420. **Entity table component** in `internal/web/components/entity_tables.templ` 15521. **Dialog modal** in `internal/web/components/dialog_modals.templ` (+ `getStringValue` cases) 15622. **Manage partial** tab in `internal/web/components/manage_partial.templ` 15723. **My Coffee tab** in `internal/web/pages/my_coffee.templ` 15824. **Feed card** switch cases in `internal/web/pages/feed.templ` (card class, content, ActionText, share URL, title, delete URL) 15925. **OG card** function in `internal/ogcard/entities.go` (+ accent color in `brew.go`) 16026. **Suggestions** config in `internal/suggestions/suggestions.go` + handler map in `internal/handlers/suggestions.go` 16127. **Client-side cache** entity case in `static/js/combo-select.js` `getUserEntities()` 162 163### Templ Architecture 164 165**Tabs only in `.templ` files** — never use spaces for indentation. A post-edit 166hook runs `templ fmt` automatically. After editing `.templ` files, run 167`templ generate` to regenerate Go code. 168 169Pages (`internal/web/pages/`) accept `*components.LayoutData` + page-specific 170props. Components (`internal/web/components/`) are reusable building blocks. 171 172Pattern: `pages.PageName(layoutData, props).Render(r.Context(), w)` 173 174### Combo-Select Component System 175 176Entity selection dropdowns (bean, grinder, brewer, roaster, cafe) use a shared 177combo-select pattern with typeahead search, community suggestions, and inline 178creation: 179 180- **Go config**: `components.ComboSelectConfig()` in `components/combo_select.templ` 181 generates Alpine.js `x-data` with entity-specific label formatting and create 182 data mapping. 183- **Templ markup**: `components.ComboSelectInput()` renders the shared dropdown UI. 184- **JS behavior**: `static/js/combo-select.js` — Alpine.js component that 185 searches user records (from client-side cache), community suggestions (from 186 `/api/suggestions/{entity}`), and creates new entities inline via POST. 187- **Suggestions backend**: `internal/suggestions/suggestions.go` — entity configs 188 define searchable fields and dedup keys. 189 190To add a new entity to combo-select: add a case to `ComboSelectConfig`, add to 191`getUserEntities()` in `combo-select.js`, add entity config to 192`suggestions.go`, and add to the entity-to-NSID map in 193`handlers/suggestions.go`. 194 195### Entity View Handler Pattern 196 197View handlers (`HandleXView`) support both authenticated (own records) and 198public (via `?owner=` parameter) access. They: 199 2001. Try witness cache first, fall back to PDS 2012. Resolve references (e.g., roaster for cafe) 2023. Populate OG metadata for social sharing 2034. Fetch social data (likes, comments, moderation state) 2045. Render the templ page with all props 205 206### CSS Cache Busting 207 208When making CSS/style changes, bump the version query parameter in 209`internal/web/components/layout.templ`: 210 211```html 212<link rel="stylesheet" href="/static/css/output.css?v=0.1.3" /> 213``` 214 215## Testing Conventions 216 217All tests MUST use [testify/assert](https://github.com/stretchr/testify). Do 218NOT use `if` statements with `t.Error()`. 219 220```go 221assert.Equal(t, expected, actual) 222assert.NoError(t, err) 223assert.Contains(t, haystack, needle) 224assert.True(t, value) 225assert.Nil(t, value) 226``` 227 228## Using Go Tooling 229 230- `go mod download -json MODULE` — get dependency source path 231- `go doc foo.Bar` — read package/type/function docs 232- `go run ./cmd/server` instead of `go build` to avoid artifacts 233 234## Design Context 235 236See `.impeccable.md` for the full design system reference. Key points: 237 238### Brand Personality 239**Cozy, social, inviting** — like a neighborhood specialty cafe. Warm, not 240clinical. The emotional goals are calm satisfaction, geeky delight, community 241belonging, and craft pride. 242 243### Visual References 244- Specialty coffee bag packaging (Counter Culture, Onyx) — craft labels, earthy 245 tones, confident type 246- Analog journals — Moleskine, handwritten brew logs, texture of paper and ink 247 248### Design Principles 2491. **Warmth over precision** — Brown paper, not graph paper 2502. **Quiet confidence** — Strong typography, restrained color, let content shine 2513. **Tactile texture** — Evoke the analog: ceramic, kraft, journal pages 2524. **Community as atmosphere** — Cafe conversations, not social media timelines 2535. **Respect the ritual** — No urgency, no gamification, intentional interactions 254 255### Typography 256Iosevka Patrick (custom monospace) is the core UI font. Open to pairing with a 257warmer display font for headings.