···11# Lazurite Phase 2 Spec
2233+## Logging
44+55+Structured logging for development debugging and in-app log inspection. Uses
66+the [`logger`](https://pub.dev/packages/logger) package (v2.x) — the most
77+widely adopted Flutter logging library — with file-based persistence via its
88+built-in `AdvancedFileOutput`.
99+1010+### Log Levels
1111+1212+| Level | Usage |
1313+| --------- | ------------------------------------------- |
1414+| `trace` | Fine-grained control flow (loop iterations) |
1515+| `debug` | Development-only diagnostics |
1616+| `info` | Significant lifecycle events (login, nav) |
1717+| `warning` | Recoverable issues (retry, fallback) |
1818+| `error` | Failures with stack traces |
1919+| `fatal` | Unrecoverable errors (crash-level) |
2020+2121+### Architecture
2222+2323+A single `AppLogger` wrapper class exposes a top-level `log` instance injected
2424+via the service locator. All subsystems log through this instance.
2525+2626+```sh
2727+AppLogger
2828+├── LogFilter (DevelopmentFilter / ProductionFilter)
2929+├── LogPrinter (PrettyPrinter for dev, SimplePrinter for file)
3030+└── LogOutput
3131+ ├── ConsoleOutput (always, dev only)
3232+ └── AdvancedFileOutput (always, all builds)
3333+```
3434+3535+**Console output** — enabled only in debug builds via `DevelopmentFilter`.
3636+Uses `PrettyPrinter` with method counts, colors, and emojis for readability
3737+in the terminal.
3838+3939+**File output** — enabled in all builds. Uses `AdvancedFileOutput` which
4040+writes to the app's documents directory (`getApplicationDocumentsDirectory()`).
4141+Files are rotated daily with a configurable retention window (default 3 days).
4242+File format: `lazurite_YYYY-MM-DD.log`, one line per event using
4343+`SimplePrinter(colors: false)`.
4444+4545+### Integration Points
4646+4747+| Subsystem | What gets logged |
4848+| --------- | ----------------------------------------------- |
4949+| BLoC | State transitions via `BlocObserver` override |
5050+| HTTP | Request/response summaries (no auth headers) |
5151+| Auth | OAuth flow steps, token refresh, session events |
5252+| Nav | Route changes via `NavigatorObserver` |
5353+| DB | Drift query errors |
5454+5555+**Security:** Never log access tokens, refresh tokens, passwords, or full
5656+request/response bodies. HTTP logging redacts the `Authorization` header and
5757+truncates bodies to 200 chars.
5858+5959+### In-App Log Viewer
6060+6161+Accessible via Settings → Dev Tools → Logs. Reads log files from disk and
6262+displays entries in a scrollable, filterable list.
6363+6464+**Features:**
6565+6666+1. **Level filter** — chip bar to toggle visibility per level
6767+2. **Search** — free-text filter across log messages
6868+3. **Auto-scroll** — locks to bottom for live tailing; unlocks on manual scroll
6969+4. **Share** — export current day's log file via the system share sheet
7070+5. **Clear** — delete all log files with confirmation
7171+7272+Each log entry renders as a single row:
7373+7474+| Element | Format |
7575+| --------- | ----------------------------------- |
7676+| Timestamp | `HH:mm:ss.SSS` in monospace |
7777+| Level | Colored badge (E / W / I / D) |
7878+| Message | Truncated to 2 lines, tap to expand |
7979+8080+No Bloc needed — use a `LogViewerCubit` with simple file-read state, since
8181+this is a stateless inspection tool.
8282+383## Feeds
484585Phase 1 builds profile author feeds only. Phase 2 adds the full home feed
+5-5
docs/tasks/phase-1.md
···19192020## M2 — Profile Rendering
21212222-- [ ] Build `ProfileBloc` — fetch via `getProfile` / `getProfiles`
2323-- [ ] Profile screen: avatar, banner, display name, handle, description, stats (followers/following/posts), pronouns, website
2424-- [ ] Build `FeedBloc` — paginated fetch via `getAuthorFeed` with cursor + filter support
2525-- [ ] Post card widget: text, timestamps, embeds (images, quote posts, link cards)
2626-- [ ] Facet rendering: parse via `bluesky_text`, render mentions / links / hashtags as tappable spans (UTF-8 byte-safe)
2222+- [x] Build `ProfileBloc` — fetch via `getProfile` / `getProfiles`
2323+- [x] Profile screen: avatar, banner, display name, handle, description, stats (followers/following/posts), pronouns, website
2424+- [x] Build `FeedBloc` — paginated fetch via `getAuthorFeed` with cursor + filter support
2525+- [x] Post card widget: text, timestamps, embeds (images, quote posts, link cards)
2626+- [x] Facet rendering: parse via `bluesky_text`, render mentions / links / hashtags as tappable spans (UTF-8 byte-safe)
27272828## M3 — Settings & Theming
2929
+18-3
docs/tasks/phase-2.md
···11# Phase 2 Milestones
2233-## M5 — Feeds
33+## M5 — Logging
44+55+- [ ] Add `logger` package dependency
66+- [ ] `AppLogger` wrapper — singleton with `DevelopmentFilter` + `PrettyPrinter` for console, `AdvancedFileOutput` + `SimplePrinter` for file
77+- [ ] File rotation — daily log files in app documents dir (`lazurite_YYYY-MM-DD.log`), 3-day retention
88+- [ ] `LoggingBlocObserver` — log BLoC state transitions at `debug` level
99+- [ ] HTTP logging interceptor — request/response summaries, redact `Authorization` header, truncate bodies
1010+- [ ] `NavigatorObserver` subclass — log route changes at `info` level
1111+- [ ] Log viewer screen — scrollable list reading from log files on disk
1212+- [ ] Level filter chip bar — toggle visibility per log level
1313+- [ ] Free-text search across log messages
1414+- [ ] Share button — export current day's log file via system share sheet
1515+- [ ] Clear all logs with confirmation dialog
1616+- [ ] Add "Logs" entry under Dev Tools in Settings screen
1717+1818+## M6 — Feeds
419520- [ ] Build home screen with horizontally-swipable tab bar (one tab per pinned feed)
621- [ ] Implement timeline feed via `getTimeline` with cursor pagination
···1025- [ ] Feed discovery screen via `getSuggestedFeeds` — browse and add generators
1126- [ ] Feed management UI — pin/unpin, drag-to-reorder, remove saved feeds
12271313-## M6 — Search
2828+## M7 — Search
14291530- [ ] Search screen with text input, sort toggle (`top` / `latest`), and result tabs (posts / actors)
1631- [ ] `SearchBloc` — events: `QuerySubmitted`, `TypeaheadRequested`, `HistoryCleared`, `HistoryEntryDeleted`
···2035- [ ] Drift migration: add `search_history` table (query, type, searched_at, account_did)
2136- [ ] Persisted search history — display recent queries, tap to re-execute, swipe to delete, cap at 50 per account
22372323-## M7 — Dev Tools (PDS Explorer)
3838+## M8 — Dev Tools (PDS Explorer)
24392540- [ ] `DevToolsCubit` with request/response state for stateless exploration
2641- [ ] Handle / DID input with resolution via `resolveHandle`
···287287 final authSession = await atp.ATProto.fromOAuthSession(session, service: service).server.getSession();
288288 resolvedHandle = authSession.data.handle;
289289 } catch (_) {
290290- // Fall back to the login hint if the server session lookup fails.
290290+ // TODO: log this -> Fall back to the login hint if the server session lookup fails.
291291 }
292292293293 try {
294294 final profile = await Bluesky.fromOAuthSession(session, service: service).actor.getProfile(actor: session.sub);
295295 displayName = profile.data.displayName;
296296 } catch (_) {
297297- // Display name is optional and should not block session persistence.
297297+ // TODO: log this -> Display name is optional and should not block session persistence.
298298 }
299299300300 return AuthTokens(