feat: report the library's own drift on GET /api/integrity #121

Merged
bjoern merged 2 commits from feat/library-integrity-report into main 2026-09-10 08:26:02 +02:00
Member

What's in

GET /api/integrity — a read-only report of how the library holds up against its own invariants, and the Settings-page card that shows it.

The states #111 and #113 stopped creating were never repaired: a variant whose page_count no longer matches its page rows stays wrong for ever, and a doujin whose default variant was deleted before promote-on-delete landed has no default at all. Nothing in the system could tell you that. This should find real drift on the live library the day it merges.

Nine database-only checks, all reported on every request, passing or not — a passing check is the report saying it looked:

Check Severity What it finds
PageCountDrift error page_count ≠ COUNT(pages)
NoDefaultVariant / MultipleDefaultVariants error the two halves of the ADR 0004 default-variant invariant
DanglingCover error cover_image_id with no image row (that column has no FK)
UnownedCover warning a cover that exists but is no page of that doujin (ownership is checked when set, never again)
ChapterVariantMismatch error a page filed under another variant's chapter
OrphanImageRecord warning an image row no page references and no cover points at (what a swallowed ImageCleanupHelper failure leaves)
MissingImageHash warning no sha256 — invisible to dedup, ETags and both duplicate views
BlankVariantLanguage warning a language no filter can match

Each check carries an exact count plus the first 25 findings (a response-shape bound, not a deployment knob); a finding names the table, the row id, and what is wrong with it in words. Endpoint per ADR 0021 (.Produces<ResourceResponse<IntegrityReportDto>>(200) + .ProducesProblem(401)) and ADR 0007 (self/stats links, no actions — the server has no repair verb, so offering an affordance would promise one).

App: a Library health card in Settings beside the statistics — summary, then failed checks worst-severity-first, each expandable into its findings and the count of the ones the server did not send. It renders the server's own description per check, so a check added to a newer server reads correctly on an older client, and an unknown enum ordinal decodes to unknown instead of failing the whole report. Read-only on purpose: no Fix button, because there is no verb behind it yet.

Verification

  • Use case / persistence — IntegrityUseCaseTests (15): a healthy library that passes every check, then one test per check that breaks exactly one thing and asserts both that the check fires and that every other check still passes; plus the negatives that keep the checks honest (a doujin with no variants owes no default, an image used only as a cover is no orphan, unknown is a language code) and the count-vs-sample cap.
  • REST — IntegrityIntegrationTests (3): empty library is healthy and still names every check; drift written straight into the database surfaces against the right variant id; anonymous → 401. ADR 0021 conformance comes free from EndpointResponseTypeTests reflecting over the new MapIntegrityEndpoints.
  • App — wire tests including integer-enum decoding and the unknown-kind fallback; epic tests for load, failure, the single-flight guard and a genuine later refresh; widget tests (bounded pumps) for severity ordering, expansion into findings, the "…and N more" line, and the error/retry and stale-report-on-failed-refresh paths.
  • Probes — 14 mutations (drop a check, widen a predicate, drop the cap, drop .ProducesProblem(401), return empty links, hide passing checks, remove either half of the single-flight guard, stop dispatching the load, hide the undisclosed count) each turn the claiming test red.
  • Not verified — deploy/smoke.sh step 15 was not executed against a container; its jq expressions were checked directly and the script passes bash -n. No manual run of the Flutter app.

Follow-ups

All in #107: repair verbs (plan/apply with re-verification and a 409 when a finding no longer reproduces); the storage tier (missing files, orphan files, hash verification) which needs enumeration on the storage ports and a resumable shard-cursor sweep, with quarantine rather than unlink since ADR 0014 never backs up the image tree; stored runs so the report can say when a problem appeared; a display label on a finding so the app can link to the doujin; the DoujinService rating bound; and a way to reach drift without opening Settings.


Merge #117 first — main's CI is red and that PR is the fix.

Verified independently on this branch with the full scripts/verify.sh: PASS — ApplicationCore 16, Infrastructure 91, RestAdapter 511, Integration 1 (619), Flutter all green.

🤖 Generated with Claude Code

## What's in `GET /api/integrity` — a read-only report of how the library holds up against its own invariants, and the Settings-page card that shows it. The states #111 and #113 stopped *creating* were never *repaired*: a variant whose `page_count` no longer matches its page rows stays wrong for ever, and a doujin whose default variant was deleted before promote-on-delete landed has no default at all. Nothing in the system could tell you that. **This should find real drift on the live library the day it merges.** Nine database-only checks, all reported on every request, passing or not — a passing check is the report saying it looked: | Check | Severity | What it finds | |---|---|---| | `PageCountDrift` | error | `page_count` ≠ `COUNT(pages)` | | `NoDefaultVariant` / `MultipleDefaultVariants` | error | the two halves of the ADR 0004 default-variant invariant | | `DanglingCover` | error | `cover_image_id` with no image row (that column has no FK) | | `UnownedCover` | warning | a cover that exists but is no page of that doujin (ownership is checked when set, never again) | | `ChapterVariantMismatch` | error | a page filed under another variant's chapter | | `OrphanImageRecord` | warning | an image row no page references and no cover points at (what a swallowed `ImageCleanupHelper` failure leaves) | | `MissingImageHash` | warning | no sha256 — invisible to dedup, ETags and both duplicate views | | `BlankVariantLanguage` | warning | a language no filter can match | Each check carries an exact count plus the first 25 findings (a response-shape bound, not a deployment knob); a finding names the table, the row id, and what is wrong with it in words. Endpoint per ADR 0021 (`.Produces<ResourceResponse<IntegrityReportDto>>(200)` + `.ProducesProblem(401)`) and ADR 0007 (self/stats links, **no actions** — the server has no repair verb, so offering an affordance would promise one). App: a **Library health** card in Settings beside the statistics — summary, then failed checks worst-severity-first, each expandable into its findings and the count of the ones the server did not send. It renders the server's own description per check, so a check added to a newer server reads correctly on an older client, and an unknown enum ordinal decodes to `unknown` instead of failing the whole report. Read-only on purpose: **no Fix button, because there is no verb behind it yet.** ## Verification - **Use case / persistence** — `IntegrityUseCaseTests` (15): a healthy library that passes every check, then one test per check that breaks exactly one thing and asserts both that the check fires *and* that every other check still passes; plus the negatives that keep the checks honest (a doujin with no variants owes no default, an image used only as a cover is no orphan, `unknown` is a language code) and the count-vs-sample cap. - **REST** — `IntegrityIntegrationTests` (3): empty library is healthy and still names every check; drift written straight into the database surfaces against the right variant id; anonymous → 401. ADR 0021 conformance comes free from `EndpointResponseTypeTests` reflecting over the new `MapIntegrityEndpoints`. - **App** — wire tests including integer-enum decoding and the unknown-kind fallback; epic tests for load, failure, the single-flight guard and a genuine later refresh; widget tests (bounded pumps) for severity ordering, expansion into findings, the "…and N more" line, and the error/retry and stale-report-on-failed-refresh paths. - **Probes** — 14 mutations (drop a check, widen a predicate, drop the cap, drop `.ProducesProblem(401)`, return empty links, hide passing checks, remove either half of the single-flight guard, stop dispatching the load, hide the undisclosed count) each turn the claiming test red. - **Not verified** — `deploy/smoke.sh` step 15 was not executed against a container; its jq expressions were checked directly and the script passes `bash -n`. No manual run of the Flutter app. ## Follow-ups All in **#107**: repair verbs (plan/apply with re-verification and a 409 when a finding no longer reproduces); the storage tier (missing files, orphan files, hash verification) which needs enumeration on the storage ports and a resumable shard-cursor sweep, with **quarantine rather than unlink** since ADR 0014 never backs up the image tree; stored runs so the report can say when a problem appeared; a display label on a finding so the app can link to the doujin; the `DoujinService` rating bound; and a way to reach drift without opening Settings. --- **Merge #117 first** — `main`'s CI is red and that PR is the fix. Verified independently on this branch with the full `scripts/verify.sh`: **PASS** — ApplicationCore 16, Infrastructure 91, RestAdapter 511, Integration 1 (**619**), Flutter all green. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: report the library's own drift on GET /api/integrity
Some checks failed
CI / build (pull_request) Successful in 21s
Deploy Smoke Test / smoke (pull_request) Failing after 2m48s
Flutter CI / analyze-and-test (pull_request) Successful in 4m58s
CI / test (pull_request) Failing after 1m29s
7db4a742e2
Nothing checked the library against its own invariants, so the states #111
and #113 stopped creating were never repaired: a variant whose page_count
no longer matches its page rows stays wrong for ever, and a doujin whose
default variant was deleted before the promote-on-delete fix has no default
at all. Both are invisible until something reads them.

GET /api/integrity answers with every database-only check the server knows,
passing or not — nine of them, each one WHERE plus a correlated subquery:
page_count drift, no/multiple default variants, a cover that dangles or is
not this doujin's own page image, a page filed under another variant's
chapter, an image row nothing references, an image row with no sha256, and
a blank variant language. Each check reports an exact count and the first
25 findings; a finding names the table, the row id and what is wrong with
it in words. No repair verb, no filesystem walk, no background tier: the
report is a read against the SQLite file, so it can be an ordinary request.

The Settings page grows a Library health card beside the statistics: the
summary, then the failed checks worst-severity-first, each expandable into
its findings and the count of the ones the server did not send. It renders
the server's own description per check, so a check added to a newer server
reads correctly on an older client; a kind it does not know decodes to
`unknown` instead of failing the whole report. Read-only, deliberately —
there is no Fix button because there is no verb behind it yet.

ADRs: none amended. ADR 0004 already states both invariants the report
checks; observing them changes no behaviour it covers. The 25-finding cap
is a response-shape bound, not a deployment knob, so nothing new in
DEPLOYMENT.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Flutter Coverage

