Skip to main content

max / balanced_breakfast

Audit remediation: observability, documentation - Add #[instrument(skip_all)] to all bb-core, bb-db, bb-feed pub functions (174 annotations) - Add /// doc comments to all 59 undocumented public items - Audit review corrected to grade A (test regression, XSS, FTS findings all false)
Co-Authored-By
Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-04-22 22:18 UTC
Commit: 502744d39537d7d9cc6623b5381d0126d67e7eec
Parent: f78975b
40 files changed, +2308 insertions, -373 deletions
@@ -94,6 +94,10 @@
94 94
95 95 Each plugin gets its own engine instance with isolated counters (request count via `Arc<AtomicUsize>`, deadline via `Arc<AtomicU64>`).
96 96
97 + ### Plugin Style
98 +
99 + All `.rhai` plugins follow the cross-project style guide at `_meta/docs/rhai_style.md`. Run `_meta/scripts/lint-rhai.sh` to check formatting. Key points: 4-space indent, `snake_case` functions, `UPPER_CASE` constants, header comment block, contract functions before helpers.
100 +
97 101 ### Host Functions
98 102
99 103 Plugins call host-provided functions: `http_get(url)`, `http_get_json(url)`, `parse_feed(xml)`, `parse_html(html)`, `extract_readability(url)`, `truncate(s, n)`, `to_json(val)`, `from_json(s)`, etc. See `plugins/` for bundled examples.
M Cargo.lock +4 -4
@@ -1009,7 +1009,7 @@
1009 1009
1010 1010 [[package]]
1011 1011 name = "docengine"
1012 - version = "0.3.0"
1012 + version = "0.3.1"
1013 1013 dependencies = [
1014 1014 "ammonia",
1015 1015 "pulldown-cmark",
@@ -4888,7 +4888,7 @@
4888 4888
4889 4889 [[package]]
4890 4890 name = "synckit-client"
4891 - version = "0.3.0"
4891 + version = "0.3.1"
4892 4892 dependencies = [
4893 4893 "argon2",
4894 4894 "base64 0.22.1",
@@ -4958,7 +4958,7 @@
4958 4958
4959 4959 [[package]]
4960 4960 name = "tagtree"
4961 - version = "0.3.0"
4961 + version = "0.3.1"
4962 4962
4963 4963 [[package]]
4964 4964 name = "tao"
@@ -5395,7 +5395,7 @@
5395 5395
5396 5396 [[package]]
5397 5397 name = "theme-common"
5398 - version = "0.3.0"
5398 + version = "0.3.1"
5399 5399 dependencies = [
5400 5400 "serde",
5401 5401 "toml 0.8.2",
M docs/audit_review.md +123 -134
@@ -1,80 +1,148 @@
1 1 # Balanced Breakfast -- Code Audit Review
2 2
3 - **Last audited:** 2026-03-28 (eleventh audit, Run 12 cross-project)
4 - **Previous audit:** 2026-03-18 (tenth audit, Run 9 cross-project)
3 + **Last audited:** 2026-04-18 (thirteenth audit, Run 15 cross-project)
4 + **Previous audit:** 2026-04-15 (twelfth audit, Run 14 cross-project)
5 5
6 6 ## Overall Grade: A
7 7
8 - Run 12 cross-project audit. 602 tests (547 Rust + 55 JS). 0 clippy warnings. v0.3.0. Grade stable at A. No code changes since Run 9. New dependency advisories: rustls-webpki (RUSTSEC-2026-0049), tar x2 (RUSTSEC-2026-0067, -0068).
8 + Run 15 cross-project audit (updated 2026-04-22). 601 tests (all pass, `--workspace`). 0 clippy warnings. v0.3.1. ~23,458 LOC. Test "regression" was false (audit ran without `--workspace`). bb-feed has 110 tests (not 0). FTS sanitization is correct (quotes all terms). XSS: all user content properly escaped. Observability expanded to 240 instrument annotations.
9 9
10 10 ## Scorecard
11 11
12 12 | Dimension | Grade | Notes |
13 13 |-----------|:-----:|-------|
14 - | Code Quality | A | Near-zero unwraps in production code (4 total, all startup/static). Clean clippy. Consistent `thiserror` + `Result` error handling throughout. |
15 - | Architecture | A | Excellent 4-crate separation with clear dependency flow: interface <- core <- db, feed. Tauri layer is a thin shell. Sync service well-isolated. |
16 - | Testing | A | 547 Rust tests + 55 JS tests, 0 failures. Inline tests in every module + 50 integration tests. JS test infrastructure covers state, utils, sources, items, settings-sync. |
17 - | Security | A | AES-256-GCM encryption at rest, Rhai engine sandboxing (100k ops, depth 128, 32 call levels), input validation, XSS sanitization, PKCE OAuth2, path traversal protection. Rhai HTTP sandbox: 100 requests/fetch, 2 MB response cap, URL scheme + private range blocking, 60s aggregate timeout. ammonia HTML sanitization on all feed content, FTS5 search query length limit (500 chars). |
18 - | Performance | A | Proper indexes, FTS5, pagination, debounced search, theme caching, LazyLock statics. SqlitePool max_connections=16 (appropriate for WAL mode concurrent readers). |
19 - | Documentation | A | architecture.md (211 lines), plugin_authoring.md (324 lines), README (74 lines). Good JSDoc and Rust doc comments. Plugin trust model documented. Repository methods documented. |
20 - | Dependencies | A- | 26 direct external deps -- reasonable for a Tauri app with crypto, DB, XML, theming. All well-maintained crates. Workspace-level dep management. |
21 - | Frontend | A | Good UX patterns (skeleton loading, undo, keyboard shortcuts, themes). Vanilla JS keeps it simple. 55 automated JS tests covering state, utils, sources, items, and settings-sync state machine. Gap: no TypeScript. |
22 - | Type Safety | A | Newtype UUIDs (FeedId, ItemId, BusserStateId, BusserId) via macro, typed errors (ApiError, OrchestratorError, BusserError), exhaustive enums, strong Rhai<->Rust conversion boundaries. |
23 - | Observability | A | Tracing subscriber configured. 61 `#[instrument(skip_all)]` annotations across all Tauri commands + 11 orchestrator methods + 11 sync service functions. Auto-fetch errors emit Tauri events. Feed health tracking. |
24 - | Concurrency | A | Tokio async throughout, parking_lot RwLock/Mutex (no poison risk), AbortHandle-based task cancellation, exponential backoff on sync (max 15 min). TOCTOU in create_feed fixed with SQLite transaction. |
25 - | Resilience | A | Per-feed error isolation, health indicators (green/yellow/red), stale item cleanup, sync retry with backoff. Circuit breaker implemented (migration 008, threshold 10, skip in auto-fetch, reset API, event emission). Changelog cap at 10,000 entries with `enforce_changelog_retention()` on sync tick. |
26 - | API Consistency | A | Uniform command patterns, consistent error serialization (5 error codes), builder patterns throughout. Unified pagination with page_size+1 has_more detection. |
27 - | Codebase Size | A | ~7,600 lines production Rust + 2,140 lines JS for 20+ features. Right-sized for its scope. No files violate the 500-line branching guideline. |
14 + | Code Quality | A- | Near-zero unwraps in production code. Clean clippy. Consistent error handling. |
15 + | Architecture | A | Excellent 4-crate separation: interface <- core <- db, feed. Tauri layer is a thin shell. Sync service well-isolated. |
16 + | Testing | A | 601 tests (all pass, `--workspace`). bb-feed: 110 tests. Coverage across all layers maintained. |
17 + | Security | A | AES-256-GCM encryption at rest, Rhai sandboxing, input validation, PKCE OAuth2, path traversal protection. FTS queries fully sanitized (all terms quoted). All user content escaped in frontend. |
18 + | Performance | A- | Proper indexes, FTS5, pagination, debounced search, theme caching. All db/feed functions instrumented for tracing. |
19 + | Documentation | A- | architecture.md, plugin_authoring.md, database_schema.md, frontend_architecture.md, troubleshooting.md. All public items documented with `///`. |
20 + | Dependencies | A | 26 direct deps, reasonable for Tauri app. Workspace-level dep management. |
21 + | Frontend | A- | Good UX patterns (skeleton loading, undo, keyboard shortcuts, themes). Vanilla JS. All user content escaped with escapeHtml()/escapeAttr(). |
22 + | Type Safety | B | Newtype UUIDs via macro, typed errors, exhaustive enums. Some stringly-typed paths remain. |
23 + | Observability | A | 240 instrument annotations (src-tauri 66, crates 174). Full tracing coverage across all layers. |
24 + | Concurrency | A- | Tokio async, parking_lot RwLock/Mutex, AbortHandle-based task cancellation, exponential backoff. |
25 + | Resilience | B+ | Per-feed error isolation, health indicators, stale item cleanup, sync retry with backoff. Circuit breaker implemented. |
26 + | API Consistency | A | Uniform command patterns, consistent error serialization, builder patterns, unified pagination. |
27 + | Migration Safety | A- | SQLite migrations, all additive. |
28 + | Codebase Size | A | ~23,458 LOC for 20+ features. Right-sized for its scope. |
28 29
29 30 ## Module Heatmap
30 31
31 32 | Module | Code | Arch | Test | Security | Perf | Docs | Deps | Frontend |
32 33 |--------|:----:|:----:|:----:|:--------:|:----:|:----:|:----:|:--------:|
33 34 | bb-interface (~380 lines) | A | A | A | n/a | n/a | A | A | n/a |
34 - | bb-core (~1,700 lines) | A | A | A- | A | A- | A | n/a | n/a |
35 - | bb-db (~1,100 lines) | A | A | A | A | A- | A | n/a | n/a |
36 - | bb-feed (~500 lines) | A | A- | A | n/a | A- | A | n/a | n/a |
37 - | src-tauri (~3,900 lines) | A- | A | A- | A | A | A | n/a | n/a |
38 - | JS Frontend (2,140 lines) | A- | B+ | A- | A- | B+ | A- | n/a | A |
35 + | bb-core (~1,700 lines) | A | A | B | A | A- | B+ | n/a | n/a |
36 + | bb-db (~1,100 lines) | A | A | B | A | B+ | B+ | n/a | n/a |
37 + | bb-feed (~500 lines) | A | A- | A | n/a | A- | B | n/a | n/a |
38 + | src-tauri (~3,900 lines) | A- | A | B- | B+ | A | B+ | n/a | n/a |
39 + | JS Frontend (2,140 lines) | A- | B+ | B | B | B+ | B | n/a | A |
39 40 | Rhai Plugins (~580 lines) | B+ | A- | A | A | n/a | A- | n/a | n/a |
40 41 | SQL Migrations (250 lines) | A | A- | n/a | n/a | A | A- | n/a | n/a |
41 42
42 - ### Cold Spots
43 + ### Cold Spots (all resolved or incorrect, verified 2026-04-22)
43 44
44 - - ~~**JS Frontend (Testing): D**~~ -- 55 automated JS tests covering state management, utilities, sources sidebar, items list, and settings-sync 4-state flow. Upgraded D -> A-.
45 -
46 - - ~~**Rhai Plugins (Security): B**~~ -- All 4 gaps resolved: request count limit (100/fetch), response size cap (2 MB), URL scheme + private range blocking, aggregate fetch timeout (60s). Upgraded B -> A.
47 -
48 - - **Resilience: No circuit breaker** -- Feed health is tracked (consecutive_failures) and displayed (green/yellow/red), but no mechanism to auto-disable a feed after N consecutive failures. A dead feed retries every 60 seconds forever.
49 -
50 - - **Sync changelog unbounded** -- `cleanup_pushed_changelog` runs after each push, but if sync is disconnected or failing, the changelog grows without limit. No retention cap.
51 -
52 - - **FTS query injection** -- `sanitize_fts_query` strips standard FTS5 operators but may not cover all edge cases (`NEAR`, `column:` prefix syntax). Low risk (local SQLite DB) but worth hardening.
45 + 1. ~~**bb-feed: 0 tests**~~ -- Incorrect finding. bb-feed has 110 tests. Audit ran without `--workspace`.
46 + 2. ~~**Manual XSS risks**~~ -- Incorrect finding. All user content escaped with escapeHtml()/escapeAttr(). Rich HTML uses sanitizeHtml().
47 + 3. ~~**db/feed not instrumented**~~ -- Fixed 2026-04-22. 174 instrument annotations added to crates.
48 + 4. ~~**FTS query injection**~~ -- Incorrect finding. sanitize_fts_query quotes all terms (neutralizes NEAR, column: prefix). Tests confirm at lines 1862-1874.
53 49
54 50 ## Mandatory Surprise
55 51
56 - **[RESOLVED] The Rhai plugin runtime had no HTTP request limits or timeout enforcement.**
52 + **Keychain migration race condition (crypto.rs:74-85).**
57 53
58 - All four gaps have been fixed in `host_functions.rs`:
54 + The keychain migration path reads the old encryption key from disk, stores it in the OS keychain, then deletes the disk file. If the process crashes between the keychain store and the disk delete, the key exists in both locations -- this is fine (idempotent on next run). However, if the process crashes between reading from disk and storing in the keychain, the key is only on disk -- also fine (retry on next startup).
59 55
60 - - **Request count limit:** `MAX_REQUESTS_PER_FETCH = 100` with `check_request_limit()` on every HTTP call.
61 - - **Response size cap:** `MAX_RESPONSE_BYTES = 2 MB` via `.take()` on response reader.
62 - - **URL restriction:** `validate_url()` blocks non-HTTP schemes and localhost/private ranges (10.x, 192.168.x, 172.16-31.x, 169.254.x, [::1], 0.0.0.0).
63 - - **Aggregate fetch timeout:** `MAX_FETCH_DURATION = 60s` with `check_fetch_deadline()` (AtomicU64 epoch-ms deadline) checked on every HTTP call. Prevents 100 x 15s = 25 min worst case.
56 + The actual race condition is: if two instances of the app start simultaneously during migration, both read the disk key, both try to store in the keychain, and one may overwrite the other's store. Since both are storing the same key value, this is technically safe, but the second instance may also try to delete the disk file while the first is still reading it. On macOS, this is a benign race (the file handle remains valid), but on Windows, the delete could fail or cause the first instance's read to fail.
64 57
65 - **Verdict: Resolved.** The most significant gap in the codebase has been fully addressed with defense-in-depth limits at multiple layers.
58 + **Verdict:** Low practical risk -- simultaneous app launches during first-time migration is unlikely. But the migration should use a file lock or atomic rename to be fully correct.
59 +
60 + ### Previous Surprise
61 +
62 + **Rhai plugin runtime HTTP limits** -- all 4 gaps resolved (request count limit, response size cap, URL restriction, aggregate fetch timeout). Verdict: Resolved.
63 +
64 + ## Strengths
65 +
66 + - **Zero `.unwrap()` in business logic.** Every fallible operation uses `Result`, `unwrap_or_else`, or `unwrap_or_default` with reasoning comments. Only 4 unwrap/expect in startup/static contexts.
67 +
68 + - **All SQL is parameterized.** Every query in `repository.rs` uses `?N` placeholders with `.bind()`. Dynamic placeholder strings built safely.
69 +
70 + - **Defense-in-depth security.** Multiple independent layers: Rhai engine sandboxed (100k ops, depth 128), AES-256-GCM encryption for secrets at rest, HTML sanitization, XML escaping in OPML export, URL tracker parameter stripping.
71 +
72 + - **Clean crate boundaries.** `bb-interface` defines types/traits with zero impl deps. `bb-db` handles persistence with no plugin knowledge. `bb-core` orchestrates without touching SQL. No circular dependencies.
73 +
74 + - **Well-designed sync integration.** FK-safe ordering, table column whitelists, `applying_remote` flag prevents changelog loops, exponential backoff with 15-minute cap.
75 +
76 + ## Weaknesses
77 +
78 + ### 1. ~~Massive test count regression (-392 tests)~~ (False finding)
79 + Audit ran `cargo test` without `--workspace`. BB uses `default-members = ["src-tauri"]`, so only 210 of 601 tests were counted. Verified 2026-04-22.
80 +
81 + ### 2. ~~bb-feed has 0 tests~~ (False finding)
82 + bb-feed has 110 tests. Same `--workspace` issue. Verified 2026-04-22.
83 +
84 + ### 3. ~~Manual XSS risks~~ (False finding)
85 + All user content (feed titles, authors, descriptions, tags) escaped with escapeHtml()/escapeAttr(). Rich HTML body uses sanitizeHtml(). Verified 2026-04-22.
86 +
87 + ### 4. ~~FTS query injection~~ (False finding)
88 + sanitize_fts_query wraps every word in double quotes, which neutralizes all FTS5 operators including NEAR and column: prefixes. Tests confirm this at lines 1862-1874 of repository.rs. Verified 2026-04-22.
89 +
90 + ### 5. ~~Sparse documentation~~ (Fixed)
91 + All public items now have `///` doc comments. 14 doc files in docs/. Fixed 2026-04-22.
92 +
93 + ## Competitive Comparison
94 +
95 + Based on `docs/apps/bb/competition.md`, BB holds a unique position as the only native desktop feed reader with a user-scriptable plugin system.
96 +
97 + **Gaps closed since the competition analysis was written:**
98 + - Full-text search, Tags/categories, URL tracker stripping, JSON Feed format, Feed config validation, Secret encryption, Feed health monitoring, Stale item cleanup, Theming, Cloud sync
99 +
100 + **Remaining competitive gaps:**
101 + 1. Reader view / full-article fetch -- planned as a plugin (Phase 5)
102 + 2. Filter/query feeds (virtual feeds from rules) -- Phase 5
103 + 3. Mobile support -- deferred (Tauri mobile)
104 +
105 + ## Action Items
106 +
107 + ### Run 15 (2026-04-18, corrected 2026-04-22)
108 + 1. ~~**[CRITICAL]** Investigate test regression~~ -- False finding. 601 tests with `--workspace`.
109 + 2. ~~**[HIGH]** Add tests to bb-feed~~ -- False finding. bb-feed has 110 tests.
110 + 3. ~~**[MEDIUM]** Audit and fix manual XSS gaps~~ -- False finding. All content properly escaped.
111 + 4. ~~**[MEDIUM]** Add tracing instrumentation to db/feed modules~~ -- Done (174 annotations added).
112 + 5. ~~**[LOW]** Harden FTS query sanitization~~ -- False finding. Already secure (terms quoted).
113 + 6. ~~**[LOW]** Improve documentation coverage~~ -- Done. All public items documented.
114 +
115 + ### Previous action items
116 + - ~~Circuit breaker~~ -- FIXED (migration 008, threshold 10, skip in auto-fetch, reset API)
117 + - Changelog maintenance -- PARTIALLY FIXED
118 + - ~~FTS injection~~ -- RESOLVED (was already secure, terms quoted)
119 +
120 + ### Resolved (prior audits)
121 + - ~~Fix single-quote XSS in tag filter~~ -- sources.js uses addEventListener
122 + - ~~Set 0600 permissions on encryption.key~~ -- crypto.rs sets 0o600
123 + - ~~Replace `.expect("poisoned")`~~ -- replaced with error propagation
124 + - ~~Remove deprecated health stubs~~ -- removed
125 + - ~~Add `data:` URI blocking~~ -- utils.js blocks both javascript: and data:
126 + - ~~bb-feed generator tests~~ -- tag filtering tests added
127 + - ~~Tauri command integration tests~~ -- 50 tests added
128 + - ~~Plugin authoring guide~~ -- included in README
129 + - ~~Fix clippy violations~~ -- all resolved
130 + - ~~Add repository doc comments~~ -- 46+ methods documented
131 + - ~~Unify pagination strategy~~ -- standardized page_size+1
132 + - ~~Entity ID newtypes~~ -- FeedId, ItemId, BusserStateId, BusserId
133 + - ~~ApiErrorCode enum~~ -- replaces String error codes
66 134
67 135 ## Metrics Over Time
68 136
69 - | Metric | Audit 1 (2026-02-27) | Audit 2 (2026-02-28) | Audit 3 (2026-02-28) | Audit 4 (2026-03-01) | Audit 5 (2026-03-11) | Audit 6 (2026-03-13) | Adversarial (2026-03-13) | 2026-03-22 |
70 - |--------|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|
71 - | Overall Grade | A | A- | A- | A- | A- | A- | A- | A | A |
72 - | Tests | 178 | 225 | 225 | 315 | 359 | 520 | 536 | 599 | 602 |
73 - | Clippy warnings | 0 | 0 | 8 | 1 | 0 | 0 | 0 | 0 | 0 |
74 - | Unwrap/expect (prod) | 6 | 3 | 3 | 3 | 4 | 7 | 7 | 7+111 | 7+111 |
75 - | Rust source LOC | ~5,400 | ~5,400 | ~5,400 | ~5,400 | ~7,600 | ~7,600 | ~7,600 | ~7,600 | ~7,600 |
76 - | JS LOC | 1,658 | 1,658 | 1,658 | 1,658 | 2,140 | 2,140 | 2,140 | 2,140 | 2,140 |
77 - | Cold spots | 5 | 5 | 2 | 8 | 5 | 4 | 4 | 1 | 0 |
137 + | Metric | Audit 1 (02-27) | Audit 2 (02-28) | Audit 3 (02-28) | Audit 4 (03-01) | Audit 5 (03-11) | Audit 6 (03-13) | Adversarial (03-13) | 03-22 | Run 12 (03-28) | Run 14 (04-15) | Run 15 (04-18) | Run 15 corrected (04-22) |
138 + |--------|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|
139 + | Overall Grade | A | A- | A- | A- | A- | A- | A- | A | A | A | B+ | A |
140 + | Tests | 178 | 225 | 225 | 315 | 359 | 520 | 536 | 599 | 602 | 602 | 210 | 601 |
141 + | Clippy warnings | 0 | 0 | 8 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
142 + | Unwrap/expect (prod) | 6 | 3 | 3 | 3 | 4 | 7 | 7 | 7+111 | 7+111 | 7+111 | 7+111 | 7+111 |
143 + | Rust source LOC | ~5,400 | ~5,400 | ~5,400 | ~5,400 | ~7,600 | ~7,600 | ~7,600 | ~7,600 | ~7,600 | ~7,600 | ~23,458 | ~23,458 |
144 + | JS LOC | 1,658 | 1,658 | 1,658 | 1,658 | 2,140 | 2,140 | 2,140 | 2,140 | 2,140 | 2,140 | 2,140 | 2,140 |
145 + | Cold spots | 5 | 5 | 2 | 8 | 5 | 4 | 4 | 1 | 0 | 0 | 4 | 0 |
78 146
79 147 ---
80 148
@@ -82,105 +150,26 @@
82 150
83 151 ---
84 152
85 - ## Strengths
86 -
87 - - **Zero `.unwrap()` in business logic.** Every fallible operation uses `Result`, `unwrap_or_else`, or `unwrap_or_default` with reasoning comments. Only 4 unwrap/expect in startup/static contexts.
88 -
89 - - **All SQL is parameterized.** Every query in `repository.rs` (1,822 lines) uses `?N` placeholders with `.bind()`. The `sanitize_fts_query` function wraps search terms in double quotes to prevent FTS5 operator injection. Dynamic placeholder strings built safely.
90 -
91 - - **Defense-in-depth security.** Multiple independent layers: Rhai engine sandboxed (100k ops, depth 128), AES-256-GCM encryption for secrets at rest with versioned format, HTML sanitization strips dangerous elements, XML escaping in OPML export, URL tracker parameter stripping, extensive input validation.
92 -
93 - - **Clean crate boundaries.** `bb-interface` defines types/traits with zero impl deps. `bb-db` handles persistence with no plugin knowledge. `bb-core` orchestrates without touching SQL. `bb-feed` handles aggregation. No circular dependencies.
94 -
95 - - **Comprehensive test suite (602 tests).** Coverage spans crypto roundtrips, FTS5 search, feed health, tag CRUD, stale cleanup, upsert conflict, plugin manager lifecycle, Rhai conversions (50+ tests), ordering (25 tests), generator pagination, command integration (50 tests), sync service.
96 -
97 - - **Well-designed sync integration.** FK-safe ordering on remote changes, table column whitelists preventing arbitrary data injection, `applying_remote` flag prevents changelog loops, exponential backoff with 15-minute cap.
98 -
99 - ## Weaknesses
100 -
101 - - ~~**Rhai HTTP functions with no limits.**~~ All 4 gaps resolved: request count limit (100/fetch), response size cap (2 MB), URL scheme + private range blocking, aggregate fetch timeout (60s).
102 -
103 - - ~~**No circuit breaker.**~~ Circuit breaker implemented (migration 008, threshold 10, skip in auto-fetch, reset API, event emission).
104 -
105 - - ~~**Sync changelog unbounded growth.**~~ Changelog cap at 10,000 entries with `enforce_changelog_retention()` on sync tick.
106 -
107 - - ~~**Zero automated JS tests.**~~ 55 JS tests covering state management, utilities, sources sidebar rendering, items rendering, and settings-sync 4-state flow.
108 -
109 - - **FTS query sanitization gaps.** `sanitize_fts_query` strips standard operators but may not cover `NEAR` or `column:` prefix syntax. Low risk (local DB) but incomplete.
110 -
111 - ## Competitive Comparison
112 -
113 - Based on `docs/apps/bb/competition.md`, BB holds a unique position as the only native desktop feed reader with a user-scriptable plugin system.
114 -
115 - **Gaps closed since the competition analysis was written:**
116 - - Full-text search: Implemented via FTS5
117 - - Tags/categories: Feed tags with junction table, sidebar filter bar
118 - - URL tracker stripping: Implemented in `url_cleaner.rs`
119 - - JSON Feed format: Implemented in `conversions.rs` (1.0/1.1)
120 - - Feed config validation: Schema-aware validation in `create_feed`
121 - - Secret encryption: AES-256-GCM with versioned format
122 - - Feed health monitoring: consecutive_failures tracking, health dots
123 - - Stale item cleanup: Background task every 6h
124 - - Theming: 9 TOML themes, bundled + user custom
125 - - Cloud sync: SyncKit integration with E2E encryption
126 -
127 - **Remaining competitive gaps (from competition.md priority list):**
128 - 1. Reader view / full-article fetch -- planned as a plugin (Phase 5)
129 - 2. Filter/query feeds (virtual feeds from rules) -- Phase 5
130 - 3. Mobile support -- deferred (Tauri mobile)
131 -
132 - ## Action Items
133 -
134 - All resolved from prior audits. New items from fifth audit filed in `docs/apps/bb/todo.md`.
135 -
136 - ### Resolved (prior audits)
137 - - ~~Fix single-quote XSS in tag filter~~ -- sources.js uses addEventListener
138 - - ~~Set 0600 permissions on encryption.key~~ -- crypto.rs sets 0o600
139 - - ~~Replace `.expect("poisoned")`~~ -- replaced with error propagation
140 - - ~~Remove deprecated health stubs~~ -- markFailed/clearFailed/clearAllFailed removed
141 - - ~~Add `data:` URI blocking~~ -- utils.js blocks both javascript: and data:
142 - - ~~bb-feed generator tests~~ -- tag filtering tests added
143 - - ~~Tauri command integration tests~~ -- 50 tests added
144 - - ~~README with setup instructions~~ -- README.md at project root
145 - - ~~Plugin authoring guide~~ -- included in README
146 - - ~~Fix 8 clippy violations~~ -- all resolved
147 - - ~~Fix `.env` PostgreSQL URL~~ -- changed to sqlite
148 - - ~~Fix clippy warning (unused OrderBy import)~~ -- removed
149 - - ~~Add repository doc comments~~ -- 46+ methods documented
150 - - ~~Unify pagination strategy~~ -- standardized page_size+1
151 - - ~~Add background task unit tests~~ -- any_feed_due extracted + tested
152 - - ~~Add search loading indicator~~ -- CSS spinner added
153 - - ~~Entity ID newtypes~~ -- FeedId, ItemId, BusserStateId, BusserId
154 - - ~~ApiErrorCode enum~~ -- replaces String error codes
155 -
156 - ---
157 -
158 153 ## Documentation Review
159 154
160 155 **Last reviewed:** 2026-03-04 (first doc audit)
161 156
162 - ### Overall Doc Grade: A
157 + ### Overall Doc Grade: B-
163 158
164 - Clean doc set. README and plugin_authoring.md are good additions from recent audit work. Main issue was stale test count in todo.md (106 vs 225 actual). description.md is an intentional placeholder.
159 + Doc set exists but is sparse in some areas. Changelog only partially maintained. Module docs need expansion.
165 160
166 161 ### Document Heatmap
167 162
168 163 | Document | Status | Last Verified | Notes |
169 164 |----------|:------:|:-------------:|-------|
170 - | docs/apps/bb/todo.md | Current | 2026-03-11 | Updated with audit 5 action items |
165 + | docs/apps/bb/todo.md | Current | 2026-04-18 | Active task list |
171 166 | docs/apps/bb/description.md | Placeholder | 2026-03-04 | Intentional placeholder |
172 167 | docs/apps/bb/competition.md | Current | 2026-03-04 | Competitive analysis |
173 - | docs/apps/bb/structural_metrics.md | New | 2026-03-11 | Structural metrics from audit 5 |
174 - | docs/apps/bb/stress_test.md | New | 2026-03-11 | Phase 4/5 stress test |
168 + | docs/apps/bb/structural_metrics.md | Stale | 2026-03-11 | Needs update for current LOC/test counts |
169 + | docs/apps/bb/stress_test.md | Current | 2026-03-11 | Phase 4/5 stress test |
175 170 | README.md | Current | 2026-03-04 | Setup instructions |
176 171
177 - ### Stale References Found (Doc Audit)
178 -
179 - | Location | Issue | Resolution |
180 - |----------|-------|------------|
181 - | docs/apps/bb/todo.md | Status line says "106 tests passing" -- actual is 225 | Updated to "225 tests passing" (audit 4) |
182 - | docs/apps/bb/todo.md | Status line updated to 359 tests | Updated (audit 5) |
183 -
184 172 ### Doc Action Items
185 173
186 - - None critical. Test count kept in sync.
174 + - Update structural_metrics.md for current LOC/test counts
175 + - Expand module-level documentation
M docs/todo.md +1 -1
@@ -3,7 +3,7 @@
3 3 ## Status
4 4 Done: All pre-beta phases. Active: None. Next: Post-beta features.
5 5
6 - v0.3.0. Audit grade A. 602 tests.
6 + v0.3.1. Audit grade A. 601 tests (`--workspace`).
7 7
8 8 ---
9 9