···4747 - [ ] Add [OAuth2](#publications--authentication)
4848- [x] Verify `pub pull` fetches and syncs documents from leaflet.
4949- [x] Confirm `pub list` with status filtering (`all`, `published`, `draft`).
5050-- [ ] Test `pub post` creates new documents with draft/preview/validate modes.
5151-- [ ] Ensure `pub patch` updates existing documents correctly.
5252-- [ ] Validate `pub push` handles batch operations (create/update).
5353-- [ ] Verify markdown conversion to leaflet block format (headings, code, images, facets).
5050+- [x] Test `pub post` creates new documents with draft/preview/validate modes.
5151+- [x] Ensure `pub patch` updates existing documents correctly.
5252+- [x] Validate `pub push` handles batch operations (create/update).
5353+- [x] Verify markdown conversion to leaflet block format (headings, code, images, facets).
54545555### Media Domains
5656···200200- [ ] External imports (Goodreads, IMDB, Letterboxd)
201201- [ ] Cross-referencing across media types
202202- [ ] Analytics: velocity, completion rates
203203+- [ ] Books (Open Library integration enhancements)
204204+ - [ ] Author detail fetching (full names, bio)
205205+ - [ ] Edition-specific metadata
206206+ - [ ] Cover image download and caching
207207+ - [ ] Reading progress tracking
208208+ - [ ] Personal reading lists sync
209209+- [ ] Movies/TV (external API integration)
210210+ - [ ] Movie databases (TMDb, OMDb)
211211+ - [ ] Rotten Tomatoes integration
212212+- [ ] Music
213213+ - [ ] Music services (MusicBrainz, Album of the Year)
203214204215### Articles
205216···232243 - [ ] Ephemeral HTTP server on localhost
233244 - [ ] Browser launch integration
234245 - [ ] Timeout handling for abandoned flows
235235- - [ ] Migration path from app passwords to OAuth
236236- - [ ] Detect existing app password sessions
237237- - [ ] Prompt users to upgrade authentication
238238- - [ ] Maintain backward compatibility
246246+ - [ ] Support both OAuth & App Passwords but recommend OAuth
247247+- [ ] Leaflet.pub enhancements
248248+ - [ ] Multiple Publications: Manage separate publications for different topics
249249+ - [ ] Image Upload: Automatically upload images to blob storage and embed in documents
250250+ - [ ] Status Management: Publish drafts and unpublish documents from CLI
251251+ - [ ] Metadata Editing: Update document titles, summaries, and tags
252252+ - [ ] Backlink Support: Parse and resolve cross-references between documents
253253+ - [ ] Offline Mode: Queue posts and patches for later upload
239254240255### User Experience
241256···243258- [ ] Manpages and docs generator
244259- [ ] Theming and customizable output
245260- [ ] Calendar integration
261261+- [ ] Task synchronization services
262262+- [ ] Git repository linking
263263+- [ ] Note export to other platforms
246264247265### Tasks
248266···274292275293### Local API Server
276294277277-A local HTTP server daemon that exposes Noteleaf data for web UIs and extensions. Runs on the user's machine and provides programmatic access to tasks, notes, and media.
295295+A local HTTP server daemon that exposes Noteleaf data for web UIs and extensions.
296296+Runs on the user's machine and provides programmatic access to tasks, notes, and media.
278297279298#### Architecture
280299···402421#### Post v1
403422404423- Backup/restore
424424+ - [ ] Automated backups
425425+ - [ ] Backup scheduling and rotation
405426- Multiple profiles
406427- Optional synchronization
428428+ - [ ] Sync service
429429+- Import/Export
430430+ - [ ] CSV export for tasks
431431+ - [ ] Markdown export for tasks
432432+ - [ ] Bulk export commands
433433+ - [ ] Migration utilities (TaskWarrior, todo.txt, etc.)
434434+ - [ ] Git integration for notes/data versioning
407435408436## v1 Feature Matrix
409437410410-| Domain | Feature | Status |
411411-|----------|-----------------------|-----------|
412412-| Tasks | CRUD | Complete |
413413-| Tasks | Projects/tags | Complete |
414414-| Tasks | Time tracking | Complete |
415415-| Tasks | Dependencies | Complete |
416416-| Tasks | Recurrence | Complete |
417417-| Tasks | Wait/scheduled | Planned |
418418-| Tasks | Urgency scoring | Planned |
419419-| Notes | CRUD | Complete |
420420-| Notes | Search/tagging | Planned |
421421-| Media | Books/movies/TV | Complete |
422422-| Media | Articles | Complete |
423423-| Media | Source/ratings | Planned |
424424-| Articles | Parser + storage | Complete |
425425-| System | SQLite persistence | Complete |
426426-| System | Synchronization | Future |
427427-| System | Import/export formats | Future |
438438+| Domain | Feature | Status |
439439+|--------------|----------------------------|-----------|
440440+| Tasks | CRUD | Complete |
441441+| Tasks | Projects/tags | Complete |
442442+| Tasks | Time tracking | Complete |
443443+| Tasks | Dependencies | Complete |
444444+| Tasks | Recurrence | Complete |
445445+| Tasks | Wait/scheduled | Planned |
446446+| Tasks | Urgency scoring | Planned |
447447+| Notes | CRUD | Complete |
448448+| Notes | Search/tagging | Planned |
449449+| Publications | AT Protocol sync | Complete |
450450+| Publications | Post/patch/push | Complete |
451451+| Publications | Markdown conversion | Complete |
452452+| Publications | OAuth2 | Future |
453453+| Media | Books/movies/TV | Complete |
454454+| Media | Articles | Complete |
455455+| Media | Source/ratings | Planned |
456456+| Articles | Parser + storage | Complete |
457457+| System | SQLite persistence | Complete |
458458+| System | Configuration management | Complete |
459459+| System | Synchronization | Future |
460460+| System | Import/export formats | Future |
+16-24
website/TODO.md
···4455## Integration and Workflows
6677-- [ ] External Integrations
88- - [ ] Open Library API
99- - [ ] Leaflet.pub API
1010- - [ ] ATProto authentication
77+- [x] External Integrations
88+ - [x] Open Library API
99+ - [x] Leaflet.pub API
1010+ - [x] ATProto authentication
1111- [ ] Workflows and Examples
1212 - [ ] Daily task review workflow
1313 - [ ] Note-taking for research
1414 - [ ] Reading list management
1515 - [ ] Publishing a blog post to leaflet.pub
1616 - [ ] Linking tasks, notes, and media
1717-- [ ] Import/Export
1818- - [ ] Exporting data
1919- - [ ] Backup and restore
2020- - [ ] Migration from other tools
1717+- [x] Import/Export
1818+ - [x] Exporting data
1919+ - [x] Backup and restore
2020+ - [x] Migration from other tools
21212222## Development
23232424-- [ ] Building Noteleaf
2525- - [ ] Development vs production builds
2626- - [ ] Build tags
2727- - [ ] Task automation (Taskfile)
2828-- [ ] Testing
2929- - [ ] Running tests
3030- - [ ] Coverage reports
3131- - [ ] Test patterns and scaffolding
2424+- [x] Building Noteleaf
2525+ - [x] Development vs production builds
2626+ - [x] Build tags
2727+ - [x] Task automation (Taskfile)
2828+- [x] Testing
2929+ - [x] Running tests
3030+ - [x] Coverage reports
3131+ - [x] Test patterns and scaffolding
3232- [ ] Contributing
3333 - [ ] Code organization
3434 - [ ] Adding new commands
···5454 - [ ] Zettelkasten method
5555 - [ ] Research notes
5656 - [ ] Meeting notes
5757-- [ ] Publishing Tutorials
5858- - [ ] Writing a blog post
5959- - [ ] Creating a publication
6060- - [ ] Managing drafts
6161-- [ ] Advanced Tutorials
6262- - [ ] Scripting with Noteleaf
6363- - [ ] Custom automation
6464- - [ ] Data analysis
65576658## Appendices
6759
+167
website/docs/development/building.md
···11+---
22+title: Building Noteleaf
33+sidebar_label: Building
44+sidebar_position: 1
55+description: Build configurations and development workflows.
66+---
77+88+# Building Noteleaf
99+1010+Noteleaf uses [Task](https://taskfile.dev) for build automation, providing consistent workflows across development, testing, and releases.
1111+1212+## Prerequisites
1313+1414+- Go 1.21 or later
1515+- [Task](https://taskfile.dev) (install via `brew install go-task/tap/go-task` on macOS)
1616+- Git (for version information)
1717+1818+## Build Types
1919+2020+### Development Build
2121+2222+Quick build without version injection for local development:
2323+2424+```sh
2525+task build
2626+```
2727+2828+Output: `./tmp/noteleaf`
2929+3030+### Development Build with Version
3131+3232+Build with git commit hash and development tools enabled:
3333+3434+```sh
3535+task build:dev
3636+```
3737+3838+Version format: `git describe` output (e.g., `v0.1.0-15-g1234abc`)
3939+Output: `./tmp/noteleaf`
4040+4141+### Release Candidate Build
4242+4343+Build with `-rc` tag, excludes development tools:
4444+4545+```sh
4646+git tag v1.0.0-rc1
4747+task build:rc
4848+```
4949+5050+Requirements:
5151+5252+- Clean git tag with `-rc` suffix
5353+- Tag format: `v1.0.0-rc1`, `v2.1.0-rc2`, etc.
5454+5555+### Production Build
5656+5757+Build for release with strict validation:
5858+5959+```sh
6060+git tag v1.0.0
6161+task build:prod
6262+```
6363+6464+Requirements:
6565+6666+- Clean semver git tag (e.g., `v1.0.0`, `v2.1.3`)
6767+- No uncommitted changes
6868+- No prerelease suffix
6969+7070+## Build Tags
7171+7272+Production builds use the `prod` build tag to exclude development and seed commands:
7373+7474+```go
7575+//go:build !prod
7676+```
7777+7878+Commands excluded from production:
7979+8080+- `noteleaf dev` - Development utilities
8181+- `noteleaf seed` - Test data generation
8282+8383+## Version Information
8484+8585+Build process injects version metadata via ldflags:
8686+8787+```go
8888+// internal/version/version.go
8989+var (
9090+ Version = "dev" // Git tag or "dev"
9191+ Commit = "none" // Git commit hash
9292+ BuildDate = "unknown" // Build timestamp
9393+)
9494+```
9595+9696+View version information:
9797+9898+```sh
9999+task version:show
100100+noteleaf version
101101+```
102102+103103+## Build Artifacts
104104+105105+All binaries are built to `./tmp/` directory:
106106+107107+```
108108+tmp/
109109+โโโ noteleaf # Binary for current platform
110110+```
111111+112112+## Development Workflow
113113+114114+Full development cycle with linting and testing:
115115+116116+```sh
117117+task dev
118118+```
119119+120120+Runs:
121121+122122+1. Clean build artifacts
123123+2. Run linters (vet, fmt)
124124+3. Execute all tests
125125+4. Build binary
126126+127127+## Manual Build
128128+129129+Build directly with Go (bypasses Task automation):
130130+131131+```sh
132132+go build -o ./tmp/noteleaf ./cmd
133133+```
134134+135135+With version injection:
136136+137137+```sh
138138+go build -ldflags "-X github.com/stormlightlabs/noteleaf/internal/version.Version=v1.0.0" -o ./tmp/noteleaf ./cmd
139139+```
140140+141141+## Cross-Platform Builds
142142+143143+Build for specific platforms:
144144+145145+```sh
146146+# Linux
147147+GOOS=linux GOARCH=amd64 go build -o ./tmp/noteleaf-linux ./cmd
148148+149149+# Windows
150150+GOOS=windows GOARCH=amd64 go build -o ./tmp/noteleaf.exe ./cmd
151151+152152+# macOS (ARM)
153153+GOOS=darwin GOARCH=arm64 go build -o ./tmp/noteleaf-darwin-arm64 ./cmd
154154+```
155155+156156+## Clean Build
157157+158158+Remove all build artifacts:
159159+160160+```sh
161161+task clean
162162+```
163163+164164+Removes:
165165+166166+- `./tmp/` directory
167167+- `coverage.out` and `coverage.html`
+331
website/docs/development/taskfile.md
···11+---
22+title: Task Automation
33+sidebar_label: Taskfile
44+sidebar_position: 3
55+description: Using Taskfile for development workflows.
66+---
77+88+# Task Automation
99+1010+Noteleaf uses [Task](https://taskfile.dev) to automate common development workflows.
1111+1212+## Installation
1313+1414+### macOS
1515+1616+```sh
1717+brew install go-task/tap/go-task
1818+```
1919+2020+### Linux
2121+2222+```sh
2323+sh -c "$(curl -fsSL https://taskfile.dev/install.sh)"
2424+```
2525+2626+### Go Install
2727+2828+```sh
2929+go install github.com/go-task/task/v3/cmd/task@latest
3030+```
3131+3232+## Available Tasks
3333+3434+View all tasks:
3535+3636+```sh
3737+task
3838+# or
3939+task --list
4040+```
4141+4242+## Common Tasks
4343+4444+### Build Commands
4545+4646+**task build** - Quick development build
4747+4848+```sh
4949+task build
5050+```
5151+5252+Output: `./tmp/noteleaf`
5353+5454+**task build:dev** - Build with version information
5555+5656+```sh
5757+task build:dev
5858+```
5959+6060+Includes git commit hash and build date.
6161+6262+**task build:rc** - Release candidate build
6363+6464+```sh
6565+git tag v1.0.0-rc1
6666+task build:rc
6767+```
6868+6969+Requires git tag with `-rc` suffix.
7070+7171+**task build:prod** - Production build
7272+7373+```sh
7474+git tag v1.0.0
7575+task build:prod
7676+```
7777+7878+Requires clean semver tag and no uncommitted changes.
7979+8080+### Testing Commands
8181+8282+**task test** - Run all tests
8383+8484+```sh
8585+task test
8686+```
8787+8888+**task coverage** - Generate HTML coverage report
8989+9090+```sh
9191+task coverage
9292+open coverage.html # View report
9393+```
9494+9595+**task cov** - Terminal coverage summary
9696+9797+```sh
9898+task cov
9999+```
100100+101101+**task check** - Lint and coverage
102102+103103+```sh
104104+task check
105105+```
106106+107107+Runs linters and generates coverage report.
108108+109109+### Development Commands
110110+111111+**task dev** - Full development workflow
112112+113113+```sh
114114+task dev
115115+```
116116+117117+Runs:
118118+119119+1. `task clean`
120120+2. `task lint`
121121+3. `task test`
122122+4. `task build`
123123+124124+**task lint** - Run linters
125125+126126+```sh
127127+task lint
128128+```
129129+130130+Runs `go vet` and `go fmt`.
131131+132132+**task run** - Build and run
133133+134134+```sh
135135+task run
136136+```
137137+138138+Builds then executes the binary.
139139+140140+### Maintenance Commands
141141+142142+**task clean** - Remove build artifacts
143143+144144+```sh
145145+task clean
146146+```
147147+148148+Removes:
149149+150150+- `./tmp/` directory
151151+- Coverage files
152152+153153+**task deps** - Download and tidy dependencies
154154+155155+```sh
156156+task deps
157157+```
158158+159159+Runs `go mod download` and `go mod tidy`.
160160+161161+### Documentation Commands
162162+163163+**task docs:generate** - Generate all documentation
164164+165165+```sh
166166+task docs:generate
167167+```
168168+169169+Generates:
170170+171171+- Docusaurus docs (website/docs/manual)
172172+- Man pages (docs/manual)
173173+174174+**task docs:man** - Generate man pages
175175+176176+```sh
177177+task docs:man
178178+```
179179+180180+**task docs:serve** - Start documentation server
181181+182182+```sh
183183+task docs:serve
184184+```
185185+186186+Starts Docusaurus development server at <http://localhost:3000>.
187187+188188+### Version Commands
189189+190190+**task version:show** - Display version info
191191+192192+```sh
193193+task version:show
194194+```
195195+196196+Shows:
197197+198198+- Git tag
199199+- Git commit
200200+- Git describe output
201201+- Build date
202202+203203+**task version:validate** - Validate git tag
204204+205205+```sh
206206+task version:validate
207207+```
208208+209209+Checks tag format for releases.
210210+211211+### Utility Commands
212212+213213+**task status** - Show Go environment
214214+215215+```sh
216216+task status
217217+```
218218+219219+Displays:
220220+221221+- Go version
222222+- Module information
223223+- Dependencies
224224+225225+## Taskfile Variables
226226+227227+Variables injected during build:
228228+229229+```yaml
230230+BINARY_NAME: noteleaf
231231+BUILD_DIR: ./tmp
232232+VERSION_PKG: github.com/stormlightlabs/noteleaf/internal/version
233233+GIT_COMMIT: $(git rev-parse --short HEAD)
234234+GIT_TAG: $(git describe --tags --exact-match)
235235+BUILD_DATE: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
236236+```
237237+238238+## Task Dependencies
239239+240240+Some tasks automatically trigger others:
241241+242242+```sh
243243+task run
244244+# Automatically runs: task build
245245+```
246246+247247+```sh
248248+task dev
249249+# Runs in sequence:
250250+# 1. task clean
251251+# 2. task lint
252252+# 3. task test
253253+# 4. task build
254254+```
255255+256256+## Custom Workflows
257257+258258+### Pre-commit Workflow
259259+260260+```sh
261261+task lint && task test
262262+```
263263+264264+### Release Preparation
265265+266266+```sh
267267+task check && \
268268+git tag v1.0.0 && \
269269+task build:prod && \
270270+./tmp/noteleaf version
271271+```
272272+273273+### Documentation Preview
274274+275275+```sh
276276+task docs:generate
277277+task docs:serve
278278+```
279279+280280+### Full CI Simulation
281281+282282+```sh
283283+task clean && \
284284+task deps && \
285285+task lint && \
286286+task test && \
287287+task coverage && \
288288+task build:dev
289289+```
290290+291291+## Taskfile Structure
292292+293293+Location: `Taskfile.yml` (project root)
294294+295295+Key sections:
296296+297297+- **vars**: Build variables and git information
298298+- **tasks**: Command definitions with descriptions
299299+- **deps**: Task dependencies
300300+- **preconditions**: Validation before execution
301301+302302+## Configuration
303303+304304+Customize via `Taskfile.yml` or environment variables:
305305+306306+```yaml
307307+vars:
308308+ BINARY_NAME: noteleaf
309309+ BUILD_DIR: ./tmp
310310+```
311311+312312+Override at runtime:
313313+314314+```sh
315315+BINARY_NAME=custom-noteleaf task build
316316+```
317317+318318+## Why Task Over Make?
319319+320320+- Cross-platform (Windows, macOS, Linux)
321321+- YAML syntax (more readable than Makefile)
322322+- Built-in variable interpolation
323323+- Better dependency management
324324+- Precondition validation
325325+- Native Go integration
326326+327327+## Further Reading
328328+329329+- [Task Documentation](https://taskfile.dev)
330330+- [Taskfile Schema](https://taskfile.dev/api/)
331331+- Project Taskfile: `Taskfile.yml`
+268
website/docs/development/testing.md
···11+---
22+title: Testing
33+sidebar_label: Testing
44+sidebar_position: 2
55+description: Running tests and understanding test patterns.
66+---
77+88+# Testing
99+1010+Noteleaf maintains comprehensive test coverage using Go's built-in testing framework with consistent patterns across the codebase.
1111+1212+## Running Tests
1313+1414+### All Tests
1515+1616+```sh
1717+task test
1818+# or
1919+go test ./...
2020+```
2121+2222+### Coverage Report
2323+2424+Generate HTML coverage report:
2525+2626+```sh
2727+task coverage
2828+```
2929+3030+Output: `coverage.html` (opens in browser)
3131+3232+### Terminal Coverage
3333+3434+View coverage in terminal:
3535+3636+```sh
3737+task cov
3838+```
3939+4040+Shows function-level coverage percentages.
4141+4242+### Package-Specific Tests
4343+4444+Test specific package:
4545+4646+```sh
4747+go test ./internal/repo
4848+go test ./internal/handlers
4949+go test ./cmd
5050+```
5151+5252+### Verbose Output
5353+5454+```sh
5555+go test -v ./...
5656+```
5757+5858+## Test Organization
5959+6060+Tests follow a hierarchical 3-level structure:
6161+6262+```go
6363+func TestRepositoryName(t *testing.T) {
6464+ // Setup once
6565+ db := CreateTestDB(t)
6666+ repos := SetupTestData(t, db)
6767+6868+ t.Run("Feature", func(t *testing.T) {
6969+ t.Run("scenario description", func(t *testing.T) {
7070+ // Test logic
7171+ })
7272+ })
7373+}
7474+```
7575+7676+Levels:
7777+7878+1. Package (top function)
7979+2. Feature (first t.Run)
8080+3. Scenario (nested t.Run)
8181+8282+## Test Patterns
8383+8484+### Repository Tests
8585+8686+Repository tests use scaffolding from `internal/repo/test_utilities.go`:
8787+8888+```go
8989+func TestTaskRepository(t *testing.T) {
9090+ db := CreateTestDB(t)
9191+ repos := SetupTestData(t, db)
9292+ ctx := context.Background()
9393+9494+ t.Run("Create", func(t *testing.T) {
9595+ t.Run("creates task successfully", func(t *testing.T) {
9696+ task := NewTaskBuilder().
9797+ WithDescription("Test task").
9898+ Build()
9999+100100+ created, err := repos.Tasks.Create(ctx, task)
101101+ AssertNoError(t, err, "create should succeed")
102102+ AssertEqual(t, "Test task", created.Description, "description should match")
103103+ })
104104+ })
105105+}
106106+```
107107+108108+### Handler Tests
109109+110110+Handler tests use `internal/handlers/handler_test_suite.go`:
111111+112112+```go
113113+func TestHandlerName(t *testing.T) {
114114+ suite := NewHandlerTestSuite(t)
115115+ defer suite.cleanup()
116116+ handler := CreateHandler(t, NewHandlerFunc)
117117+118118+ t.Run("Feature", func(t *testing.T) {
119119+ t.Run("scenario", func(t *testing.T) {
120120+ AssertNoError(t, handler.Method(), "operation should succeed")
121121+ })
122122+ })
123123+}
124124+```
125125+126126+## Test Utilities
127127+128128+### Assertion Helpers
129129+130130+Located in `internal/repo/test_utilities.go` and `internal/handlers/test_utilities.go`:
131131+132132+```go
133133+// Error checking
134134+AssertNoError(t, err, "operation should succeed")
135135+AssertError(t, err, "operation should fail")
136136+137137+// Value comparison
138138+AssertEqual(t, expected, actual, "values should match")
139139+AssertTrue(t, condition, "should be true")
140140+AssertFalse(t, condition, "should be false")
141141+142142+// Nil checking
143143+AssertNil(t, value, "should be nil")
144144+AssertNotNil(t, value, "should not be nil")
145145+146146+// String operations
147147+AssertContains(t, str, substr, "should contain substring")
148148+```
149149+150150+### Test Data Builders
151151+152152+Create test data with builders:
153153+154154+```go
155155+task := NewTaskBuilder().
156156+ WithDescription("Test task").
157157+ WithStatus("pending").
158158+ WithPriority("high").
159159+ WithProject("test-project").
160160+ Build()
161161+162162+book := NewBookBuilder().
163163+ WithTitle("Test Book").
164164+ WithAuthor("Test Author").
165165+ Build()
166166+167167+note := NewNoteBuilder().
168168+ WithTitle("Test Note").
169169+ WithContent("Test content").
170170+ Build()
171171+```
172172+173173+### Test Database
174174+175175+In-memory SQLite for isolated tests:
176176+177177+```go
178178+db := CreateTestDB(t) // Automatic cleanup via t.Cleanup()
179179+```
180180+181181+### Sample Data
182182+183183+Pre-populated test data:
184184+185185+```go
186186+repos := SetupTestData(t, db)
187187+// Creates tasks, notes, books, movies, TV shows
188188+```
189189+190190+## Test Naming
191191+192192+Use direct descriptions without "should":
193193+194194+```go
195195+t.Run("creates task successfully", func(t *testing.T) { }) // Good
196196+t.Run("should create task", func(t *testing.T) { }) // Bad
197197+t.Run("returns error for invalid input", func(t *testing.T) { }) // Good
198198+```
199199+200200+## Test Independence
201201+202202+Each test must be independent:
203203+204204+- Use `CreateTestDB(t)` for isolated database
205205+- Don't rely on test execution order
206206+- Clean up resources with `t.Cleanup()`
207207+- Avoid package-level state
208208+209209+## Coverage Targets
210210+211211+Maintain high coverage for:
212212+213213+- Repository layer (data access)
214214+- Handler layer (business logic)
215215+- Services (external integrations)
216216+- Models (data validation)
217217+218218+Current coverage visible via:
219219+220220+```sh
221221+task cov
222222+```
223223+224224+## Continuous Integration
225225+226226+Tests run automatically on:
227227+228228+- Pull requests
229229+- Main branch commits
230230+- Release builds
231231+232232+CI configuration validates:
233233+234234+- All tests pass
235235+- No race conditions
236236+- Coverage thresholds met
237237+238238+## Debugging Tests
239239+240240+### Run Single Test
241241+242242+```sh
243243+go test -run TestTaskRepository ./internal/repo
244244+go test -run TestTaskRepository/Create ./internal/repo
245245+```
246246+247247+### Race Detector
248248+249249+```sh
250250+go test -race ./...
251251+```
252252+253253+### Verbose with Stack Traces
254254+255255+```sh
256256+go test -v -race ./internal/repo 2>&1 | grep -A 10 "FAIL"
257257+```
258258+259259+## Best Practices
260260+261261+1. Write tests for all public APIs
262262+2. Use builders for complex test data
263263+3. Apply semantic assertion helpers
264264+4. Keep tests focused and readable
265265+5. Test both success and error paths
266266+6. Avoid brittle time-based tests
267267+7. Mock external dependencies
268268+8. Use table-driven tests for variations
+204
website/docs/integrations/openlibrary.md
···11+---
22+title: Open Library API
33+sidebar_label: Open Library
44+sidebar_position: 1
55+description: Book metadata via Open Library API integration.
66+---
77+88+# Open Library API
99+1010+Noteleaf integrates with [Open Library](https://openlibrary.org) to fetch book metadata, search for books, and enrich your reading list.
1111+1212+## Overview
1313+1414+Open Library provides:
1515+1616+- Book search by title, author, ISBN
1717+- Work and edition metadata
1818+- Author information
1919+- Cover images
2020+- Subject classifications
2121+- Publication details
2222+2323+## Configuration
2424+2525+No API key required. Open Library is a free, open API service.
2626+2727+Optional user agent configuration is handled automatically:
2828+2929+```toml
3030+# .noteleaf.conf.toml
3131+# No configuration needed for Open Library
3232+```
3333+3434+## Rate Limiting
3535+3636+Open Library enforces rate limits:
3737+3838+- 180 requests per minute
3939+- 3 requests per second
4040+- Burst limit: 5 requests
4141+4242+Noteleaf automatically manages rate limiting to stay within these boundaries.
4343+4444+## Book Search
4545+4646+Search for books from the command line:
4747+4848+```sh
4949+noteleaf book search "Design Patterns"
5050+noteleaf book search "Neal Stephenson"
5151+```
5252+5353+Interactive selection shows:
5454+5555+- Title
5656+- Author(s)
5757+- First publication year
5858+- Edition count
5959+- Publisher information
6060+6161+## Book Metadata
6262+6363+When adding a book, Noteleaf fetches:
6464+6565+- Title
6666+- Author names
6767+- Publication year
6868+- Edition information
6969+- Subjects/genres
7070+- Description (when available)
7171+- Cover ID
7272+7373+## API Endpoints
7474+7575+### Search Endpoint
7676+7777+```
7878+GET https://openlibrary.org/search.json
7979+```
8080+8181+Parameters:
8282+8383+- `q`: Search query
8484+- `offset`: Pagination offset
8585+- `limit`: Results per page
8686+- `fields`: Requested fields
8787+8888+### Work Endpoint
8989+9090+```
9191+GET https://openlibrary.org/works/{work_key}.json
9292+```
9393+9494+Returns detailed work information including authors, description, subjects, and covers.
9595+9696+## Data Mapping
9797+9898+Open Library data maps to Noteleaf book fields:
9999+100100+| Open Library | Noteleaf Field |
101101+|--------------|----------------|
102102+| title | Title |
103103+| author_name | Author |
104104+| first_publish_year | Notes (included) |
105105+| edition_count | Notes (included) |
106106+| publisher | Notes (included) |
107107+| subject | Notes (included) |
108108+| cover_i | Notes (cover ID) |
109109+110110+## Example API Response
111111+112112+Search result document:
113113+114114+```json
115115+{
116116+ "key": "/works/OL45804W",
117117+ "title": "Design Patterns",
118118+ "author_name": ["Erich Gamma", "Richard Helm"],
119119+ "first_publish_year": 1994,
120120+ "edition_count": 23,
121121+ "isbn": ["0201633612", "9780201633610"],
122122+ "publisher": ["Addison-Wesley"],
123123+ "subject": ["Software design", "Object-oriented programming"],
124124+ "cover_i": 8644882
125125+}
126126+```
127127+128128+## Limitations
129129+130130+### No Direct Page Count
131131+132132+Open Library doesn't consistently provide page counts in search results. Use the interactive editor to add page counts manually if needed.
133133+134134+### Author Keys vs Names
135135+136136+Work endpoints return author keys (`/authors/OL123A`) rather than full names. Noteleaf displays available author names from search results.
137137+138138+### Cover Images
139139+140140+Cover IDs are stored but not automatically downloaded. Future versions may support local cover image caching.
141141+142142+## Error Handling
143143+144144+### Network Issues
145145+146146+```sh
147147+noteleaf book search "query"
148148+# Error: failed to connect to Open Library
149149+```
150150+151151+Check internet connection and Open Library status.
152152+153153+### Rate Limit Exceeded
154154+155155+Noteleaf automatically waits when approaching rate limits. If you see delays, this is normal behavior.
156156+157157+### No Results
158158+159159+```sh
160160+noteleaf book search "very obscure title"
161161+# No results found
162162+```
163163+164164+Try:
165165+166166+- Different search terms
167167+- Author names instead of titles
168168+- ISBNs for specific editions
169169+170170+## API Service Architecture
171171+172172+Implementation in `internal/services/services.go`:
173173+174174+```go
175175+type BookService struct {
176176+ client *http.Client
177177+ limiter *rate.Limiter
178178+ baseURL string
179179+}
180180+```
181181+182182+Features:
183183+184184+- Automatic rate limiting
185185+- Context-aware requests
186186+- Proper error handling
187187+- Timeout management (30s)
188188+189189+## Custom User Agent
190190+191191+Noteleaf identifies itself to Open Library:
192192+193193+```
194194+User-Agent: Noteleaf/v{version} (contact: info@stormlightlabs.org)
195195+```
196196+197197+This helps Open Library track API usage and contact developers if needed.
198198+199199+## Resources
200200+201201+- [Open Library API Documentation](https://openlibrary.org/dev/docs/api/books)
202202+- [Open Library Search](https://openlibrary.org/dev/docs/api/search)
203203+- [Open Library Covers](https://openlibrary.org/dev/docs/api/covers)
204204+- [Rate Limiting Policy](https://openlibrary.org/developers/api)
+164
website/docs/integrations/overview.md
···11+---
22+title: External Integrations
33+sidebar_label: Overview
44+sidebar_position: 1
55+description: Overview of external service integrations.
66+---
77+88+# External Integrations
99+1010+Noteleaf integrates with external services to enrich your productivity workflow and extend functionality beyond local storage.
1111+1212+## Available Integrations
1313+1414+### Open Library API
1515+1616+Free book metadata service for building your reading list.
1717+1818+**Features:**
1919+2020+- Search books by title, author, ISBN
2121+- Fetch metadata (author, year, subjects)
2222+- Edition and publication information
2323+- No API key required
2424+2525+**Use Cases:**
2626+2727+- Adding books to reading list
2828+- Enriching book metadata
2929+- Discovering related works
3030+3131+See [Open Library API](./openlibrary.md) for details.
3232+3333+### Leaflet.pub
3434+3535+Decentralized publishing platform built on AT Protocol.
3636+3737+**Features:**
3838+3939+- Publish notes as structured documents
4040+- Pull existing documents into local notes
4141+- Update published content
4242+- Manage drafts and publications
4343+4444+**Use Cases:**
4545+4646+- Blog publishing from terminal
4747+- Long-form content management
4848+- Decentralized content ownership
4949+5050+See [Leaflet.pub section](../leaflet/intro.md) for details.
5151+5252+### AT Protocol (Bluesky)
5353+5454+Authentication and identity via AT Protocol network.
5555+5656+**Features:**
5757+5858+- Decentralized identity (DID)
5959+- Session management
6060+- Token refresh
6161+- Secure authentication
6262+6363+**Use Cases:**
6464+6565+- Leaflet.pub authentication
6666+- Portable identity across services
6767+- Content verification
6868+6969+See [Authentication](../leaflet/authentication.md) for details.
7070+7171+## Integration Architecture
7272+7373+### Service Layer
7474+7575+External integrations live in `internal/services/`:
7676+7777+- `services.go` - Open Library API client
7878+- `atproto.go` - AT Protocol authentication
7979+- `http.go` - HTTP utilities and rate limiting
8080+8181+### Rate Limiting
8282+8383+All external services use rate limiting to respect API quotas:
8484+8585+- Open Library: 3 requests/second
8686+- AT Protocol: Per PDS configuration
8787+8888+Rate limiters are built-in and automatic.
8989+9090+### Error Handling
9191+9292+Services implement consistent error handling:
9393+9494+- Network errors
9595+- Rate limit exceeded
9696+- Authentication failures
9797+- Invalid responses
9898+9999+Errors propagate to user with actionable messages.
100100+101101+## Configuration
102102+103103+Integration configuration in `.noteleaf.conf.toml`:
104104+105105+```toml
106106+# Open Library (no configuration needed)
107107+# book_api_key = "" # Reserved for future use
108108+109109+# AT Protocol / Leaflet.pub
110110+atproto_handle = "username.bsky.social"
111111+atproto_did = "did:plc:..."
112112+atproto_pds_url = "https://bsky.social"
113113+atproto_access_jwt = "..."
114114+atproto_refresh_jwt = "..."
115115+```
116116+117117+See [Configuration](../Configuration.md) for all options.
118118+119119+## Offline Support
120120+121121+Noteleaf works fully offline for local data. Integrations are optional enhancements:
122122+123123+- Books can be added manually without Open Library
124124+- Notes exist locally without Leaflet.pub
125125+- Tasks and media work without any external service
126126+127127+External services enhance but don't require connectivity.
128128+129129+## Privacy and Data
130130+131131+### Data Sent
132132+133133+**Open Library:**
134134+135135+- Search queries
136136+- Work/edition IDs
137137+138138+**AT Protocol:**
139139+140140+- Handle/DID
141141+- Published note content
142142+- Authentication credentials
143143+144144+### Data Stored Locally
145145+146146+- API responses (cached)
147147+- Session tokens
148148+- Publication metadata
149149+150150+### No Tracking
151151+152152+Noteleaf does not:
153153+154154+- Track usage
155155+- Send analytics
156156+- Share data with third parties
157157+- Require accounts (except for publishing)
158158+159159+## Resources
160160+161161+- [Open Library API Documentation](https://openlibrary.org/developers/api)
162162+- [AT Protocol Docs](https://atproto.com)
163163+- [Leaflet.pub](https://leaflet.pub)
164164+- [Bluesky](https://bsky.app)
-26
website/docs/leaflet/future.md
···11----
22-title: Leaflet Future Features
33-sidebar_label: Future
44-description: Upcoming enhancements planned for the leaflet integration.
55-sidebar_position: 10
66----
77-88-# Leaflet Future Features
99-1010-Planned improvements to leaflet.pub integration:
1111-1212-**Multiple Publications**: Create and manage separate publications for different topics
1313-1414-**Image Upload**: Automatically upload images to blob storage and embed in documents
1515-1616-**Status Management**: Publish drafts and unpublish documents from CLI
1717-1818-**Metadata Editing**: Update document titles, summaries, and tags
1919-2020-**Backlink Support**: Parse and resolve cross-references between documents
2121-2222-**Collaborative Editing**: Pull and merge changes from multiple devices
2323-2424-**Offline Mode**: Queue posts and patches for later upload
2525-2626-For the latest roadmap, check the [GitHub repository](https://github.com/stormlightlabs/noteleaf).
+1-1
website/docs/notes/best-practices.md
···11111212**Tag consistently**: Establish a tagging taxonomy early and stick to it. Review tags periodically with `noteleaf note tags`.
13131414-**Link liberally**: Reference related notes, tasks, and articles. Future features will leverage these connections.
1414+**Link liberally**: Reference related notes, tasks, and articles using markdown links and references.
15151616**Short, focused notes**: Better to have many small notes than few giant ones. Easier to link and reuse.
1717
+297
website/docs/workflows/import-export.md
···11+---
22+title: Import and Export
33+sidebar_label: Import & Export
44+sidebar_position: 1
55+description: Data portability, backups, and migration.
66+---
77+88+# Import and Export
99+1010+Noteleaf stores data in open formats for portability: SQLite for structured data and Markdown for notes.
1111+1212+## Data Storage
1313+1414+### SQLite Database
1515+1616+Location varies by platform:
1717+1818+**macOS:**
1919+2020+```
2121+~/Library/Application Support/noteleaf/noteleaf.db
2222+```
2323+2424+**Linux:**
2525+2626+```
2727+~/.local/share/noteleaf/noteleaf.db
2828+```
2929+3030+**Windows:**
3131+3232+```
3333+%LOCALAPPDATA%\noteleaf\noteleaf.db
3434+```
3535+3636+### Markdown Files
3737+3838+Notes are stored as individual markdown files:
3939+4040+**Default location:**
4141+4242+```
4343+<data_dir>/notes/
4444+```
4545+4646+Configure via `notes_dir` in `.noteleaf.conf.toml`.
4747+4848+### Articles
4949+5050+Saved articles are stored as markdown:
5151+5252+**Default location:**
5353+5454+```
5555+<data_dir>/articles/
5656+```
5757+5858+Configure via `articles_dir` in `.noteleaf.conf.toml`.
5959+6060+## JSON Export
6161+6262+### Task Export
6363+6464+Export tasks to JSON format:
6565+6666+```sh
6767+noteleaf todo view 123 --json
6868+noteleaf todo list --static --json
6969+```
7070+7171+Output includes all task attributes:
7272+7373+- Description
7474+- Status, priority
7575+- Project, context, tags
7676+- Due dates, recurrence
7777+- Dependencies, parent tasks
7878+- Timestamps
7979+8080+### Export Format Configuration
8181+8282+Set default export format:
8383+8484+```sh
8585+noteleaf config set export_format "json"
8686+```
8787+8888+Options:
8989+9090+- `json` (default)
9191+- `csv` (planned)
9292+- `markdown` (planned)
9393+9494+## Backup Strategy
9595+9696+### Full Backup
9797+9898+Back up the entire data directory:
9999+100100+```sh
101101+# macOS
102102+cp -r ~/Library/Application\ Support/noteleaf ~/Backups/noteleaf-$(date +%Y%m%d)
103103+104104+# Linux
105105+cp -r ~/.local/share/noteleaf ~/backups/noteleaf-$(date +%Y%m%d)
106106+```
107107+108108+Includes:
109109+110110+- SQLite database
111111+- Notes directory
112112+- Articles directory
113113+- Configuration file
114114+115115+### Database Only
116116+117117+```sh
118118+# macOS
119119+cp ~/Library/Application\ Support/noteleaf/noteleaf.db ~/Backups/
120120+121121+# Linux
122122+cp ~/.local/share/noteleaf/noteleaf.db ~/backups/
123123+```
124124+125125+### Notes Only
126126+127127+```sh
128128+# Copy notes directory
129129+cp -r <data_dir>/notes ~/Backups/notes-$(date +%Y%m%d)
130130+```
131131+132132+Notes are plain markdown files, easily versioned with Git:
133133+134134+```sh
135135+cd <data_dir>/notes
136136+git init
137137+git add .
138138+git commit -m "Initial notes backup"
139139+```
140140+141141+## Restore from Backup
142142+143143+### Full Restore
144144+145145+```sh
146146+# Stop noteleaf
147147+# Replace data directory
148148+cp -r ~/Backups/noteleaf-20240315 ~/Library/Application\ Support/noteleaf
149149+```
150150+151151+### Database Restore
152152+153153+```sh
154154+cp ~/Backups/noteleaf.db ~/Library/Application\ Support/noteleaf/
155155+```
156156+157157+### Notes Restore
158158+159159+```sh
160160+cp -r ~/Backups/notes-20240315 <data_dir>/notes
161161+```
162162+163163+## Direct Database Access
164164+165165+SQLite database is accessible with standard tools:
166166+167167+```sh
168168+# Open database
169169+sqlite3 ~/Library/Application\ Support/noteleaf/noteleaf.db
170170+171171+# List tables
172172+.tables
173173+174174+# Query tasks
175175+SELECT id, description, status FROM tasks WHERE status = 'pending';
176176+177177+# Export to CSV
178178+.mode csv
179179+.output tasks.csv
180180+SELECT * FROM tasks;
181181+.quit
182182+```
183183+184184+## Portable Installation
185185+186186+Use environment variables for portable setup:
187187+188188+```sh
189189+export NOTELEAF_DATA_DIR=/path/to/usb/noteleaf-data
190190+export NOTELEAF_CONFIG=/path/to/usb/noteleaf.conf.toml
191191+noteleaf todo list
192192+```
193193+194194+Useful for:
195195+196196+- USB drive installations
197197+- Synced folders (Dropbox, iCloud)
198198+- Multiple workspaces
199199+- Testing environments
200200+201201+## Migration Strategies
202202+203203+### From TaskWarrior
204204+205205+Manual migration via SQLite:
206206+207207+1. Export TaskWarrior data to JSON
208208+2. Parse JSON and insert into noteleaf database
209209+3. Map TaskWarrior attributes to Noteleaf schema
210210+211211+Custom migration script required (future documentation).
212212+213213+### From todo.txt
214214+215215+Convert todo.txt to Noteleaf tasks:
216216+217217+1. Parse todo.txt format
218218+2. Map projects, contexts, priorities
219219+3. Bulk insert via SQLite
220220+221221+Custom migration script required (future documentation).
222222+223223+### From Other Note Apps
224224+225225+Notes are markdown files:
226226+227227+1. Export notes from source app
228228+2. Convert to plain markdown
229229+3. Copy to `<data_dir>/notes/`
230230+4. Noteleaf will index them on next scan
231231+232232+## Sync and Cloud Storage
233233+234234+### Cloud Sync
235235+236236+Store data directory in synced folder:
237237+238238+```sh
239239+# Use Dropbox
240240+export NOTELEAF_DATA_DIR=~/Dropbox/noteleaf-data
241241+242242+# Use iCloud
243243+export NOTELEAF_DATA_DIR=~/Library/Mobile\ Documents/com~apple~CloudDocs/noteleaf
244244+```
245245+246246+**Warning:** SQLite databases don't handle concurrent writes well. Only run one Noteleaf instance at a time per database.
247247+248248+### Version Control
249249+250250+Notes directory can be versioned:
251251+252252+```sh
253253+cd <data_dir>/notes
254254+git init
255255+git add .
256256+git commit -m "Initial commit"
257257+git remote add origin <repository-url>
258258+git push -u origin main
259259+```
260260+261261+Automatic git commits planned for future release.
262262+263263+## Data Formats
264264+265265+### SQLite Schema
266266+267267+View schema:
268268+269269+```sh
270270+sqlite3 noteleaf.db .schema
271271+```
272272+273273+Tables include:
274274+275275+- `tasks` - Task management
276276+- `notes` - Note metadata
277277+- `articles` - Article metadata
278278+- `books`, `movies`, `tv_shows` - Media tracking
279279+- `publications` - Leaflet.pub publications
280280+- Linking tables for tags, dependencies
281281+282282+### Markdown Format
283283+284284+Notes use standard markdown with YAML frontmatter:
285285+286286+```markdown
287287+---
288288+title: Note Title
289289+created: 2024-03-15T10:30:00Z
290290+modified: 2024-03-15T11:00:00Z
291291+tags: [tag1, tag2]
292292+---
293293+294294+# Note Content
295295+296296+Regular markdown content...
297297+```