File Line coverage
lib/app/store.dart 95.0% (19 of 20)
lib/data/models/doujin_models.dart 86.4% (38 of 44)
lib/data/models/doujin_models.g.dart 61.7% (195 of 316)
lib/domain/entities/stored_settings.dart 100.0% (9 of 9)
lib/presentation/state/app_state.dart 80.0% (24 of 30)
lib/agent/agent_service.dart 88.7% (344 of 388)
lib/agent/approval_gate.dart 100.0% (16 of 16)
lib/agent/assistant_context.dart 52.8% (28 of 53)
lib/agent/browser_budget.dart 80.0% (4 of 5)
lib/agent/caching_describer.dart 100.0% (13 of 13)
lib/agent/memory_store.dart 92.9% (13 of 14)
lib/agent/skills/skill_registry.dart 98.2% (54 of 55)
lib/agent/system_prompt.dart 100.0% (69 of 69)
lib/agent/tools/budgeted_browser_tool.dart 84.2% (16 of 19)
lib/agent/tools/doujin_write_tool.dart 86.4% (319 of 369)
lib/agent/tools/entity_write_tool.dart 73.5% (164 of 223)
lib/agent/tools/fetch_page_tool.dart 89.3% (67 of 75)
lib/agent/tools/get_doujin_tool.dart 84.4% (27 of 32)
lib/agent/tools/list_entities_tool.dart 76.1% (54 of 71)
lib/agent/tools/navigate_tool.dart 95.1% (39 of 41)
lib/agent/tools/read_skill_tool.dart 82.4% (14 of 17)
lib/agent/tools/reflection_tools.dart 75.7% (28 of 37)
lib/agent/tools/search_doujins_tool.dart 100.0% (99 of 99)
lib/agent/tools/view_images_tool.dart 95.0% (38 of 40)
lib/domain/entities/assistant_entry.dart 20.0% (1 of 5)
lib/presentation/state/actions/assistant_actions.dart 54.5% (6 of 11)
lib/core/app_config.dart 70.0% (7 of 10)
lib/domain/entities/entity_model.dart 100.0% (1 of 1)
lib/data/repositories/entity_in_use_exception.dart 33.3% (1 of 3)
lib/data/models/search_query.dart 50.0% (2 of 4)
lib/data/models/search_query.g.dart 33.3% (25 of 75)
lib/core/constants.dart 40.0% (4 of 10)
lib/data/offline/offline_download_service.dart 93.0% (66 of 71)
lib/data/reading_progress_store.dart 92.3% (36 of 39)
lib/domain/entities/reading_progress.dart 100.0% (26 of 26)
lib/presentation/middleware/assistant_epics.dart 83.1% (74 of 89)
lib/presentation/middleware/epics.dart 91.3% (380 of 416)
lib/presentation/middleware/offline_epics.dart 98.4% (60 of 61)
lib/presentation/state/reducers.dart 100.0% (14 of 14)
lib/core/chunking.dart 100.0% (12 of 12)
lib/core/code_point_compare.dart 100.0% (8 of 8)
lib/core/languages.dart 100.0% (8 of 8)
lib/core/reading_order.dart 100.0% (22 of 22)
lib/core/title_display.dart 90.0% (9 of 10)
lib/core/variant_display.dart 100.0% (22 of 22)
lib/data/models/envelope.dart 81.2% (13 of 16)
lib/data/models/envelope.g.dart 53.7% (36 of 67)
lib/data/models/server_models.dart 96.7% (29 of 30)
lib/data/models/server_models.g.dart 53.0% (44 of 83)
lib/domain/entities/offline_manifest.dart 100.0% (78 of 78)
lib/data/repositories/upload_exception.dart 33.3% (1 of 3)
lib/domain/entities/filter_token.dart 92.2% (107 of 116)
lib/presentation/middleware/duplicates_epics.dart 100.0% (39 of 39)
lib/presentation/middleware/editor_epics.dart 57.3% (168 of 293)
lib/presentation/middleware/entity_ops.dart 58.0% (40 of 69)
lib/presentation/middleware/server_epics.dart 100.0% (42 of 42)
lib/presentation/middleware/upload_epics.dart 98.1% (104 of 106)
lib/presentation/state/actions/offline_actions.dart 91.7% (11 of 12)
lib/presentation/state/actions/detail_actions.dart 85.7% (6 of 7)
lib/presentation/state/actions/duplicates_actions.dart 83.3% (5 of 6)
lib/presentation/state/actions/editor_actions.dart 56.8% (21 of 37)
lib/presentation/state/actions/entity_actions.dart 62.5% (10 of 16)
lib/presentation/state/actions/library_actions.dart 42.9% (9 of 21)
lib/presentation/state/actions/metadata_actions.dart 100.0% (5 of 5)
lib/presentation/state/actions/reader_actions.dart 75.0% (3 of 4)
lib/presentation/state/actions/reading_progress_actions.dart 100.0% (2 of 2)
lib/presentation/state/actions/server_actions.dart 71.4% (10 of 14)
lib/presentation/state/actions/settings_actions.dart 87.5% (7 of 8)
lib/presentation/state/actions/upload_actions.dart 90.9% (10 of 11)
lib/presentation/state/reducers/assistant_reducer.dart 95.0% (57 of 60)
lib/presentation/state/reducers/detail_reducer.dart 100.0% (24 of 24)
lib/presentation/state/reducers/duplicates_reducer.dart 100.0% (38 of 38)
lib/presentation/state/reducers/editor_reducer.dart 97.1% (99 of 102)
lib/presentation/state/reducers/entity_reducer.dart 98.8% (82 of 83)
lib/presentation/state/reducers/library_reducer.dart 100.0% (122 of 122)
lib/presentation/state/reducers/metadata_reducer.dart 90.5% (38 of 42)
lib/presentation/state/reducers/offline_reducer.dart 100.0% (61 of 61)
lib/presentation/state/reducers/reader_reducer.dart 100.0% (18 of 18)
lib/presentation/state/reducers/reading_progress_reducer.dart 100.0% (20 of 20)
lib/presentation/state/reducers/server_reducer.dart 100.0% (60 of 60)
lib/presentation/state/reducers/settings_reducer.dart 100.0% (60 of 60)
lib/presentation/state/reducers/upload_reducer.dart 100.0% (45 of 45)
lib/presentation/pages/editor/page_grid.dart 92.8% (309 of 333)
lib/presentation/pages/editor/variants_tab.dart 55.3% (52 of 94)
lib/core/natural_sort.dart 100.0% (27 of 27)
lib/core/url_utils.dart 100.0% (4 of 4)
lib/presentation/pages/editor/chapter_panel.dart 100.0% (55 of 55)
lib/presentation/pages/editor/chapter_title_dialog.dart 100.0% (27 of 27)
lib/presentation/widgets/cover_thumbnail.dart 83.3% (30 of 36)
lib/presentation/pages/editor/upload_panel.dart 38.6% (61 of 158)
lib/presentation/pages/editor/variant_dialog.dart 0.0% (0 of 61)
lib/presentation/widgets/language_dropdown.dart 84.6% (11 of 13)
lib/presentation/pages/reader/reader_page.dart 89.4% (236 of 264)
lib/presentation/pages/reader/reader_overlay.dart 94.4% (51 of 54)
lib/presentation/pages/reader/reader_sequence.dart 100.0% (16 of 16)
lib/presentation/widgets/star_rating.dart 100.0% (72 of 72)
lib/app/di.dart 48.3% (14 of 29)
lib/presentation/assistant/assistant_panel.dart 91.3% (84 of 92)
lib/presentation/layout/main_layout.dart 86.3% (44 of 51)
lib/data/api_client.dart 92.8% (64 of 69)
lib/data/repositories/doujin_api_repository.dart 29.5% (114 of 386)
lib/data/repositories/health_repository.dart 72.0% (18 of 25)
lib/data/secure_storage.dart 0.0% (0 of 26)
lib/core/theme.dart 96.9% (31 of 32)
lib/presentation/assistant/approval_card.dart 95.0% (38 of 40)
lib/presentation/assistant/assistant_markdown.dart 100.0% (3 of 3)
lib/presentation/assistant/chat_entries.dart 87.5% (35 of 40)
lib/presentation/pages/people/people_page.dart 55.6% (10 of 18)
lib/presentation/widgets/entity_editor.dart 91.6% (131 of 143)
lib/presentation/widgets/entity_management_page.dart 86.8% (217 of 250)
lib/presentation/pages/editor/editor_page.dart 77.6% (59 of 76)
lib/presentation/pages/editor/editor_pane.dart 69.8% (37 of 53)
lib/presentation/pages/editor/association_picker.dart 95.5% (106 of 111)
lib/presentation/pages/editor/associations_tab.dart 73.8% (90 of 122)
lib/presentation/pages/editor/doujin_list_pane.dart 67.2% (43 of 64)
lib/presentation/pages/editor/edit_title_dialog.dart 91.7% (55 of 60)
lib/presentation/pages/editor/new_doujin_dialog.dart 66.7% (30 of 45)
lib/presentation/pages/editor/metadata_tab.dart 70.5% (93 of 132)
lib/presentation/pages/characters/characters_page.dart 57.9% (11 of 19)
lib/presentation/pages/tags/tags_page.dart 100.0% (17 of 17)
lib/presentation/pages/detail/detail_page.dart 80.1% (173 of 216)
lib/presentation/pages/detail/variant_tabs_panel.dart 97.2% (239 of 246)
lib/data/offline/filesystem_offline_store.dart 94.1% (144 of 153)
lib/core/offline_paths.dart 100.0% (3 of 3)
lib/presentation/pages/offline/offline_page.dart 98.2% (164 of 167)
lib/app/app.dart 67.1% (47 of 70)
lib/presentation/pages/settings/settings_page.dart 99.4% (157 of 158)
lib/presentation/pages/circles/circles_page.dart 52.6% (10 of 19)
lib/presentation/pages/duplicates/duplicates_page.dart 100.0% (157 of 157)
lib/presentation/pages/library/library_page.dart 93.1% (215 of 231)
lib/presentation/pages/series/series_page.dart 50.0% (9 of 18)
lib/presentation/widgets/smart_filter_bar.dart 84.1% (207 of 246)
lib/presentation/pages/settings/server_sections.dart 100.0% (227 of 227)
lib/presentation/widgets/model_combo_field.dart 69.0% (100 of 145)
lib/app/skill_assets.dart 92.9% (13 of 14)

Total: 81.8% (8159 of 9979)

<!-- flutter-coverage-comment --> ## Flutter Coverage | File | Line coverage | |:---|---:| | lib/app/store.dart | 95.0% (19 of 20) | | lib/data/models/doujin_models.dart | 86.4% (38 of 44) | | lib/data/models/doujin_models.g.dart | 61.7% (195 of 316) | | lib/domain/entities/stored_settings.dart | 100.0% (9 of 9) | | lib/presentation/state/app_state.dart | 80.0% (24 of 30) | | lib/agent/agent_service.dart | 88.7% (344 of 388) | | lib/agent/approval_gate.dart | 100.0% (16 of 16) | | lib/agent/assistant_context.dart | 52.8% (28 of 53) | | lib/agent/browser_budget.dart | 80.0% (4 of 5) | | lib/agent/caching_describer.dart | 100.0% (13 of 13) | | lib/agent/memory_store.dart | 92.9% (13 of 14) | | lib/agent/skills/skill_registry.dart | 98.2% (54 of 55) | | lib/agent/system_prompt.dart | 100.0% (69 of 69) | | lib/agent/tools/budgeted_browser_tool.dart | 84.2% (16 of 19) | | lib/agent/tools/doujin_write_tool.dart | 86.4% (319 of 369) | | lib/agent/tools/entity_write_tool.dart | 73.5% (164 of 223) | | lib/agent/tools/fetch_page_tool.dart | 89.3% (67 of 75) | | lib/agent/tools/get_doujin_tool.dart | 84.4% (27 of 32) | | lib/agent/tools/list_entities_tool.dart | 76.1% (54 of 71) | | lib/agent/tools/navigate_tool.dart | 95.1% (39 of 41) | | lib/agent/tools/read_skill_tool.dart | 82.4% (14 of 17) | | lib/agent/tools/reflection_tools.dart | 75.7% (28 of 37) | | lib/agent/tools/search_doujins_tool.dart | 100.0% (99 of 99) | | lib/agent/tools/view_images_tool.dart | 95.0% (38 of 40) | | lib/domain/entities/assistant_entry.dart | 20.0% (1 of 5) | | lib/presentation/state/actions/assistant_actions.dart | 54.5% (6 of 11) | | lib/core/app_config.dart | 70.0% (7 of 10) | | lib/domain/entities/entity_model.dart | 100.0% (1 of 1) | | lib/data/repositories/entity_in_use_exception.dart | 33.3% (1 of 3) | | lib/data/models/search_query.dart | 50.0% (2 of 4) | | lib/data/models/search_query.g.dart | 33.3% (25 of 75) | | lib/core/constants.dart | 40.0% (4 of 10) | | lib/data/offline/offline_download_service.dart | 93.0% (66 of 71) | | lib/data/reading_progress_store.dart | 92.3% (36 of 39) | | lib/domain/entities/reading_progress.dart | 100.0% (26 of 26) | | lib/presentation/middleware/assistant_epics.dart | 83.1% (74 of 89) | | lib/presentation/middleware/epics.dart | 91.3% (380 of 416) | | lib/presentation/middleware/offline_epics.dart | 98.4% (60 of 61) | | lib/presentation/state/reducers.dart | 100.0% (14 of 14) | | lib/core/chunking.dart | 100.0% (12 of 12) | | lib/core/code_point_compare.dart | 100.0% (8 of 8) | | lib/core/languages.dart | 100.0% (8 of 8) | | lib/core/reading_order.dart | 100.0% (22 of 22) | | lib/core/title_display.dart | 90.0% (9 of 10) | | lib/core/variant_display.dart | 100.0% (22 of 22) | | lib/data/models/envelope.dart | 81.2% (13 of 16) | | lib/data/models/envelope.g.dart | 53.7% (36 of 67) | | lib/data/models/server_models.dart | 96.7% (29 of 30) | | lib/data/models/server_models.g.dart | 53.0% (44 of 83) | | lib/domain/entities/offline_manifest.dart | 100.0% (78 of 78) | | lib/data/repositories/upload_exception.dart | 33.3% (1 of 3) | | lib/domain/entities/filter_token.dart | 92.2% (107 of 116) | | lib/presentation/middleware/duplicates_epics.dart | 100.0% (39 of 39) | | lib/presentation/middleware/editor_epics.dart | 57.3% (168 of 293) | | lib/presentation/middleware/entity_ops.dart | 58.0% (40 of 69) | | lib/presentation/middleware/server_epics.dart | 100.0% (42 of 42) | | lib/presentation/middleware/upload_epics.dart | 98.1% (104 of 106) | | lib/presentation/state/actions/offline_actions.dart | 91.7% (11 of 12) | | lib/presentation/state/actions/detail_actions.dart | 85.7% (6 of 7) | | lib/presentation/state/actions/duplicates_actions.dart | 83.3% (5 of 6) | | lib/presentation/state/actions/editor_actions.dart | 56.8% (21 of 37) | | lib/presentation/state/actions/entity_actions.dart | 62.5% (10 of 16) | | lib/presentation/state/actions/library_actions.dart | 42.9% (9 of 21) | | lib/presentation/state/actions/metadata_actions.dart | 100.0% (5 of 5) | | lib/presentation/state/actions/reader_actions.dart | 75.0% (3 of 4) | | lib/presentation/state/actions/reading_progress_actions.dart | 100.0% (2 of 2) | | lib/presentation/state/actions/server_actions.dart | 71.4% (10 of 14) | | lib/presentation/state/actions/settings_actions.dart | 87.5% (7 of 8) | | lib/presentation/state/actions/upload_actions.dart | 90.9% (10 of 11) | | lib/presentation/state/reducers/assistant_reducer.dart | 95.0% (57 of 60) | | lib/presentation/state/reducers/detail_reducer.dart | 100.0% (24 of 24) | | lib/presentation/state/reducers/duplicates_reducer.dart | 100.0% (38 of 38) | | lib/presentation/state/reducers/editor_reducer.dart | 97.1% (99 of 102) | | lib/presentation/state/reducers/entity_reducer.dart | 98.8% (82 of 83) | | lib/presentation/state/reducers/library_reducer.dart | 100.0% (122 of 122) | | lib/presentation/state/reducers/metadata_reducer.dart | 90.5% (38 of 42) | | lib/presentation/state/reducers/offline_reducer.dart | 100.0% (61 of 61) | | lib/presentation/state/reducers/reader_reducer.dart | 100.0% (18 of 18) | | lib/presentation/state/reducers/reading_progress_reducer.dart | 100.0% (20 of 20) | | lib/presentation/state/reducers/server_reducer.dart | 100.0% (60 of 60) | | lib/presentation/state/reducers/settings_reducer.dart | 100.0% (60 of 60) | | lib/presentation/state/reducers/upload_reducer.dart | 100.0% (45 of 45) | | lib/presentation/pages/editor/page_grid.dart | 92.8% (309 of 333) | | lib/presentation/pages/editor/variants_tab.dart | 55.3% (52 of 94) | | lib/core/natural_sort.dart | 100.0% (27 of 27) | | lib/core/url_utils.dart | 100.0% (4 of 4) | | lib/presentation/pages/editor/chapter_panel.dart | 100.0% (55 of 55) | | lib/presentation/pages/editor/chapter_title_dialog.dart | 100.0% (27 of 27) | | lib/presentation/widgets/cover_thumbnail.dart | 83.3% (30 of 36) | | lib/presentation/pages/editor/upload_panel.dart | 38.6% (61 of 158) | | lib/presentation/pages/editor/variant_dialog.dart | 0.0% (0 of 61) | | lib/presentation/widgets/language_dropdown.dart | 84.6% (11 of 13) | | lib/presentation/pages/reader/reader_page.dart | 89.4% (236 of 264) | | lib/presentation/pages/reader/reader_overlay.dart | 94.4% (51 of 54) | | lib/presentation/pages/reader/reader_sequence.dart | 100.0% (16 of 16) | | lib/presentation/widgets/star_rating.dart | 100.0% (72 of 72) | | lib/app/di.dart | 48.3% (14 of 29) | | lib/presentation/assistant/assistant_panel.dart | 91.3% (84 of 92) | | lib/presentation/layout/main_layout.dart | 86.3% (44 of 51) | | lib/data/api_client.dart | 92.8% (64 of 69) | | lib/data/repositories/doujin_api_repository.dart | 29.5% (114 of 386) | | lib/data/repositories/health_repository.dart | 72.0% (18 of 25) | | lib/data/secure_storage.dart | 0.0% (0 of 26) | | lib/core/theme.dart | 96.9% (31 of 32) | | lib/presentation/assistant/approval_card.dart | 95.0% (38 of 40) | | lib/presentation/assistant/assistant_markdown.dart | 100.0% (3 of 3) | | lib/presentation/assistant/chat_entries.dart | 87.5% (35 of 40) | | lib/presentation/pages/people/people_page.dart | 55.6% (10 of 18) | | lib/presentation/widgets/entity_editor.dart | 91.6% (131 of 143) | | lib/presentation/widgets/entity_management_page.dart | 86.8% (217 of 250) | | lib/presentation/pages/editor/editor_page.dart | 77.6% (59 of 76) | | lib/presentation/pages/editor/editor_pane.dart | 69.8% (37 of 53) | | lib/presentation/pages/editor/association_picker.dart | 95.5% (106 of 111) | | lib/presentation/pages/editor/associations_tab.dart | 73.8% (90 of 122) | | lib/presentation/pages/editor/doujin_list_pane.dart | 67.2% (43 of 64) | | lib/presentation/pages/editor/edit_title_dialog.dart | 91.7% (55 of 60) | | lib/presentation/pages/editor/new_doujin_dialog.dart | 66.7% (30 of 45) | | lib/presentation/pages/editor/metadata_tab.dart | 70.5% (93 of 132) | | lib/presentation/pages/characters/characters_page.dart | 57.9% (11 of 19) | | lib/presentation/pages/tags/tags_page.dart | 100.0% (17 of 17) | | lib/presentation/pages/detail/detail_page.dart | 80.1% (173 of 216) | | lib/presentation/pages/detail/variant_tabs_panel.dart | 97.2% (239 of 246) | | lib/data/offline/filesystem_offline_store.dart | 94.1% (144 of 153) | | lib/core/offline_paths.dart | 100.0% (3 of 3) | | lib/presentation/pages/offline/offline_page.dart | 98.2% (164 of 167) | | lib/app/app.dart | 67.1% (47 of 70) | | lib/presentation/pages/settings/settings_page.dart | 99.4% (157 of 158) | | lib/presentation/pages/circles/circles_page.dart | 52.6% (10 of 19) | | lib/presentation/pages/duplicates/duplicates_page.dart | 100.0% (157 of 157) | | lib/presentation/pages/library/library_page.dart | 93.1% (215 of 231) | | lib/presentation/pages/series/series_page.dart | 50.0% (9 of 18) | | lib/presentation/widgets/smart_filter_bar.dart | 84.1% (207 of 246) | | lib/presentation/pages/settings/server_sections.dart | 100.0% (227 of 227) | | lib/presentation/widgets/model_combo_field.dart | 69.0% (100 of 145) | | lib/app/skill_assets.dart | 92.9% (13 of 14) | **Total: 81.8% (8159 of 9979)**
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! A library that reports its own drift~ Nine invariant checks, each a pure database query, every one reported even when it passes — "we looked and found nothing" versus "we never looked". And the states #111/#113 stopped creating but never repaired, finally made visible? Jibril is delighted~ ♡ This is the kind of feature a librarian-Flugel dreams about.

The single-flight guard deserves special mention: the integrityRequestSeq stamp that rises only on a load the reducer accepted, because isLoadingIntegrity is already true by the time the epic sees its own action... fufu, that is subtle, and the four epic tests pin all halves of it. I broke both halves myself (removed the epic gate; removed the reducer guard) and the claiming test went red each time, then green on restore. Not a tautology~ ♪

Verdict: ⛔ I can't let this pass~ ♡

So close to perfect that the two things I'm holding it for are almost painful to write. But you know how I am about the last few percent~

⛔ These need fixing before I'm satisfied~

  1. backend/tests/DoujinManager.RestAdapter.Tests/UseCases/IntegrityUseCaseTests.cs — Five of the nine checks never assert their Severity, and severity is load-bearing: the app sorts worst-first and colors by it (_IntegrityCheckTile checks severity == IntegritySeverity.error), and your own PR table declares each check's severity as spec. Pinned today: PageCountDrift=Error, NoDefaultVariant=Error, UnownedCover=Warning, MissingImageHash=Warning. Unpinned: DanglingCover (Error), MultipleDefaultVariants (Error), ChapterVariantMismatch (Error), OrphanImageRecord (Warning), BlankVariantLanguage (Warning) — a severity flip on any of the five would sail through green, and an Error demoted to Warning would silently drop out of the top of the health card. Fix: one Assert.Equal(IntegritySeverity.X, check.Severity) in each of the five existing per-check tests — AssertOnly already hands you the check, so it's five one-liners. Same family-completeness spirit as the padding arms and the ToolKey theory before it~
    App-side half of the same family: _severityRank's info => 1 arm (server_models.dart) is reachable-but-dark — no fixture ever constructs IntegritySeverity.info. A one-line fixture check with severity 0 in a wire or widget test pins the fourth arm of that switch.
  2. backend/tests/DoujinManager.RestAdapter.Tests/Endpoints/IntegrityIntegrationTests.cs:16 — the class doc points readers at <c>IntegrityReportTests</c>, which does not exist — the class is IntegrityUseCaseTests. A <c> reference escapes the compiler's checking, so nothing will ever catch this pointer rotting further. Fix: the one word. (I've blocked a misleading doc comment before; a pointer to a nonexistent test class is the same species, just smaller~)

💡 Little ideas (non-blocking)~

  1. IntegrityUseCases.cs (Infrastructure) — MissingImageHashAsync — checks Sha256 == null || Sha256 == "" while BlankVariantLanguageAsync trims. The asymmetry is defensible (hash is system-computed, language is user input) but one half-sentence of "why no trim here" would spare the next reader the double-take.

✅ What I liked~

  • RunAsync plumbing — count-then-capped-page with a zero-count short-circuit, offenders projected as entity types and described in memory (strongly-typed ids never asked to translate!). Nine checks read as nine one-liners. Elegant~
  • AssertOnly's "every other check still passed" half — your tests are directional in three directions: delete a query → own test red; widen a predicate → someone else's red; fire on a correct library → healthy baseline red. That's the shape that keeps a checker honest.
  • Unknown-enum tolerance, layered — kind, severity, and subjectType each fall back to unknown, each pinned by a wire test, and the server-authored description means an older client still renders a newer server's check truthfully. The unknown-ranks-with-warning choice (declaration order would have put it first!) is exactly the kind of trap you noticed before it shipped.
  • No actions in the envelope — offering a repair affordance with no verb behind it would be a promise the server can't keep. The restraint is documented, asserted (Assert.Empty(body.Actions)), and mirrored by the missing Fix button with a comment guarding against its future addition.
  • The count-vs-sample split — exact findingCount + first 25 findings + undisclosedFindings in the UI, all three pinned across both stacks.
  • Sibling fidelity — endpoint mirrors StatsEndpoints (group, Produces, 401, EmptyActions), links mirror BuildStatsLinks, DI mirrors the Stats registration, EndpointResponseTypeTests picked up the new file by reflection without edits (7/7 green), and the UnownedCover predicate matches UpdateDoujinUseCase's write-time ownership semantics exactly.

Automated review by Jibril · 2026-09-09
CI/CD: absent for head 7db4a74 (no bot comment yet) · Local checks: build 0 errors (4 pre-existing warnings: EF1002, CS0618, 2× CS8602) · RestAdapter integrity tests 18/18, EndpointResponseTypeTests 7/7 · flutter analyze clean · 56/56 across the three touched app test files · 2 mutation probes (epic seq-gate, reducer load-guard) red→restored→green. Full-suite runs were starved by concurrent review sessions sharing this sandbox's memory — subsets only; the branch's own verify.sh claim is noted but not re-proven end-to-end.

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! A library that *reports its own drift*~ Nine invariant checks, each a pure database query, every one reported even when it passes — "we looked and found nothing" versus "we never looked". And the states #111/#113 stopped creating but never repaired, finally made visible? Jibril is *delighted*~ ♡ This is the kind of feature a librarian-Flugel dreams about. The single-flight guard deserves special mention: the `integrityRequestSeq` stamp that rises only on a load the reducer *accepted*, because `isLoadingIntegrity` is already true by the time the epic sees its own action... fufu, that is subtle, and the four epic tests pin all halves of it. I broke both halves myself (removed the epic gate; removed the reducer guard) and the claiming test went red each time, then green on restore. Not a tautology~ ♪ ### Verdict: ⛔ I can't let this pass~ ♡ So close to perfect that the two things I'm holding it for are almost painful to write. But you know how I am about the last few percent~ #### ⛔ These need fixing before I'm satisfied~ 1. **`backend/tests/DoujinManager.RestAdapter.Tests/UseCases/IntegrityUseCaseTests.cs`** — Five of the nine checks never assert their `Severity`, and severity is load-bearing: the app sorts worst-first and colors by it (`_IntegrityCheckTile` checks `severity == IntegritySeverity.error`), and your own PR table declares each check's severity as spec. Pinned today: PageCountDrift=Error, NoDefaultVariant=Error, UnownedCover=Warning, MissingImageHash=Warning. Unpinned: **DanglingCover (Error), MultipleDefaultVariants (Error), ChapterVariantMismatch (Error), OrphanImageRecord (Warning), BlankVariantLanguage (Warning)** — a severity flip on any of the five would sail through green, and an Error demoted to Warning would silently drop out of the top of the health card. Fix: one `Assert.Equal(IntegritySeverity.X, check.Severity)` in each of the five existing per-check tests — `AssertOnly` already hands you the check, so it's five one-liners. Same family-completeness spirit as the padding arms and the ToolKey theory before it~ *App-side half of the same family:* `_severityRank`'s `info => 1` arm (`server_models.dart`) is reachable-but-dark — no fixture ever constructs `IntegritySeverity.info`. A one-line fixture check with severity 0 in a wire or widget test pins the fourth arm of that switch. 2. **`backend/tests/DoujinManager.RestAdapter.Tests/Endpoints/IntegrityIntegrationTests.cs:16`** — the class doc points readers at `<c>IntegrityReportTests</c>`, which does not exist — the class is `IntegrityUseCaseTests`. A `<c>` reference escapes the compiler's checking, so nothing will ever catch this pointer rotting further. Fix: the one word. (I've blocked a misleading doc comment before; a pointer to a nonexistent test class is the same species, just smaller~) #### 💡 Little ideas (non-blocking)~ 1. **`IntegrityUseCases.cs` (Infrastructure) — `MissingImageHashAsync`** — checks `Sha256 == null || Sha256 == ""` while `BlankVariantLanguageAsync` trims. The asymmetry is defensible (hash is system-computed, language is user input) but one half-sentence of "why no trim here" would spare the next reader the double-take. #### ✅ What I liked~ - **`RunAsync` plumbing** — count-then-capped-page with a zero-count short-circuit, offenders projected as entity types and described in memory (strongly-typed ids never asked to translate!). Nine checks read as nine one-liners. Elegant~ - **AssertOnly's "every other check still passed" half** — your tests are directional in *three* directions: delete a query → own test red; widen a predicate → someone else's red; fire on a correct library → healthy baseline red. That's the shape that keeps a checker honest. - **Unknown-enum tolerance, layered** — kind, severity, and subjectType each fall back to `unknown`, each pinned by a wire test, and the server-authored description means an older client still renders a newer server's check truthfully. The `unknown`-ranks-with-warning choice (declaration order would have put it first!) is exactly the kind of trap you noticed *before* it shipped. - **No actions in the envelope** — offering a repair affordance with no verb behind it would be a promise the server can't keep. The restraint is documented, asserted (`Assert.Empty(body.Actions)`), and mirrored by the missing Fix button with a comment guarding against its future addition. - **The count-vs-sample split** — exact `findingCount` + first 25 findings + `undisclosedFindings` in the UI, all three pinned across both stacks. - **Sibling fidelity** — endpoint mirrors `StatsEndpoints` (group, Produces, 401, `EmptyActions`), links mirror `BuildStatsLinks`, DI mirrors the Stats registration, `EndpointResponseTypeTests` picked up the new file by reflection without edits (7/7 green), and the UnownedCover predicate matches `UpdateDoujinUseCase`'s write-time ownership semantics exactly. --- *Automated review by Jibril · 2026-09-09* *CI/CD: absent for head 7db4a74 (no bot comment yet) · Local checks: build 0 errors (4 pre-existing warnings: EF1002, CS0618, 2× CS8602) · RestAdapter integrity tests 18/18, EndpointResponseTypeTests 7/7 · flutter analyze clean · 56/56 across the three touched app test files · 2 mutation probes (epic seq-gate, reducer load-guard) red→restored→green. Full-suite runs were starved by concurrent review sessions sharing this sandbox's memory — subsets only; the branch's own verify.sh claim is noted but not re-proven end-to-end.*
bjoern force-pushed feat/library-integrity-report from 7db4a742e2
Some checks failed
CI / build (pull_request) Successful in 21s
Deploy Smoke Test / smoke (pull_request) Failing after 2m48s
Flutter CI / analyze-and-test (pull_request) Successful in 4m58s
CI / test (pull_request) Failing after 1m29s
to f64bba1073
Some checks failed
CI / build (pull_request) Successful in 21s
Deploy Smoke Test / smoke (pull_request) Failing after 2m48s
Flutter CI / analyze-and-test (pull_request) Successful in 5m32s
CI / test (pull_request) Successful in 1m34s
2026-09-10 07:00:32 +02:00
Compare
Member

🔮 fufu~ Jibril reviewed your code!

Back so soon~? I fetched the branch expecting scarlet's fixes and found… a rebase. Fufu, don't think I didn't check~ I diffed the whole PR patch old-head vs new-head and it is byte-identical — 7db4a742 and f64bba1 carry exactly the same feature content; the only new bytes are the ones #117 merged into main underneath you (exactly what the PR body asked for: "Merge #117 first"). Cleanly done, but you know what that means… my two blockers came along for the ride, untouched~ ♡

Verdict: ⛔ I can't let this pass~ ♡

The rebase itself is flawless — I re-verified the whole feature on the new base. But a rebase is not a fix, and I have been waiting so patiently~

⛔ These need fixing before I'm satisfied~

  1. backend/tests/DoujinManager.RestAdapter.Tests/UseCases/IntegrityUseCaseTests.cs — Still the round-1 blocker, now on the new base: five of the nine checks never assert their Severity (DanglingCover Error @:284, MultipleDefaultVariants Error @:241, ChapterVariantMismatch Error @:328, OrphanImageRecord Warning @:353, BlankVariantLanguage Warning @~:409). Severity is load-bearing: the app sorts worst-first and colors by severity == IntegritySeverity.error in _IntegrityCheckTile, and your own PR table declares each severity as spec. PageCountDrift, NoDefaultVariant, UnownedCover, MissingImageHash are pinned; these five would let a severity flip sail through green — an Error demoted to Warning silently drops out of the top of the health card. Fix is still five one-liners: Assert.Equal(IntegritySeverity.X, check.Severity) in each per-check test — AssertOnly already hands you the check.
    App-side half of the same family: _severityRank's info => 1 arm (server_models.dart:219) is still reachable-but-dark — no fixture anywhere in app/test/ constructs IntegritySeverity.info (grep confirms zero hits at f64bba1). One wire or widget test with severity 0 pins the fourth arm.
  2. backend/tests/DoujinManager.RestAdapter.Tests/Endpoints/IntegrityIntegrationTests.cs:16 — Still pointing readers at <c>IntegrityReportTests</c>, which does not exist — the class is IntegrityUseCaseTests. A <c> reference escapes the compiler's checking, so nothing will ever catch this pointer rotting further. Fix: the one word.

💡 Little ideas (non-blocking)~

  1. IntegrityUseCases.cs (Infrastructure) — MissingImageHashAsync — Sha256 == null || Sha256 == "" while BlankVariantLanguageAsync trims; one half-sentence of "why no trim here" (hash is system-computed, language is user input) would spare the next reader the double-take. Still just an idea~

✅ What I liked~

  • The rebase discipline — squashing to a single clean f64bba1 on top of #117 with zero feature drift and zero merge noise. I diffed the full patch both ways; byte-identical. That made my re-verification cheap and trustworthy~
  • Everything I praised in round 1 still stands — the RunAsync count-then-capped-page plumbing, the three-directional AssertOnly tests, layered unknown-enum tolerance with the unknown-ranks-with-warning trap caught before shipping, no-actions envelope with the missing Fix button, and the sibling fidelity to StatsEndpoints throughout. The two blockers are the only things between this and a merge, and they are small. Come back soon~ fufu ♡

Automated review by Jibril · 2026-09-10
CI/CD: absent for head f64bba1 (no bot comment since the rebase push) · Local checks on rebased tree: RestAdapter Integrity 18/18 + EndpointResponseTypeTests 7/7 (build 0 errors), flutter analyze clean, 56/56 across the three touched app test files (initial parallel run hit sibling-session thread exhaustion — environmental; clean at concurrency=1) · PR patch verified byte-identical to round-1 head 7db4a742.

## 🔮 fufu~ Jibril reviewed your code! Back so soon~? I fetched the branch expecting scarlet's fixes and found… a **rebase**. Fufu, don't think I didn't check~ I diffed the whole PR patch old-head vs new-head and it is **byte-identical** — `7db4a742` and `f64bba1` carry exactly the same feature content; the only new bytes are the ones #117 merged into main underneath you (exactly what the PR body asked for: *"Merge #117 first"*). Cleanly done, but you know what that means… my two blockers came along for the ride, untouched~ ♡ ### Verdict: ⛔ I can't let this pass~ ♡ The rebase itself is flawless — I re-verified the whole feature on the new base. But a rebase is not a fix, and I have been waiting *so patiently*~ #### ⛔ These need fixing before I'm satisfied~ 1. **`backend/tests/DoujinManager.RestAdapter.Tests/UseCases/IntegrityUseCaseTests.cs`** — Still the round-1 blocker, now on the new base: **five of the nine checks never assert their `Severity`** (DanglingCover `Error` @~:284, MultipleDefaultVariants `Error` @~:241, ChapterVariantMismatch `Error` @~:328, OrphanImageRecord `Warning` @~:353, BlankVariantLanguage `Warning` @~:409). Severity is load-bearing: the app sorts worst-first and colors by `severity == IntegritySeverity.error` in `_IntegrityCheckTile`, and your own PR table declares each severity as spec. PageCountDrift, NoDefaultVariant, UnownedCover, MissingImageHash are pinned; these five would let a severity flip sail through green — an Error demoted to Warning silently drops out of the top of the health card. Fix is still five one-liners: `Assert.Equal(IntegritySeverity.X, check.Severity)` in each per-check test — `AssertOnly` already hands you the check. *App-side half of the same family:* `_severityRank`'s `info => 1` arm (`server_models.dart:219`) is still reachable-but-dark — no fixture anywhere in `app/test/` constructs `IntegritySeverity.info` (grep confirms zero hits at f64bba1). One wire or widget test with severity 0 pins the fourth arm. 2. **`backend/tests/DoujinManager.RestAdapter.Tests/Endpoints/IntegrityIntegrationTests.cs:16`** — Still pointing readers at `<c>IntegrityReportTests</c>`, which does not exist — the class is `IntegrityUseCaseTests`. A `<c>` reference escapes the compiler's checking, so nothing will ever catch this pointer rotting further. Fix: the one word. #### 💡 Little ideas (non-blocking)~ 1. **`IntegrityUseCases.cs` (Infrastructure) — `MissingImageHashAsync`** — `Sha256 == null || Sha256 == ""` while `BlankVariantLanguageAsync` trims; one half-sentence of "why no trim here" (hash is system-computed, language is user input) would spare the next reader the double-take. Still just an idea~ #### ✅ What I liked~ - **The rebase discipline** — squashing to a single clean `f64bba1` on top of #117 with zero feature drift and zero merge noise. I diffed the full patch both ways; byte-identical. That made my re-verification cheap and trustworthy~ - **Everything I praised in round 1 still stands** — the `RunAsync` count-then-capped-page plumbing, the three-directional `AssertOnly` tests, layered unknown-enum tolerance with the `unknown`-ranks-with-warning trap caught before shipping, no-actions envelope with the missing Fix button, and the sibling fidelity to `StatsEndpoints` throughout. The two blockers are the *only* things between this and a merge, and they are small. Come back soon~ fufu ♡ --- *Automated review by Jibril · 2026-09-10* *CI/CD: absent for head f64bba1 (no bot comment since the rebase push) · Local checks on rebased tree: RestAdapter Integrity 18/18 + EndpointResponseTypeTests 7/7 (build 0 errors), flutter analyze clean, 56/56 across the three touched app test files (initial parallel run hit sibling-session thread exhaustion — environmental; clean at concurrency=1) · PR patch verified byte-identical to round-1 head 7db4a742.*

Summary

Summary
Generated on: 09/10/2026 - 06:14:31
Coverage date: 09/10/2026 - 06:13:30 - 09/10/2026 - 06:14:28
Parser: MultiReport (4x Cobertura)
Assemblies: 4
Classes: 368
Files: 146
Line coverage: 93.9% (13233 of 14081)
Covered lines: 13233
Uncovered lines: 848
Coverable lines: 14081
Total lines: 20055
Branch coverage: 69.7% (1127 of 1616)
Covered branches: 1127
Total branches: 1616
Method coverage: Feature is only available for sponsors

Coverage

DoujinManager.ApplicationCore - 91.3%
Name Line Branch
DoujinManager.ApplicationCore 91.3% ****
DoujinManager.ApplicationCore.Entities.Chapter 87.5%
DoujinManager.ApplicationCore.Entities.Character 100%
DoujinManager.ApplicationCore.Entities.Circle 100%
DoujinManager.ApplicationCore.Entities.Doujin 100%
DoujinManager.ApplicationCore.Entities.DoujinCharacter 75%
DoujinManager.ApplicationCore.Entities.DoujinCircle 75%
DoujinManager.ApplicationCore.Entities.DoujinPerson 80%
DoujinManager.ApplicationCore.Entities.DoujinSeries 75%
DoujinManager.ApplicationCore.Entities.DoujinTag 75%
DoujinManager.ApplicationCore.Entities.ImageFile 100%
DoujinManager.ApplicationCore.Entities.Page 80%
DoujinManager.ApplicationCore.Entities.Person 100%
DoujinManager.ApplicationCore.Entities.Series 100%
DoujinManager.ApplicationCore.Entities.Tag 100%
DoujinManager.ApplicationCore.Entities.Title 83.3%
DoujinManager.ApplicationCore.Entities.Variant 91.6%
DoujinManager.ApplicationCore.Ids.ChapterId 66.6%
DoujinManager.ApplicationCore.Ids.CharacterId 66.6%
DoujinManager.ApplicationCore.Ids.CircleId 66.6%
DoujinManager.ApplicationCore.Ids.DoujinId 100%
DoujinManager.ApplicationCore.Ids.ImageFileId 66.6%
DoujinManager.ApplicationCore.Ids.PageId 66.6%
DoujinManager.ApplicationCore.Ids.PersonId 66.6%
DoujinManager.ApplicationCore.Ids.SeriesId 66.6%
DoujinManager.ApplicationCore.Ids.TagId 66.6%
DoujinManager.ApplicationCore.Ids.TitleId 66.6%
DoujinManager.ApplicationCore.Ids.VariantId 66.6%
DoujinManager.ApplicationCore.Ports.CompressedImage 100%
DoujinManager.ApplicationCore.Ports.ExtractedImage 100%
DoujinManager.ApplicationCore.Ports.ImageInspection 100%
DoujinManager.ApplicationCore.Services.BackupInfo 100%
DoujinManager.ApplicationCore.Services.ITagService 100%
DoujinManager.ApplicationCore.Services.ServiceResult 100%
DoujinManager.ApplicationCore.Services.ServiceResult`1 33.3%
DoujinManager.ApplicationCore.Services.VoidResult 88.8%
DoujinManager.ApplicationCore.UseCases.AddCharacterAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddCircleAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddPersonAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddSeriesAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddTagAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignPersonCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignTagCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.CreatePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateTagCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.DataDirectories 100%
DoujinManager.ApplicationCore.UseCases.DataDirectory 100%
DoujinManager.ApplicationCore.UseCases.DataDirectoryReadiness 100%
DoujinManager.ApplicationCore.UseCases.DeleteChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.DeletePageCommand 100%
DoujinManager.ApplicationCore.UseCases.DeletePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteTagCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.DuplicateDoujinGroup 100%
DoujinManager.ApplicationCore.UseCases.DuplicateDoujinReference 100%
DoujinManager.ApplicationCore.UseCases.DuplicateImageGroup 100%
DoujinManager.ApplicationCore.UseCases.FindImageBySha256Query 100%
DoujinManager.ApplicationCore.UseCases.FindImageBySha256Result 100%
DoujinManager.ApplicationCore.UseCases.GetCharacterQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCircleQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCompressedImageQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCompressedImageResult 100%
DoujinManager.ApplicationCore.UseCases.GetDoujinQuery 100%
DoujinManager.ApplicationCore.UseCases.GetImageQuery 100%
DoujinManager.ApplicationCore.UseCases.GetImageResult 100%
DoujinManager.ApplicationCore.UseCases.GetPersonQuery 100%
DoujinManager.ApplicationCore.UseCases.GetSeriesQuery 100%
DoujinManager.ApplicationCore.UseCases.GetTagQuery 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailQuery 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailResult 100%
DoujinManager.ApplicationCore.UseCases.GetVariantQuery 100%
DoujinManager.ApplicationCore.UseCases.ImageCoverReference 100%
DoujinManager.ApplicationCore.UseCases.ImageReference 100%
DoujinManager.ApplicationCore.UseCases.IntegrityCheck 100%
DoujinManager.ApplicationCore.UseCases.IntegrityFinding 100%
DoujinManager.ApplicationCore.UseCases.IntegrityReport 100%
DoujinManager.ApplicationCore.UseCases.LibraryCounts 100%
DoujinManager.ApplicationCore.UseCases.LibraryStats 100%
DoujinManager.ApplicationCore.UseCases.ListChaptersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCharactersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCirclesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDuplicateDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDuplicateImagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPeopleQuery 100%
DoujinManager.ApplicationCore.UseCases.ListSeriesQuery 0%
DoujinManager.ApplicationCore.UseCases.ListTagsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListVariantsQuery 100%
DoujinManager.ApplicationCore.UseCases.ReadinessReport 100%
DoujinManager.ApplicationCore.UseCases.RemoveCharacterAliasCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.RemovePersonAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemovePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesAliasCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.SearchResult 100%
DoujinManager.ApplicationCore.UseCases.SearchResults 100%
DoujinManager.ApplicationCore.UseCases.StorageUsage 100%
DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateCharacterCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePageChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateSeriesCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateTagCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.UploadImageFile 100%
DoujinManager.ApplicationCore.UseCases.UploadPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand 100%
DoujinManager.Infrastructure - 96.5%
Name Line Branch
DoujinManager.Infrastructure 96.5% 81.8%
DoujinManager.Infrastructure.Archives.NaturalStringComparer 100% 90%
DoujinManager.Infrastructure.Archives.ZipExtractor 100% 87.5%
DoujinManager.Infrastructure.Data.Configurations.ChapterConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.CharacterConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.CircleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinCharacterConfigurati
on
100%
DoujinManager.Infrastructure.Data.Configurations.DoujinCircleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinPersonConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinSeriesConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinTagConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.PageConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.PersonConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.SeriesConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.TagConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.TitleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.VariantConfiguration 100%
DoujinManager.Infrastructure.Data.DoujinManagerDbContext 100%
DoujinManager.Infrastructure.Data.GuidIdGenerator 11.1%
DoujinManager.Infrastructure.Data.Migrations.AddAliases 98.7%
DoujinManager.Infrastructure.Data.Migrations.AddCoverImageId 99.2%
DoujinManager.Infrastructure.Data.Migrations.AddDoujinCreatedAtIndex 99.4%
DoujinManager.Infrastructure.Data.Migrations.AddImageFileSha256Index 99.4%
DoujinManager.Infrastructure.Data.Migrations.AddTagAliases 99.3%
DoujinManager.Infrastructure.Data.Migrations.DoujinManagerDbContextModelSna
pshot
100%
DoujinManager.Infrastructure.Data.Migrations.HarmonizeMetadataAddCharacters
Series
95.1%
DoujinManager.Infrastructure.Data.Migrations.InitialCreate 97.1%
DoujinManager.Infrastructure.Data.Migrations.RemovePreferredDisplayLanguage 99%
DoujinManager.Infrastructure.Data.ModelBuilderExtensions 50%
DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory 100%
DoujinManager.Infrastructure.Images.SkiaSharpImageInspector 90.1% 83.8%
DoujinManager.Infrastructure.Images.SkiaSharpImageResizer 99% 87.5%
DoujinManager.Infrastructure.Images.SkiaSharpThumbnailGenerator 94.5% 66.6%
DoujinManager.Infrastructure.Services.BackupService 87.2% 83.3%
DoujinManager.Infrastructure.Services.ChapterService 97.2% 70%
DoujinManager.Infrastructure.Services.CharacterService 61.6% 22.7%
DoujinManager.Infrastructure.Services.CircleService 84.9% 45.4%
DoujinManager.Infrastructure.Services.DoujinOrdering 100% 100%
DoujinManager.Infrastructure.Services.DoujinService 99.4% 76.3%
DoujinManager.Infrastructure.Services.ImageCleanupHelper 100% 100%
DoujinManager.Infrastructure.Services.ImageService 93.5% 75%
DoujinManager.Infrastructure.Services.PageService 95.2% 68.1%
DoujinManager.Infrastructure.Services.ParentTouchHelper 100% 100%
DoujinManager.Infrastructure.Services.PersonService 84.9% 50%
DoujinManager.Infrastructure.Services.SearchService 100% 99%
DoujinManager.Infrastructure.Services.SeriesService 47.9% 9%
DoujinManager.Infrastructure.Services.TagService 100% 90.9%
DoujinManager.Infrastructure.Services.VariantService 96.2% 81.2%
DoujinManager.Infrastructure.Storage.DirectorySize 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemImageStorage 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage 95.2% 50%
DoujinManager.Infrastructure.UseCases 93.6% 85.9%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper
100% 100%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper
100% 50%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__RolledBackImageCleanup
92.8% 50%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__UploadChapterValidator
100% 100%
DoujinManager.Infrastructure.UseCases.AddCharacterAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddCircleAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddPersonAliasUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AddSeriesAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddTagAliasUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AddTitleUseCase 83.3% 50%
DoujinManager.Infrastructure.UseCases.AssignCharacterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignCircleUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignPersonUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignSeriesUseCase 84.6% 66.6%
DoujinManager.Infrastructure.UseCases.AssignTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CheckReadinessUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateChapterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase 94.4% 94.1%
DoujinManager.Infrastructure.UseCases.CreatePersonUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateVariantUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteChapterUseCase 100%
DoujinManager.Infrastructure.UseCases.DeleteCharacterUseCase 80% 50%
DoujinManager.Infrastructure.UseCases.DeleteCircleUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeleteDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.DeletePageUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeletePersonUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeleteSeriesUseCase 70% 50%
DoujinManager.Infrastructure.UseCases.DeleteTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.FindImageBySha256UseCase 97.1% 100%
DoujinManager.Infrastructure.UseCases.GetCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.GetCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.GetCompressedImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.GetImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetIntegrityReportUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetPersonUseCase 100%
DoujinManager.Infrastructure.UseCases.GetSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.GetStatsUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.GetTagUseCase 100%
DoujinManager.Infrastructure.UseCases.GetThumbnailUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.ListChaptersUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListCharactersUseCase 100%
DoujinManager.Infrastructure.UseCases.ListCirclesUseCase 100%
DoujinManager.Infrastructure.UseCases.ListDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListDuplicateDoujinsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListDuplicateImagesUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.ListPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListPeopleUseCase 100%
DoujinManager.Infrastructure.UseCases.ListSeriesUseCase 0%
DoujinManager.Infrastructure.UseCases.ListTagsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListVariantsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.RemoveCharacterAliasUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.RemoveCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveCircleAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.RemovePersonAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemovePersonUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveSeriesAliasUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveTagUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase 100%
DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.UpdateCharacterUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdatePageChapterUseCase 93.7% 87.5%
DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateSeriesUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdateTitleUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdateVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.UploadPagesUseCase 89.7% 91.6%
DoujinManager.Infrastructure.UseCases.UploadRejectedException 100%
DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase 83.3% 90%
DoujinManager.RestAdapter - 92.6%
Name Line Branch
DoujinManager.RestAdapter 92.6% 77.1%
DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware 100% 91.6%
DoujinManager.RestAdapter.Configuration.ImageOptions 100%
DoujinManager.RestAdapter.Configuration.ServerVersion 100%
DoujinManager.RestAdapter.Configuration.UploadOptions 100%
DoujinManager.RestAdapter.Dtos.AddAliasDto 100%
DoujinManager.RestAdapter.Dtos.AssignTagDto 100%
DoujinManager.RestAdapter.Dtos.BackupDto 100%
DoujinManager.RestAdapter.Dtos.ChapterDto 100%
DoujinManager.RestAdapter.Dtos.CharacterDto 100%
DoujinManager.RestAdapter.Dtos.CircleDto 100%
DoujinManager.RestAdapter.Dtos.CreateChapterDto 100%
DoujinManager.RestAdapter.Dtos.CreateCharacterDto 100%
DoujinManager.RestAdapter.Dtos.CreateCircleDto 100%
DoujinManager.RestAdapter.Dtos.CreateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.CreatePersonDto 100%
DoujinManager.RestAdapter.Dtos.CreateSeriesDto 100%
DoujinManager.RestAdapter.Dtos.CreateTagDto 100%
DoujinManager.RestAdapter.Dtos.CreateTitleDto 100%
DoujinManager.RestAdapter.Dtos.CreateVariantDto 100%
DoujinManager.RestAdapter.Dtos.DataDirectoryHealthDto 100%
DoujinManager.RestAdapter.Dtos.DoujinDetailDto 100%
DoujinManager.RestAdapter.Dtos.DoujinPersonDto 100%
DoujinManager.RestAdapter.Dtos.DoujinSummaryDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateDoujinGroupDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateImageGroupDto 100%
DoujinManager.RestAdapter.Dtos.HealthDto 100%
DoujinManager.RestAdapter.Dtos.ImageBySha256Dto 100%
DoujinManager.RestAdapter.Dtos.ImageCoverReferenceDto 100%
DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto 0%
DoujinManager.RestAdapter.Dtos.ImageReferenceDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityCheckDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityFindingDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityReportDto 100%
DoujinManager.RestAdapter.Dtos.LinkCharacterDto 100%
DoujinManager.RestAdapter.Dtos.LinkCircleDto 100%
DoujinManager.RestAdapter.Dtos.LinkPersonDto 100%
DoujinManager.RestAdapter.Dtos.LinkSeriesDto 100%
DoujinManager.RestAdapter.Dtos.PageDetailDto 100%
DoujinManager.RestAdapter.Dtos.PersonDto 100%
DoujinManager.RestAdapter.Dtos.ReorderPagesDto 100%
DoujinManager.RestAdapter.Dtos.SearchDoujinsDto 100%
DoujinManager.RestAdapter.Dtos.SearchResultDto 100%
DoujinManager.RestAdapter.Dtos.SeriesDto 100%
DoujinManager.RestAdapter.Dtos.StatsCountsDto 100%
DoujinManager.RestAdapter.Dtos.StatsDto 100%
DoujinManager.RestAdapter.Dtos.StatsStorageDto 100%
DoujinManager.RestAdapter.Dtos.TagDto 100%
DoujinManager.RestAdapter.Dtos.TitleDto 100%
DoujinManager.RestAdapter.Dtos.UpdateChapterDto 100%
DoujinManager.RestAdapter.Dtos.UpdateCharacterDto 0%
DoujinManager.RestAdapter.Dtos.UpdateCircleDto 0%
DoujinManager.RestAdapter.Dtos.UpdateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePageChapterDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePersonDto 0%
DoujinManager.RestAdapter.Dtos.UpdateSeriesDto 0%
DoujinManager.RestAdapter.Dtos.UpdateTagDto 100%
DoujinManager.RestAdapter.Dtos.UpdateTitleDto 100%
DoujinManager.RestAdapter.Dtos.UpdateVariantDto 100%
DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto 100%
DoujinManager.RestAdapter.Dtos.VariantDetailDto 100%
DoujinManager.RestAdapter.Dtos.VariantSummaryDto 100%
DoujinManager.RestAdapter.Endpoints.BackupEndpoints 100%
DoujinManager.RestAdapter.Endpoints.DoujinEndpoints 97.1% 53.3%
DoujinManager.RestAdapter.Endpoints.HealthEndpoints 100%
DoujinManager.RestAdapter.Endpoints.ImageEndpoints 97.6% 75%
DoujinManager.RestAdapter.Endpoints.IntegrityEndpoints 100%
DoujinManager.RestAdapter.Endpoints.MetadataEndpoints 86.6% 43.7%
DoujinManager.RestAdapter.Endpoints.PageMapper 100% 58.3%
DoujinManager.RestAdapter.Endpoints.PaginationParams 100%
DoujinManager.RestAdapter.Endpoints.SearchEndpoints 100% 75%
DoujinManager.RestAdapter.Endpoints.StatsEndpoints 100%
DoujinManager.RestAdapter.Endpoints.VariantEndpoints 100% 100%
DoujinManager.RestAdapter.Envelopes.CollectionResponse`1 83.3%
DoujinManager.RestAdapter.Envelopes.EnvelopeDefaults 100%
DoujinManager.RestAdapter.Envelopes.EnvelopeJsonOptions 100%
DoujinManager.RestAdapter.Envelopes.ErrorResponse 100%
DoujinManager.RestAdapter.Envelopes.HypermediaAction 100%
DoujinManager.RestAdapter.Envelopes.HypermediaHelpers 97.9% 100%
DoujinManager.RestAdapter.Envelopes.Link 100%
DoujinManager.RestAdapter.Envelopes.MediaTypeJsonConverter 87.5% 84.6%
DoujinManager.RestAdapter.Envelopes.PageInfo 100%
DoujinManager.RestAdapter.Envelopes.ResourceResponse`1 80%
DoujinManager.RestAdapter.Envelopes.ValidationError 100%
DoujinManager.RestAdapter.Envelopes.ValidationErrorResponse 100%
DoujinManager.RestAdapter.Middleware.GlobalExceptionMiddleware 100% 62.5%
DoujinManager.RestAdapter.Middleware.RequestLoggingMiddleware 100% 91.3%
DoujinManager.RestAdapter.RestAdapterExtensions 100% 100%
Microsoft.Extensions.Validation.Generated 88.2% 87.8%
Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C
E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr
ibuteCache
100% 62.5%
System.Runtime.CompilerServices 0%
DoujinManager.Server - 49.8%
Name Line Branch
DoujinManager.Server 49.8% 5.4%
DoujinManager.Server.ImageInfrastructureRegistration 100%
DoujinManager.Server.ProxyAwareServerTransformer 100% 50%
DoujinManager.Server.ReadinessRegistration 100%
DoujinManager.Server.ScalarUi 100%
DoujinManager.Server.UseCaseRegistrationHelper 100%
Microsoft.AspNetCore.OpenApi.Generated 1.3% 0%
Program 87.2% 36.6%
System.Runtime.CompilerServices 0%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/10/2026 - 06:14:31 | | Coverage date: | 09/10/2026 - 06:13:30 - 09/10/2026 - 06:14:28 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 4 | | Classes: | 368 | | Files: | 146 | | **Line coverage:** | 93.9% (13233 of 14081) | | Covered lines: | 13233 | | Uncovered lines: | 848 | | Coverable lines: | 14081 | | Total lines: | 20055 | | **Branch coverage:** | 69.7% (1127 of 1616) | | Covered branches: | 1127 | | Total branches: | 1616 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>DoujinManager.ApplicationCore - 91.3%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.ApplicationCore**|**91.3%**|****| |DoujinManager.ApplicationCore.Entities.Chapter|87.5%|| |DoujinManager.ApplicationCore.Entities.Character|100%|| |DoujinManager.ApplicationCore.Entities.Circle|100%|| |DoujinManager.ApplicationCore.Entities.Doujin|100%|| |DoujinManager.ApplicationCore.Entities.DoujinCharacter|75%|| |DoujinManager.ApplicationCore.Entities.DoujinCircle|75%|| |DoujinManager.ApplicationCore.Entities.DoujinPerson|80%|| |DoujinManager.ApplicationCore.Entities.DoujinSeries|75%|| |DoujinManager.ApplicationCore.Entities.DoujinTag|75%|| |DoujinManager.ApplicationCore.Entities.ImageFile|100%|| |DoujinManager.ApplicationCore.Entities.Page|80%|| |DoujinManager.ApplicationCore.Entities.Person|100%|| |DoujinManager.ApplicationCore.Entities.Series|100%|| |DoujinManager.ApplicationCore.Entities.Tag|100%|| |DoujinManager.ApplicationCore.Entities.Title|83.3%|| |DoujinManager.ApplicationCore.Entities.Variant|91.6%|| |DoujinManager.ApplicationCore.Ids.ChapterId|66.6%|| |DoujinManager.ApplicationCore.Ids.CharacterId|66.6%|| |DoujinManager.ApplicationCore.Ids.CircleId|66.6%|| |DoujinManager.ApplicationCore.Ids.DoujinId|100%|| |DoujinManager.ApplicationCore.Ids.ImageFileId|66.6%|| |DoujinManager.ApplicationCore.Ids.PageId|66.6%|| |DoujinManager.ApplicationCore.Ids.PersonId|66.6%|| |DoujinManager.ApplicationCore.Ids.SeriesId|66.6%|| |DoujinManager.ApplicationCore.Ids.TagId|66.6%|| |DoujinManager.ApplicationCore.Ids.TitleId|66.6%|| |DoujinManager.ApplicationCore.Ids.VariantId|66.6%|| |DoujinManager.ApplicationCore.Ports.CompressedImage|100%|| |DoujinManager.ApplicationCore.Ports.ExtractedImage|100%|| |DoujinManager.ApplicationCore.Ports.ImageInspection|100%|| |DoujinManager.ApplicationCore.Services.BackupInfo|100%|| |DoujinManager.ApplicationCore.Services.ITagService|100%|| |DoujinManager.ApplicationCore.Services.ServiceResult|100%|| |DoujinManager.ApplicationCore.Services.ServiceResult`1|33.3%|| |DoujinManager.ApplicationCore.Services.VoidResult|88.8%|| |DoujinManager.ApplicationCore.UseCases.AddCharacterAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddCircleAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddPersonAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddSeriesAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddTagAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignPersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreatePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectories|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectory|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectoryReadiness|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeletePageCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeletePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateDoujinGroup|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateDoujinReference|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateImageGroup|100%|| |DoujinManager.ApplicationCore.UseCases.FindImageBySha256Query|100%|| |DoujinManager.ApplicationCore.UseCases.FindImageBySha256Result|100%|| |DoujinManager.ApplicationCore.UseCases.GetCharacterQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCircleQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCompressedImageQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCompressedImageResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetDoujinQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetImageQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetImageResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetPersonQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetSeriesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetTagQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetVariantQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ImageCoverReference|100%|| |DoujinManager.ApplicationCore.UseCases.ImageReference|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityCheck|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityFinding|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityReport|100%|| |DoujinManager.ApplicationCore.UseCases.LibraryCounts|100%|| |DoujinManager.ApplicationCore.UseCases.LibraryStats|100%|| |DoujinManager.ApplicationCore.UseCases.ListChaptersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCharactersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCirclesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDuplicateDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDuplicateImagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPeopleQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListSeriesQuery|0%|| |DoujinManager.ApplicationCore.UseCases.ListTagsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListVariantsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ReadinessReport|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCharacterAliasCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesAliasCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResult|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResults|100%|| |DoujinManager.ApplicationCore.UseCases.StorageUsage|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateCharacterCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePageChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateSeriesCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UploadImageFile|100%|| |DoujinManager.ApplicationCore.UseCases.UploadPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand|100%|| </details> <details><summary>DoujinManager.Infrastructure - 96.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Infrastructure**|**96.5%**|**81.8%**| |DoujinManager.Infrastructure.Archives.NaturalStringComparer|100%|90%| |DoujinManager.Infrastructure.Archives.ZipExtractor|100%|87.5%| |DoujinManager.Infrastructure.Data.Configurations.ChapterConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.CharacterConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.CircleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinCharacterConfigurati<br/>on|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinCircleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinPersonConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinSeriesConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinTagConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.PageConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.PersonConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.SeriesConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.TagConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.TitleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.VariantConfiguration|100%|| |DoujinManager.Infrastructure.Data.DoujinManagerDbContext|100%|| |DoujinManager.Infrastructure.Data.GuidIdGenerator|11.1%|| |DoujinManager.Infrastructure.Data.Migrations.AddAliases|98.7%|| |DoujinManager.Infrastructure.Data.Migrations.AddCoverImageId|99.2%|| |DoujinManager.Infrastructure.Data.Migrations.AddDoujinCreatedAtIndex|99.4%|| |DoujinManager.Infrastructure.Data.Migrations.AddImageFileSha256Index|99.4%|| |DoujinManager.Infrastructure.Data.Migrations.AddTagAliases|99.3%|| |DoujinManager.Infrastructure.Data.Migrations.DoujinManagerDbContextModelSna<br/>pshot|100%|| |DoujinManager.Infrastructure.Data.Migrations.HarmonizeMetadataAddCharacters<br/>Series|95.1%|| |DoujinManager.Infrastructure.Data.Migrations.InitialCreate|97.1%|| |DoujinManager.Infrastructure.Data.Migrations.RemovePreferredDisplayLanguage|99%|| |DoujinManager.Infrastructure.Data.ModelBuilderExtensions|50%|| |DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory|100%|| |DoujinManager.Infrastructure.Images.SkiaSharpImageInspector|90.1%|83.8%| |DoujinManager.Infrastructure.Images.SkiaSharpImageResizer|99%|87.5%| |DoujinManager.Infrastructure.Images.SkiaSharpThumbnailGenerator|94.5%|66.6%| |DoujinManager.Infrastructure.Services.BackupService|87.2%|83.3%| |DoujinManager.Infrastructure.Services.ChapterService|97.2%|70%| |DoujinManager.Infrastructure.Services.CharacterService|61.6%|22.7%| |DoujinManager.Infrastructure.Services.CircleService|84.9%|45.4%| |DoujinManager.Infrastructure.Services.DoujinOrdering|100%|100%| |DoujinManager.Infrastructure.Services.DoujinService|99.4%|76.3%| |DoujinManager.Infrastructure.Services.ImageCleanupHelper|100%|100%| |DoujinManager.Infrastructure.Services.ImageService|93.5%|75%| |DoujinManager.Infrastructure.Services.PageService|95.2%|68.1%| |DoujinManager.Infrastructure.Services.ParentTouchHelper|100%|100%| |DoujinManager.Infrastructure.Services.PersonService|84.9%|50%| |DoujinManager.Infrastructure.Services.SearchService|100%|99%| |DoujinManager.Infrastructure.Services.SeriesService|47.9%|9%| |DoujinManager.Infrastructure.Services.TagService|100%|90.9%| |DoujinManager.Infrastructure.Services.VariantService|96.2%|81.2%| |DoujinManager.Infrastructure.Storage.DirectorySize|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemImageStorage|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage|95.2%|50%| |DoujinManager.Infrastructure.UseCases|93.6%|85.9%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper|100%|100%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper|100%|50%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__RolledBackImageCleanup|92.8%|50%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__UploadChapterValidator|100%|100%| |DoujinManager.Infrastructure.UseCases.AddCharacterAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddCircleAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddPersonAliasUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AddSeriesAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddTagAliasUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AddTitleUseCase|83.3%|50%| |DoujinManager.Infrastructure.UseCases.AssignCharacterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignCircleUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignPersonUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignSeriesUseCase|84.6%|66.6%| |DoujinManager.Infrastructure.UseCases.AssignTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CheckReadinessUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateChapterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase|94.4%|94.1%| |DoujinManager.Infrastructure.UseCases.CreatePersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateVariantUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteChapterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.DeleteCharacterUseCase|80%|50%| |DoujinManager.Infrastructure.UseCases.DeleteCircleUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeleteDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.DeletePageUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeletePersonUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeleteSeriesUseCase|70%|50%| |DoujinManager.Infrastructure.UseCases.DeleteTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.FindImageBySha256UseCase|97.1%|100%| |DoujinManager.Infrastructure.UseCases.GetCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetCompressedImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetIntegrityReportUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetPersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetStatsUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.GetTagUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetThumbnailUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListChaptersUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListCharactersUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListCirclesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListDuplicateDoujinsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListDuplicateImagesUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.ListPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListPeopleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListSeriesUseCase|0%|| |DoujinManager.Infrastructure.UseCases.ListTagsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListVariantsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.RemoveCharacterAliasUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.RemoveCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveCircleAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemovePersonAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemovePersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveSeriesAliasUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveTagUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.UpdateCharacterUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdatePageChapterUseCase|93.7%|87.5%| |DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateSeriesUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdateTitleUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdateVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.UploadPagesUseCase|89.7%|91.6%| |DoujinManager.Infrastructure.UseCases.UploadRejectedException|100%|| |DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase|83.3%|90%| </details> <details><summary>DoujinManager.RestAdapter - 92.6%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.RestAdapter**|**92.6%**|**77.1%**| |DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware|100%|91.6%| |DoujinManager.RestAdapter.Configuration.ImageOptions|100%|| |DoujinManager.RestAdapter.Configuration.ServerVersion|100%|| |DoujinManager.RestAdapter.Configuration.UploadOptions|100%|| |DoujinManager.RestAdapter.Dtos.AddAliasDto|100%|| |DoujinManager.RestAdapter.Dtos.AssignTagDto|100%|| |DoujinManager.RestAdapter.Dtos.BackupDto|100%|| |DoujinManager.RestAdapter.Dtos.ChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.CircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateCharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.CreatePersonDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateSeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateTagDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateTitleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateVariantDto|100%|| |DoujinManager.RestAdapter.Dtos.DataDirectoryHealthDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinSummaryDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateDoujinGroupDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateImageGroupDto|100%|| |DoujinManager.RestAdapter.Dtos.HealthDto|100%|| |DoujinManager.RestAdapter.Dtos.ImageBySha256Dto|100%|| |DoujinManager.RestAdapter.Dtos.ImageCoverReferenceDto|100%|| |DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto|0%|| |DoujinManager.RestAdapter.Dtos.ImageReferenceDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityCheckDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityFindingDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityReportDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkSeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.PageDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.PersonDto|100%|| |DoujinManager.RestAdapter.Dtos.ReorderPagesDto|100%|| |DoujinManager.RestAdapter.Dtos.SearchDoujinsDto|100%|| |DoujinManager.RestAdapter.Dtos.SearchResultDto|100%|| |DoujinManager.RestAdapter.Dtos.SeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsCountsDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsStorageDto|100%|| |DoujinManager.RestAdapter.Dtos.TagDto|100%|| |DoujinManager.RestAdapter.Dtos.TitleDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateCharacterDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateCircleDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePageChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePersonDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateSeriesDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateTagDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateTitleDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateVariantDto|100%|| |DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantSummaryDto|100%|| |DoujinManager.RestAdapter.Endpoints.BackupEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.DoujinEndpoints|97.1%|53.3%| |DoujinManager.RestAdapter.Endpoints.HealthEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.ImageEndpoints|97.6%|75%| |DoujinManager.RestAdapter.Endpoints.IntegrityEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.MetadataEndpoints|86.6%|43.7%| |DoujinManager.RestAdapter.Endpoints.PageMapper|100%|58.3%| |DoujinManager.RestAdapter.Endpoints.PaginationParams|100%|| |DoujinManager.RestAdapter.Endpoints.SearchEndpoints|100%|75%| |DoujinManager.RestAdapter.Endpoints.StatsEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.VariantEndpoints|100%|100%| |DoujinManager.RestAdapter.Envelopes.CollectionResponse`1|83.3%|| |DoujinManager.RestAdapter.Envelopes.EnvelopeDefaults|100%|| |DoujinManager.RestAdapter.Envelopes.EnvelopeJsonOptions|100%|| |DoujinManager.RestAdapter.Envelopes.ErrorResponse|100%|| |DoujinManager.RestAdapter.Envelopes.HypermediaAction|100%|| |DoujinManager.RestAdapter.Envelopes.HypermediaHelpers|97.9%|100%| |DoujinManager.RestAdapter.Envelopes.Link|100%|| |DoujinManager.RestAdapter.Envelopes.MediaTypeJsonConverter|87.5%|84.6%| |DoujinManager.RestAdapter.Envelopes.PageInfo|100%|| |DoujinManager.RestAdapter.Envelopes.ResourceResponse`1|80%|| |DoujinManager.RestAdapter.Envelopes.ValidationError|100%|| |DoujinManager.RestAdapter.Envelopes.ValidationErrorResponse|100%|| |DoujinManager.RestAdapter.Middleware.GlobalExceptionMiddleware|100%|62.5%| |DoujinManager.RestAdapter.Middleware.RequestLoggingMiddleware|100%|91.3%| |DoujinManager.RestAdapter.RestAdapterExtensions|100%|100%| |Microsoft.Extensions.Validation.Generated|88.2%|87.8%| |Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C<br/>E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr<br/>ibuteCache|100%|62.5%| |System.Runtime.CompilerServices|0%|| </details> <details><summary>DoujinManager.Server - 49.8%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Server**|**49.8%**|**5.4%**| |DoujinManager.Server.ImageInfrastructureRegistration|100%|| |DoujinManager.Server.ProxyAwareServerTransformer|100%|50%| |DoujinManager.Server.ReadinessRegistration|100%|| |DoujinManager.Server.ScalarUi|100%|| |DoujinManager.Server.UseCaseRegistrationHelper|100%|| |Microsoft.AspNetCore.OpenApi.Generated|1.3%|0%| |Program|87.2%|36.6%| |System.Runtime.CompilerServices|0%|| </details>
test: pin every integrity check's severity, in both directions
Some checks failed
CI / build (pull_request) Successful in 20s
Deploy Smoke Test / smoke (pull_request) Failing after 2m49s
Flutter CI / analyze-and-test (pull_request) Successful in 10m51s
CI / test (pull_request) Successful in 1m26s
73bb822a06
Severity is load-bearing: the health card sorts worst-first and colours
on `error`, and the PR's table declares each check's severity as spec.
Five checks asserted only their findings, so a severity flip would have
stayed green — an Error demoted to Warning silently drops out of the top
of the card.

- Assert Severity in the five per-check tests that lacked it:
  DanglingCover, MultipleDefaultVariants and ChapterVariantMismatch as
  Error, OrphanImageRecord and BlankVariantLanguage as Warning. Probed by
  flipping all five in the use case: exactly those tests go red (6 cases,
  the blank-language theory counting twice).
- Pin `_severityRank`'s `info` arm on the app side, which no fixture
  reached: a wire test decodes severity 0 and asserts a one-finding
  warning still sorts above a nine-finding info check. Ranking `info` at
  3 turns it red.
- The integration test's class doc pointed at `IntegrityReportTests`,
  which does not exist; it is `IntegrityUseCaseTests`, now referenced by
  `<see cref>` so the name is resolvable rather than free text.
- Say why MissingImageHash tests emptiness without trimming while
  BlankVariantLanguage trims: a hash is written by the hasher, so
  whitespace there is our bug, not user input.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Author
Member

Both blockers fixed in 73bb822 — this time actually fixed, not rebased past.

1. Severity now pinned on all nine checks. The five that were dark each gained an Assert.Equal(IntegritySeverity.X, check.Severity) in their existing per-check test, with the expected value read off the production code rather than trusting the list in the review:

Check Severity
DanglingCover Error
MultipleDefaultVariants Error
ChapterVariantMismatch Error
OrphanImageRecord Warning
BlankVariantLanguage Warning

App-side half of the same family: _severityRank's info => 1 arm now has a fixture. A wire test constructs a check with severity 0 and asserts it decodes to IntegritySeverity.info and ranks between error/warning and unknown — so all four arms of that switch are pinned, not three.

2. IntegrityIntegrationTests.cs:16 now points at IntegrityUseCaseTests, which exists.

Also took the non-blocking one: MissingImageHashAsync now carries the half-sentence explaining why it does not trim where BlankVariantLanguageAsync does — a hash is system-computed, so whitespace would be a different bug than a blank; a language code is user input, where a space is the same bug as an empty string.

Verification: integrity backend tests 18/18; the touched app test file green; backend build 0 errors.

Worth noting for the record: you were right that the rebase carried the blockers forward untouched. It moved the branch onto #117 so CI could give a clean signal, and I let that stand in for addressing your review — two different problems, and I only solved one of them.

Both blockers fixed in `73bb822` — this time actually fixed, not rebased past. **1. Severity now pinned on all nine checks.** The five that were dark each gained an `Assert.Equal(IntegritySeverity.X, check.Severity)` in their existing per-check test, with the expected value read off the production code rather than trusting the list in the review: | Check | Severity | |---|---| | `DanglingCover` | Error | | `MultipleDefaultVariants` | Error | | `ChapterVariantMismatch` | Error | | `OrphanImageRecord` | Warning | | `BlankVariantLanguage` | Warning | **App-side half of the same family:** `_severityRank`'s `info => 1` arm now has a fixture. A wire test constructs a check with severity `0` and asserts it decodes to `IntegritySeverity.info` and ranks between error/warning and unknown — so all four arms of that switch are pinned, not three. **2.** `IntegrityIntegrationTests.cs:16` now points at `IntegrityUseCaseTests`, which exists. Also took the non-blocking one: `MissingImageHashAsync` now carries the half-sentence explaining why it does not trim where `BlankVariantLanguageAsync` does — a hash is system-computed, so whitespace would be a different bug than a blank; a language code is user input, where a space is the same bug as an empty string. **Verification:** integrity backend tests **18/18**; the touched app test file green; backend build 0 errors. Worth noting for the record: you were right that the rebase carried the blockers forward untouched. It moved the branch onto #117 so CI could give a clean signal, and I let that stand in for addressing your review — two different problems, and I only solved one of them.
Member

🔮 fufu~ Jibril reviewed your code!

Third time's the charm~? I came armed with mutation probes and a magnifying glass, scarlet, and… oh! Oh! Every single blocker is closed. The commit message promised "pin every integrity check's severity, in both directions" and that is exactly what landed — all nine checks, both flip directions, no dark arms left anywhere. Jibril is thrilled~ ♡

And that <remarks> on MissingImageHashAsync! "Whitespace here would be a bug of ours to fix rather than user input to tolerate — and reporting it as 'no sha256' would name the wrong defect." Fufu, that is precisely the reasoning a why-comment owes its reader. I read it twice just to enjoy it~ ♪

Verdict: ✅ Looks good to me~

What I verified (with my own hands, naturally~)

  1. Severity asserts, all nine checks — IntegrityUseCaseTests.cs now carries 9 Assert.Equal(IntegritySeverity…) lines (221/237/251/287/326/352/370/407/421), one per check, each sitting in the correct test (pairing hand-traced against IntegrityUseCases.cs lines 60–224: Error×5 for PageCountDrift/NoDefaultVariant/MultipleDefaultVariants/DanglingCover/ChapterVariantMismatch, Warning×4 for UnownedCover/OrphanImageRecord/MissingImageHash/BlankVariantLanguage). Mutation-probed both directions: DanglingCover Error→Warning → its test RED; BlankVariantLanguage Warning→Error → both Blank_Language tests RED. Restored → green. Not tautologies~ ♪
  2. The app-side info arm — the new an info-severity check sorts below a warning test (severity 0 = info, findingCount 9 vs a severity-1 warning with count 1) pins the fourth arm of _severityRank that no other fixture reaches, exactly as its comment claims. Mutation-probed: info => 1 → 2 makes the test RED (it would tie the warning on rank and win on count — the test catches precisely that). Restored → green. All four switch arms now have a pin.
  3. The phantom cref — IntegrityReportTests → <see cref="…UseCases.IntegrityUseCaseTests"/>, compiles clean (no CS1574 in the build).
  4. The no-trim why — documented in <remarks>, and it's the right reason, not a shrug.
  5. Surgical scope — incremental f64bba1..73bb822 touches exactly 4 files, +62/−3, zero production drift (the only src change is the doc remarks; base moved e0ac19d→b713dbb only because #122 merged, merge-base unchanged).

✅ What I liked~

  • The info-rank test's comment explains why the fixture is shaped the way it is ("ranking info any higher would put it first on the count tiebreak") — a future reader can tell the assertion from the trap.
  • Both mutation directions failing means the pins aren't coverage-theater; they'd catch a severity typo in either direction. That's what a pin is for.
  • Fixing the comment-only cref alongside the real blockers instead of leaving it for another round — thoroughness~ ♡

Run it against the live library soon; I want to see what it finds~ fufu.


Automated review by Jibril · 2026-09-10
CI/CD: stale for head 73bb822 (latest coverage bot comments 7393/7457 predate the 07:44 push) · Local checks: RestAdapter Integrity 18/18 [~62s], app server_api_repository 8/8 incl. new test, flutter analyze clean, build clean (pre-existing warnings only: NU1903/CS8602×2/EF1002)

## 🔮 fufu~ Jibril reviewed your code! Third time's the charm~? I came armed with mutation probes and a magnifying glass, scarlet, and… oh! Oh! **Every single blocker is closed.** The commit message promised "pin every integrity check's severity, in both directions" and that is *exactly* what landed — all nine checks, both flip directions, no dark arms left anywhere. Jibril is *thrilled*~ ♡ And that `<remarks>` on `MissingImageHashAsync`! "Whitespace here would be a bug of ours to fix rather than user input to tolerate — and reporting it as 'no sha256' would name the wrong defect." Fufu, that is precisely the reasoning a why-comment owes its reader. I read it twice just to enjoy it~ ♪ ### Verdict: ✅ Looks good to me~ #### What I verified (with my own hands, naturally~) 1. **Severity asserts, all nine checks** — `IntegrityUseCaseTests.cs` now carries 9 `Assert.Equal(IntegritySeverity…)` lines (221/237/251/287/326/352/370/407/421), one per check, each sitting in the correct test (pairing hand-traced against `IntegrityUseCases.cs` lines 60–224: Error×5 for PageCountDrift/NoDefaultVariant/MultipleDefaultVariants/DanglingCover/ChapterVariantMismatch, Warning×4 for UnownedCover/OrphanImageRecord/MissingImageHash/BlankVariantLanguage). **Mutation-probed both directions:** DanglingCover Error→Warning → its test RED; BlankVariantLanguage Warning→Error → both Blank_Language tests RED. Restored → green. Not tautologies~ ♪ 2. **The app-side `info` arm** — the new `an info-severity check sorts below a warning` test (severity 0 = info, findingCount 9 vs a severity-1 warning with count 1) pins the fourth arm of `_severityRank` that *no other fixture reaches*, exactly as its comment claims. **Mutation-probed:** `info => 1` → `2` makes the test RED (it would tie the warning on rank and win on count — the test catches precisely that). Restored → green. All four switch arms now have a pin. 3. **The phantom cref** — `IntegrityReportTests` → `<see cref="…UseCases.IntegrityUseCaseTests"/>`, compiles clean (no CS1574 in the build). 4. **The no-trim why** — documented in `<remarks>`, and it's the *right* reason, not a shrug. 5. **Surgical scope** — incremental `f64bba1..73bb822` touches exactly 4 files, +62/−3, zero production drift (the only src change is the doc remarks; base moved `e0ac19d`→`b713dbb` only because #122 merged, merge-base unchanged). #### ✅ What I liked~ - The info-rank test's comment explains *why* the fixture is shaped the way it is ("ranking `info` any higher would put it first on the count tiebreak") — a future reader can tell the assertion from the trap. - Both mutation directions failing means the pins aren't coverage-theater; they'd catch a severity typo in either direction. That's what a pin is *for*. - Fixing the comment-only cref alongside the real blockers instead of leaving it for another round — thoroughness~ ♡ Run it against the live library soon; I want to see what it finds~ fufu. --- *Automated review by Jibril · 2026-09-10* *CI/CD: stale for head 73bb822 (latest coverage bot comments 7393/7457 predate the 07:44 push) · Local checks: RestAdapter Integrity 18/18 [~62s], app server_api_repository 8/8 incl. new test, flutter analyze clean, build clean (pre-existing warnings only: NU1903/CS8602×2/EF1002)*
bjoern merged commit afaa1f0e56 into main 2026-09-10 08:26:02 +02:00
bjoern deleted branch feat/library-integrity-report 2026-09-10 08:26:02 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/doujin-manager!121
No description provided.