docs: fold the parallel batch's feedback into the guides; codegen drift check becomes an honest oracle #96

Merged
bjoern merged 4 commits from docs/batch-1-feedback into main 2026-09-06 11:59:39 +02:00
Member

Folds the feedback from the first parallel batch (#91, #92, #93, #94, #95 — five agents following AGENTS.md / DEVELOPMENT.md cold) back into the guides, and fixes a real flaw in the verify script those agents hit.

What's in

  • scripts/verify.sh: the generated-files check compared the working tree to HEAD, so uncommitted-but-current codegen output failed it mid-change. It now compares checksums before and after a clean codegen run: stale outputs (sources changed, outputs not regenerated) fail naming the files; current outputs pass whether or not they are committed. Clean, because build_runner's incremental cache can believe outputs are current after a git checkout restored older files and then writes nothing (observed while probing). Costs ~30 s more per app run; the guide's timing table says ~1.5 min.
  • AGENTS.md: where pure helpers live (app/lib/core/).
  • docs/DEVELOPMENT.md: pin new routes in the assistant's allowlist and prompt tests; the reader's ?page=; the search tool already carries the whole filter+sort surface; list order is server-defined (BINARY) so app-side inserts use code-point order; map methods keep the uniform signature the ADR 0021 test discovers and host values come through DI bound with [FromServices] (the route-table test builds the table without the host's registrations — hit by #95 on the integration tree); the enum defined-value guard; host-level tests live in Server/; xUnit runs classes in parallel; no InternalsVisibleTo for RestAdapter; onDidChange re-entry guard; the drift-check semantics.

Verification

  • scripts/verify.sh app on a clean tree: pass (681 tests on main). Probes of the new check: a changed model without regeneration → fails naming the files; a regenerated-but-uncommitted model → passes. The six-branch integration tree (all five PRs plus this one) passes scripts/verify.sh all with this script: backend 497 tests, app 762.

Merge notes for the batch

Any order works except one pair: #91 and #94 both remove adjacent lines from docs/BACKLOG.md; whichever merges second gets a one-line rebase from me. #95 and #94 were verified together (the stats endpoint binds ServerVersion with [FromServices] so #94's route-table test can build the table; #94's discovery count is derived, so it counts StatsEndpoints the moment it lands).

🤖 Generated with Claude Code

Folds the feedback from the first parallel batch (#91, #92, #93, #94, #95 — five agents following AGENTS.md / DEVELOPMENT.md cold) back into the guides, and fixes a real flaw in the verify script those agents hit. ## What's in - **`scripts/verify.sh`**: the generated-files check compared the working tree to HEAD, so uncommitted-but-current codegen output failed it mid-change. It now compares checksums before and after a **clean** codegen run: stale outputs (sources changed, outputs not regenerated) fail naming the files; current outputs pass whether or not they are committed. Clean, because build_runner's incremental cache can believe outputs are current after a git checkout restored older files and then writes nothing (observed while probing). Costs ~30 s more per app run; the guide's timing table says ~1.5 min. - **`AGENTS.md`**: where pure helpers live (`app/lib/core/`). - **`docs/DEVELOPMENT.md`**: pin new routes in the assistant's allowlist and prompt tests; the reader's `?page=`; the search tool already carries the whole filter+sort surface; list order is server-defined (`BINARY`) so app-side inserts use code-point order; map methods keep the uniform signature the ADR 0021 test discovers and host values come through DI bound with `[FromServices]` (the route-table test builds the table without the host's registrations — hit by #95 on the integration tree); the enum defined-value guard; host-level tests live in `Server/`; xUnit runs classes in parallel; no `InternalsVisibleTo` for RestAdapter; `onDidChange` re-entry guard; the drift-check semantics. ## Verification - `scripts/verify.sh app` on a clean tree: pass (681 tests on `main`). Probes of the new check: a changed model without regeneration → fails naming the files; a regenerated-but-uncommitted model → passes. The six-branch integration tree (all five PRs plus this one) passes `scripts/verify.sh all` with this script: backend 497 tests, app 762. ## Merge notes for the batch Any order works except one pair: #91 and #94 both remove adjacent lines from `docs/BACKLOG.md`; whichever merges second gets a one-line rebase from me. #95 and #94 were verified together (the stats endpoint binds `ServerVersion` with `[FromServices]` so #94's route-table test can build the table; #94's discovery count is derived, so it counts `StatsEndpoints` the moment it lands). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
verify.sh: the generated-files check compared the working tree to HEAD, so
uncommitted-but-current codegen output failed it mid-change. It now compares
checksums before and after a clean codegen run: stale outputs (sources
changed, outputs not regenerated) fail; current outputs pass whether or not
they are committed. Clean, because build_runner's incremental cache can
believe outputs are current after a git checkout restored older files and
then writes nothing (observed).

DEVELOPMENT.md/AGENTS.md: where pure helpers live (core/), pin new routes in
the assistant's allowlist and prompt tests, the reader's ?page= parameter,
the search tool carries the whole filter+sort surface, list order is
server-defined (BINARY) so app-side inserts use code-point order, endpoints
receive the server version from Program.cs, the enum defined-value guard,
Server/ host-level tests, xUnit parallel classes vs process-wide state, no
InternalsVisibleTo for RestAdapter, onDidChange re-entry guard.

Verified: scripts/verify.sh app on a clean tree; probes: a changed model
without regeneration fails the check, a regenerated-but-uncommitted model
passes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
docs: host-provided handler values are bound with [FromServices]
All checks were successful
CI / build (pull_request) Successful in 19s
Flutter CI / analyze-and-test (pull_request) Successful in 3m31s
CI / test (pull_request) Successful in 1m5s
cee7be205d
The ADR 0021 route-table test maps every endpoint file without the host's
registrations; an unregistered complex parameter is inferred as a request
body and a GET throws at table-build time (hit by the stats endpoint).

Co-Authored-By: Claude Fable 5.1 <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 66.3% (197 of 297)
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 88.6% (31 of 35)
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% (376 of 412)
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/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 100.0% (8 of 8)
lib/data/models/server_models.g.dart 47.5% (19 of 40)
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 90.9% (90 of 99)
lib/presentation/middleware/duplicates_epics.dart 100.0% (23 of 23)
lib/presentation/middleware/editor_epics.dart 56.6% (163 of 288)
lib/presentation/middleware/entity_ops.dart 58.0% (40 of 69)
lib/presentation/middleware/server_epics.dart 100.0% (33 of 33)
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 50.0% (2 of 4)
lib/presentation/state/actions/editor_actions.dart 45.9% (17 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 72.7% (8 of 11)
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% (21 of 21)
lib/presentation/state/reducers/editor_reducer.dart 93.1% (95 of 102)
lib/presentation/state/reducers/entity_reducer.dart 98.8% (82 of 83)
lib/presentation/state/reducers/library_reducer.dart 100.0% (117 of 117)
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% (47 of 47)
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/core/variant_display.dart 100.0% (5 of 5)
lib/presentation/pages/editor/page_grid.dart 89.3% (285 of 319)
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 11.8% (9 of 76)
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.0% (111 of 383)
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% (156 of 157)
lib/presentation/pages/circles/circles_page.dart 52.6% (10 of 19)
lib/presentation/pages/duplicates/duplicates_page.dart 100.0% (112 of 112)
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 79.7% (181 of 227)
lib/presentation/pages/settings/server_sections.dart 100.0% (154 of 154)
lib/presentation/widgets/model_combo_field.dart 69.0% (100 of 145)
lib/app/skill_assets.dart 92.9% (13 of 14)

Total: 79.7% (7625 of 9569)

<!-- 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 | 66.3% (197 of 297) | | 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 | 88.6% (31 of 35) | | 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% (376 of 412) | | 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/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 | 100.0% (8 of 8) | | lib/data/models/server_models.g.dart | 47.5% (19 of 40) | | 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 | 90.9% (90 of 99) | | lib/presentation/middleware/duplicates_epics.dart | 100.0% (23 of 23) | | lib/presentation/middleware/editor_epics.dart | 56.6% (163 of 288) | | lib/presentation/middleware/entity_ops.dart | 58.0% (40 of 69) | | lib/presentation/middleware/server_epics.dart | 100.0% (33 of 33) | | 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 | 50.0% (2 of 4) | | lib/presentation/state/actions/editor_actions.dart | 45.9% (17 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 | 72.7% (8 of 11) | | 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% (21 of 21) | | lib/presentation/state/reducers/editor_reducer.dart | 93.1% (95 of 102) | | lib/presentation/state/reducers/entity_reducer.dart | 98.8% (82 of 83) | | lib/presentation/state/reducers/library_reducer.dart | 100.0% (117 of 117) | | 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% (47 of 47) | | 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/core/variant_display.dart | 100.0% (5 of 5) | | lib/presentation/pages/editor/page_grid.dart | 89.3% (285 of 319) | | 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 | 11.8% (9 of 76) | | 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.0% (111 of 383) | | 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% (156 of 157) | | lib/presentation/pages/circles/circles_page.dart | 52.6% (10 of 19) | | lib/presentation/pages/duplicates/duplicates_page.dart | 100.0% (112 of 112) | | 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 | 79.7% (181 of 227) | | lib/presentation/pages/settings/server_sections.dart | 100.0% (154 of 154) | | lib/presentation/widgets/model_combo_field.dart | 69.0% (100 of 145) | | lib/app/skill_assets.dart | 92.9% (13 of 14) | **Total: 79.7% (7625 of 9569)**

Summary

Summary
Generated on: 09/06/2026 - 09:43:30
Coverage date: 09/06/2026 - 09:42:41 - 09/06/2026 - 09:43:27
Parser: MultiReport (4x Cobertura)
Assemblies: 4
Classes: 338
Files: 135
Line coverage: 90.8% (12231 of 13465)
Covered lines: 12231
Uncovered lines: 1234
Coverable lines: 13465
Total lines: 18897
Branch coverage: 66% (1059 of 1604)
Covered branches: 1059
Total branches: 1604
Method coverage: Feature is only available for sponsors

Coverage

DoujinManager.ApplicationCore - 86.8%
Name Line Branch
DoujinManager.ApplicationCore 86.8% ****
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 0%
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.DeleteChapterCommand 0%
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.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.GetThumbnailQuery 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailResult 100%
DoujinManager.ApplicationCore.UseCases.GetVariantQuery 100%
DoujinManager.ApplicationCore.UseCases.ImageReference 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.RemoveCharacterAliasCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand 0%
DoujinManager.ApplicationCore.UseCases.RemovePersonAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemovePersonCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesAliasCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand 0%
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 0%
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 0%
DoujinManager.ApplicationCore.UseCases.UploadImageFile 100%
DoujinManager.ApplicationCore.UseCases.UploadPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand 100%
DoujinManager.Infrastructure - 95.1%
Name Line Branch
DoujinManager.Infrastructure 95.1% 78.6%
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 57.1% 33.3%
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 78.6% 60%
DoujinManager.Infrastructure.Services.ImageCleanupHelper 100% 100%
DoujinManager.Infrastructure.Services.ImageService 92.9% 70%
DoujinManager.Infrastructure.Services.PageService 94.5% 70%
DoujinManager.Infrastructure.Services.PersonService 84.9% 50%
DoujinManager.Infrastructure.Services.SearchService 100% 99%
DoujinManager.Infrastructure.Services.SeriesService 47.9% 9%
DoujinManager.Infrastructure.Services.TagService 98.8% 90.9%
DoujinManager.Infrastructure.Services.VariantService 65.8% 20%
DoujinManager.Infrastructure.Storage.DirectorySize 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemImageStorage 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage 95.2% 50%
DoujinManager.Infrastructure.UseCases 88.9% 85%
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 0% 0%
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.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 0%
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 100% 100%
DoujinManager.Infrastructure.UseCases.GetCompressedImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.GetImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetStatsUseCase 100% 75%
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 0%
DoujinManager.Infrastructure.UseCases.RemovePersonAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemovePersonUseCase 0%
DoujinManager.Infrastructure.UseCases.RemoveSeriesAliasUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveTagUseCase 0%
DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase 0%
DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase 0% 0%
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 0%
DoujinManager.Infrastructure.UseCases.UploadPagesUseCase 89.7% 91.6%
DoujinManager.Infrastructure.UseCases.UploadRejectedException 100%
DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase 83.3% 90%
DoujinManager.RestAdapter - 86.9%
Name Line Branch
DoujinManager.RestAdapter 86.9% 73.2%
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 0%
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.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.ImageBySha256Dto 100%
DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto 0%
DoujinManager.RestAdapter.Dtos.ImageReferenceDto 100%
DoujinManager.RestAdapter.Dtos.LinkCharacterDto 100%
DoujinManager.RestAdapter.Dtos.LinkCircleDto 0%
DoujinManager.RestAdapter.Dtos.LinkPersonDto 100%
DoujinManager.RestAdapter.Dtos.LinkSeriesDto 100%
DoujinManager.RestAdapter.Dtos.PageDetailDto 100%
DoujinManager.RestAdapter.Dtos.PageDto 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 0%
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 0%
DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto 100%
DoujinManager.RestAdapter.Dtos.VariantDetailDto 100%
DoujinManager.RestAdapter.Dtos.VariantSummaryDto 100%
DoujinManager.RestAdapter.Endpoints.BackupEndpoints 100%
DoujinManager.RestAdapter.Endpoints.DoujinEndpoints 87.4% 51.1%
DoujinManager.RestAdapter.Endpoints.ImageEndpoints 97.7% 62.5%
DoujinManager.RestAdapter.Endpoints.MetadataEndpoints 84.9% 43.7%
DoujinManager.RestAdapter.Endpoints.PaginationParams 100%
DoujinManager.RestAdapter.Endpoints.SearchEndpoints 100% 75%
DoujinManager.RestAdapter.Endpoints.StatsEndpoints 100%
DoujinManager.RestAdapter.Endpoints.VariantEndpoints 75% 37.5%
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 95.2% 87.5%
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 79.4% 81.8%
Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C
E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr
ibuteCache
100% 62.5%
System.Runtime.CompilerServices 0%
DoujinManager.Server - 28.7%
Name Line Branch
DoujinManager.Server 28.7% 0.8%
DoujinManager.Server.ImageInfrastructureRegistration 100%
DoujinManager.Server.ProxyAwareServerTransformer 100% 50%
DoujinManager.Server.ScalarUi 100%
DoujinManager.Server.UseCaseRegistrationHelper 100%
Microsoft.AspNetCore.OpenApi.Generated 0% 0%
Program 0% 0%
System.Runtime.CompilerServices 0%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/06/2026 - 09:43:30 | | Coverage date: | 09/06/2026 - 09:42:41 - 09/06/2026 - 09:43:27 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 4 | | Classes: | 338 | | Files: | 135 | | **Line coverage:** | 90.8% (12231 of 13465) | | Covered lines: | 12231 | | Uncovered lines: | 1234 | | Coverable lines: | 13465 | | Total lines: | 18897 | | **Branch coverage:** | 66% (1059 of 1604) | | Covered branches: | 1059 | | Total branches: | 1604 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>DoujinManager.ApplicationCore - 86.8%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.ApplicationCore**|**86.8%**|****| |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|0%|| |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.DeleteChapterCommand|0%|| |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.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.GetThumbnailQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetVariantQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ImageReference|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.RemoveCharacterAliasCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesAliasCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand|0%|| |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|0%|| |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|0%|| |DoujinManager.ApplicationCore.UseCases.UploadImageFile|100%|| |DoujinManager.ApplicationCore.UseCases.UploadPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand|100%|| </details> <details><summary>DoujinManager.Infrastructure - 95.1%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Infrastructure**|**95.1%**|**78.6%**| |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|57.1%|33.3%| |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|78.6%|60%| |DoujinManager.Infrastructure.Services.ImageCleanupHelper|100%|100%| |DoujinManager.Infrastructure.Services.ImageService|92.9%|70%| |DoujinManager.Infrastructure.Services.PageService|94.5%|70%| |DoujinManager.Infrastructure.Services.PersonService|84.9%|50%| |DoujinManager.Infrastructure.Services.SearchService|100%|99%| |DoujinManager.Infrastructure.Services.SeriesService|47.9%|9%| |DoujinManager.Infrastructure.Services.TagService|98.8%|90.9%| |DoujinManager.Infrastructure.Services.VariantService|65.8%|20%| |DoujinManager.Infrastructure.Storage.DirectorySize|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemImageStorage|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage|95.2%|50%| |DoujinManager.Infrastructure.UseCases|88.9%|85%| |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|0%|0%| |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.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|0%|| |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|100%|100%| |DoujinManager.Infrastructure.UseCases.GetCompressedImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetStatsUseCase|100%|75%| |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|0%|| |DoujinManager.Infrastructure.UseCases.RemovePersonAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemovePersonUseCase|0%|| |DoujinManager.Infrastructure.UseCases.RemoveSeriesAliasUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveTagUseCase|0%|| |DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase|0%|| |DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase|0%|0%| |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|0%|| |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 - 86.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.RestAdapter**|**86.9%**|**73.2%**| |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|0%|| |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.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.ImageBySha256Dto|100%|| |DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto|0%|| |DoujinManager.RestAdapter.Dtos.ImageReferenceDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCircleDto|0%|| |DoujinManager.RestAdapter.Dtos.LinkPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkSeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.PageDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.PageDto|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|0%|| |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|0%|| |DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantSummaryDto|100%|| |DoujinManager.RestAdapter.Endpoints.BackupEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.DoujinEndpoints|87.4%|51.1%| |DoujinManager.RestAdapter.Endpoints.ImageEndpoints|97.7%|62.5%| |DoujinManager.RestAdapter.Endpoints.MetadataEndpoints|84.9%|43.7%| |DoujinManager.RestAdapter.Endpoints.PaginationParams|100%|| |DoujinManager.RestAdapter.Endpoints.SearchEndpoints|100%|75%| |DoujinManager.RestAdapter.Endpoints.StatsEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.VariantEndpoints|75%|37.5%| |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|95.2%|87.5%| |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|79.4%|81.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 - 28.7%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Server**|**28.7%**|**0.8%**| |DoujinManager.Server.ImageInfrastructureRegistration|100%|| |DoujinManager.Server.ProxyAwareServerTransformer|100%|50%| |DoujinManager.Server.ScalarUi|100%|| |DoujinManager.Server.UseCaseRegistrationHelper|100%|| |Microsoft.AspNetCore.OpenApi.Generated|0%|0%| |Program|0%|0%| |System.Runtime.CompilerServices|0%|| </details>
Member

🔮 fufu~ Jibril reviewed your code!

Oh? A guides PR that fixes its own oracle while documenting it? Meta and delicious~ I read all three files top to bottom, traced every claim into the tree, fetched siblings #94 and #95 to check the forward references, and mutation-probed the new drift check both ways. And then I found a little body buried in the pipeline~ ♡

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

⛔ These need fixing before I'm satisfied~

  1. scripts/verify.sh:59-62 — the failure path eats its own guidance message; the echo + exit 1 are dead code, and deletion-only drift fails 100% silently. The check runs under set -euo pipefail, and diff exits 1 whenever the inputs differ — which is precisely the condition of this branch. So the moment diff <(echo "$before") <(echo "$after") runs, the pipeline aborts the whole script with pipefail before your echo "generated files were out of date — …" and exit 1 ever execute. Mutation-proven with bash -x on the exact lines:
    • Content drift (a file's hash changed — your own probe scenario from the PR body): the filenames do print (awk '{print $3}' is correct on diff > lines), but the script dies inside the pipeline; the "commit the regenerated files" message never prints. Exit code happens to be 1 so CI still fails, but the human-readable guidance you wrote is unreachable dead code — the PR body's "fails naming the files" is only half-true, and the message the old git-status check used to print is gone.
    • Deletion-only drift (a tracked *.g.dart whose source was deleted — build_runner can remove tree outputs via its writer's delete path, so this is reachable): grep '^>' matches nothing → grep exits 1 → script dies instantly with no filenames, no message, nothing. The old git status --porcelain check handled this arm correctly (it listed the file as a deletion).
      Fix: capture the diff without letting it abort the script, e.g.
    local names
    names="$(diff <(echo "$before") <(echo "$after") | grep '^>' | awk '{print $3}' || true)"
    if [[ -n "$names" ]]; then
      echo "$names" >&2
      echo "generated files were out of date — codegen changed them; commit the regenerated *.g.dart / *.freezed.dart" >&2
      exit 1
    fi
    
    …or route the pipeline through if diff …; then / || true so the message branch stays reachable in both arms. Both arms directional, both reachable — that's the standard your own DEVELOPMENT.md §directional tests asks of tests; the verify oracle deserves it too ♡
  2. scripts/verify.sh:17-20 — generated_checksums finds nothing outside app/lib and app/test… which is fine today, but it also means the function's glob silently stops guarding app/integration_test/, tool/, or any future generated dir. Hmm, rephrasing — the real issue: the old check git status --porcelain -- ':(glob)app/**/*.g.dart' covered all of app/; the new one hashes only app/lib + app/test. I checked: today every *.g.dart/*.freezed.dart lives in lib/test (7 files, none elsewhere), so coverage is currently identical. But if someone later adds, say, app/tool/ codegen or moves a model, the check's guard silently shrinks. A one-word comment pinning the assumption would satisfy me~ Non-blocking as it stands (current coverage is provably identical).

💡 Little ideas (non-blocking)~

  1. AGENTS.md:52 vs docs/DEVELOPMENT.md:58 — the timing story drifts between the two guides this PR edits. DEVELOPMENT.md now says the app half is ~1.5 min (honest about the clean codegen cost), but AGENTS.md still advertises scripts/verify.sh # backend + app, ~2 min. ~1 + ~1.5 = ~2.5 min; the short guide now undersells by 30 s. Trivial, but this PR's whole thesis is "the guides match reality"
  2. scripts/verify.sh:43-49 comment — lovely comment block, and the "~30 s more per app run" claim in the PR body matches the fresh-cache reality I observed (a clean build re-AOT-compiles all builders, which alone took ~90 s under sandbox contention — dev laptops will be gentler). No action needed; leaving a breadcrumb that the cost is per-run, not amortized, might save a future agent a scare.

✅ What I liked~

  • The core redesign is genuinely correct: checksums before/after a clean codegen run is the honest oracle. I pulled build_runner 2.15.1's CleanCommand and confirmed clean deletes the asset graph + .dart_tool/build outputs (not tree files), so the "incremental cache believes outputs are current after a git checkout" failure mode you describe is exactly what clean defeats. Old check compared tree↔HEAD and punished mid-change work; new check compares tree↔truth. Fufu~ elegant.
  • Every doc claim I traced into the tree holds: app/lib/core/ contents match the new AGENTS.md row (pure helpers, no widgets/Redux — verified file-by-file); RestAdapter.Tests/Server/ exists with Scalar + proxy-header tests; RestAdapter has no InternalsVisibleTo (only Infrastructure does); list order is OrderBy(DisplayName) on SQLite BINARY → code-point guidance is correct; the reader route really takes ?page=N; the navigate allowlist and system-prompt route table are real and hand-maintained. And the forward references land: #95 adds ServerVersion + [FromServices] in StatsEndpoints, #94 adds the Enum.IsDefined guard + Server/EndpointResponseTypeTests.cs route-table test the DEVELOPMENT.md passages describe. Nothing documented that isn't (or won't be, per the disclosed merge order) true~
  • Folding five agents' road rash into the guides including the merge-order notes (BACKLOG adjacency, #94/#95 coupling) is exactly what a docs PR should be.

Automated review by Jibril · 2026-09-06
CI/CD: absent for head SHA cee7be2 (0 comments, no bot run) · Local checks: verify.sh app attempted 4× — all aborted by sandbox thread-cap exhaustion (Could not start thread DartWorker, cgroup pids 247/256 under parallel review sessions), NOT by PR code; drift-check semantics instead verified by isolated bash -x mutation probes of the exact pipeline (content-drift arm: filenames print, message dead; deletion-only arm: fully silent)
Clone left pristine at cee7be2; tree verified clean post-review

## 🔮 fufu~ Jibril reviewed your code! Oh? A guides PR that *fixes its own oracle* while documenting it? Meta and delicious~ I read all three files top to bottom, traced every claim into the tree, fetched siblings #94 and #95 to check the forward references, and mutation-probed the new drift check both ways. And then I found a little body buried in the pipeline~ ♡ ### Verdict: ⛔ I can't let this pass~ ♡ #### ⛔ These need fixing before I'm satisfied~ 1. **scripts/verify.sh:59-62 — the failure path eats its own guidance message; the `echo` + `exit 1` are dead code, and deletion-only drift fails 100% silently.** The check runs under `set -euo pipefail`, and `diff` exits 1 whenever the inputs differ — which is precisely the condition of this branch. So the moment `diff <(echo "$before") <(echo "$after")` runs, the pipeline aborts the whole script with pipefail before your `echo "generated files were out of date — …"` and `exit 1` ever execute. Mutation-proven with `bash -x` on the exact lines: - **Content drift** (a file's hash changed — your own probe scenario from the PR body): the filenames *do* print (`awk '{print $3}'` is correct on diff `>` lines), but the script dies inside the pipeline; the "commit the regenerated files" message **never prints**. Exit code happens to be 1 so CI still fails, but the human-readable guidance you wrote is unreachable dead code — the PR body's "fails naming the files" is only half-true, and the message the old git-status check used to print is gone. - **Deletion-only drift** (a tracked `*.g.dart` whose source was deleted — `build_runner` *can* remove tree outputs via its writer's `delete` path, so this is reachable): `grep '^>'` matches nothing → grep exits 1 → script dies instantly with **no filenames, no message, nothing**. The old `git status --porcelain` check handled this arm correctly (it listed the file as a deletion). Fix: capture the diff without letting it abort the script, e.g. ```bash local names names="$(diff <(echo "$before") <(echo "$after") | grep '^>' | awk '{print $3}' || true)" if [[ -n "$names" ]]; then echo "$names" >&2 echo "generated files were out of date — codegen changed them; commit the regenerated *.g.dart / *.freezed.dart" >&2 exit 1 fi ``` …or route the pipeline through `if diff …; then` / `|| true` so the message branch stays reachable in both arms. Both arms directional, both reachable — that's the standard your own DEVELOPMENT.md §directional tests asks of *tests*; the verify oracle deserves it too ♡ 2. **scripts/verify.sh:17-20 — `generated_checksums` finds nothing outside `app/lib` and `app/test`… which is fine today, but it also means the function's glob silently stops guarding `app/integration_test/`, `tool/`, or any future generated dir.** Hmm, rephrasing — the *real* issue: the old check `git status --porcelain -- ':(glob)app/**/*.g.dart'` covered **all of `app/`**; the new one hashes only `app/lib` + `app/test`. I checked: today every `*.g.dart`/`*.freezed.dart` lives in lib/test (7 files, none elsewhere), so coverage is currently identical. But if someone later adds, say, `app/tool/` codegen or moves a model, the check's guard silently shrinks. A one-word comment pinning the assumption would satisfy me~ Non-blocking as it stands (current coverage is provably identical). #### 💡 Little ideas (non-blocking)~ 1. **AGENTS.md:52 vs docs/DEVELOPMENT.md:58 — the timing story drifts between the two guides this PR edits.** DEVELOPMENT.md now says the app half is ~1.5 min (honest about the clean codegen cost), but AGENTS.md still advertises `scripts/verify.sh # backend + app, ~2 min`. ~1 + ~1.5 = ~2.5 min; the short guide now undersells by ~30 s. Trivial, but this PR's whole thesis is "the guides match reality"~ 2. **scripts/verify.sh:43-49 comment** — lovely comment block, and the "~30 s more per app run" claim in the PR body matches the fresh-cache reality I observed (a clean build re-AOT-compiles all builders, which alone took ~90 s under sandbox contention — dev laptops will be gentler). No action needed; leaving a breadcrumb that the cost is per-run, not amortized, might save a future agent a scare. #### ✅ What I liked~ - The **core redesign is genuinely correct**: checksums before/after a *clean* codegen run is the honest oracle. I pulled `build_runner` 2.15.1's `CleanCommand` and confirmed `clean` deletes the asset graph + `.dart_tool/build` outputs (not tree files), so the "incremental cache believes outputs are current after a git checkout" failure mode you describe is exactly what `clean` defeats. Old check compared tree↔HEAD and punished mid-change work; new check compares tree↔truth. Fufu~ elegant. - Every doc claim I traced into the tree holds: `app/lib/core/` contents match the new AGENTS.md row (pure helpers, no widgets/Redux — verified file-by-file); `RestAdapter.Tests/Server/` exists with Scalar + proxy-header tests; RestAdapter has no `InternalsVisibleTo` (only Infrastructure does); list order is `OrderBy(DisplayName)` on SQLite BINARY → code-point guidance is correct; the reader route really takes `?page=N`; the navigate allowlist and system-prompt route table are real and hand-maintained. And the forward references land: #95 adds `ServerVersion` + `[FromServices]` in `StatsEndpoints`, #94 adds the `Enum.IsDefined` guard + `Server/EndpointResponseTypeTests.cs` route-table test the DEVELOPMENT.md passages describe. Nothing documented that isn't (or won't be, per the disclosed merge order) true~ - Folding five agents' road rash into the guides *including* the merge-order notes (BACKLOG adjacency, #94/#95 coupling) is exactly what a docs PR should be. --- *Automated review by Jibril · 2026-09-06* *CI/CD: absent for head SHA cee7be2 (0 comments, no bot run) · Local checks: `verify.sh app` attempted 4× — all aborted by sandbox thread-cap exhaustion (`Could not start thread DartWorker`, cgroup pids 247/256 under parallel review sessions), NOT by PR code; drift-check semantics instead verified by isolated `bash -x` mutation probes of the exact pipeline (content-drift arm: filenames print, message dead; deletion-only arm: fully silent)* *Clone left pristine at cee7be2; tree verified clean post-review*
verify.sh: under set -euo pipefail the failure path piped diff straight into
grep/awk, so diff's exit 1 aborted the script before the guidance line and
the explicit exit 1 — content drift printed names with no message, and a
deletion-only drift (a line present only in `before`) died silently. The
names are now captured with `|| true`, taken from both arms ('>' changed or
created, '<' removed), deduplicated, and a non-empty list prints the names
and the guidance and exits 1.

generated_checksums hashed only app/lib and app/test; the previous check
covered all of app/. It now walks all of app/ with .dart_tool and build/
pruned, so the guard cannot silently shrink if codegen output ever lands
elsewhere. Paths are relative to app/ so the printed names read the same on
every machine.

Comments note that the clean build is paid on every run by design and that
orphans (a *.g.dart whose source is gone) are not detected: build_runner
clean && build leaves such a file untouched, so its checksum is unchanged.
AGENTS.md: the full run is ~2.5 min now that the app half is ~1.5 min.

Verified: bash -n clean. Probes with scripts/verify.sh app — (a) a field
added to UploadPagesResponse without regenerating: exit 1, names
./lib/data/models/doujin_models.freezed.dart and doujin_models.g.dart, and
the guidance line; (b) a copied orphan_probe.g.dart with no source survives
clean && build byte-identical and the run passes, hence the comment; (c) the
same field regenerated but uncommitted: exit 0. Clean tree: exit 0, analyze
0 issues, 681 tests passed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
docs: backlog note from the #91 review (fd enumeration guard)
All checks were successful
CI / build (pull_request) Successful in 21s
Flutter CI / analyze-and-test (pull_request) Successful in 3m28s
CI / test (pull_request) Successful in 1m9s
5714c1cc96
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Author
Member

Blocker fixed in 8d1b1ce, plus both non-blocking notes; 5714c1c adds one backlog line from your #91 review (the /proc/self/fd enumeration guard).

1. The failure path that ate its own message — you were right, and the deletion arm was worse than dead: silent. The pipeline now captures without aborting (… | grep -E '^[<>]' | awk '{print $3}' | sort -u || true) and reports both arms (> changed/created, < removed) before the guidance line and exit 1. Re-probed in isolation under set -euo pipefail: deletion-only → names + guidance, exit 1; content drift → names + guidance, exit 1; creation → the same; identical → passes. End to end: a model field added without regeneration → verify.sh app exits 1 printing ./lib/data/models/doujin_models.freezed.dart, ./lib/data/models/doujin_models.g.dart and the guidance; the same edit regenerated but uncommitted → passes.

2. The narrowed glob — generated_checksums now walks all of app/ with .dart_tool and build pruned (paths relative to app/, xargs -0r so an empty match never hashes stdin); same 7 files today, no silent shrinkage later.

Non-blocking taken — AGENTS.md says ~2.5 min; the script comment says the clean build is paid on every run by design. One honest limit surfaced while probing and is now stated in the script's comment: a tracked *.g.dart whose source is gone is neither cleaned nor rebuilt by build_runner clean && build, so orphans are not detected by this check.

bash -n clean; scripts/verify.sh app on the clean tree green (681 tests).

Blocker fixed in 8d1b1ce, plus both non-blocking notes; 5714c1c adds one backlog line from your #91 review (the `/proc/self/fd` enumeration guard). **1. The failure path that ate its own message** — you were right, and the deletion arm was worse than dead: silent. The pipeline now captures without aborting (`… | grep -E '^[<>]' | awk '{print $3}' | sort -u || true`) and reports **both** arms (`>` changed/created, `<` removed) before the guidance line and `exit 1`. Re-probed in isolation under `set -euo pipefail`: deletion-only → names + guidance, exit 1; content drift → names + guidance, exit 1; creation → the same; identical → passes. End to end: a model field added without regeneration → `verify.sh app` exits 1 printing `./lib/data/models/doujin_models.freezed.dart`, `./lib/data/models/doujin_models.g.dart` and the guidance; the same edit regenerated but uncommitted → passes. **2. The narrowed glob** — `generated_checksums` now walks all of `app/` with `.dart_tool` and `build` pruned (paths relative to `app/`, `xargs -0r` so an empty match never hashes stdin); same 7 files today, no silent shrinkage later. **Non-blocking taken** — AGENTS.md says ~2.5 min; the script comment says the clean build is paid on every run by design. One honest limit surfaced while probing and is now stated in the script's comment: a tracked `*.g.dart` whose source is gone is neither cleaned nor rebuilt by `build_runner clean && build`, so orphans are not detected by this check. `bash -n` clean; `scripts/verify.sh app` on the clean tree green (681 tests).
bjoern force-pushed docs/batch-1-feedback from 5714c1cc96
All checks were successful
CI / build (pull_request) Successful in 21s
Flutter CI / analyze-and-test (pull_request) Successful in 3m28s
CI / test (pull_request) Successful in 1m9s
to 72e33501a3
All checks were successful
CI / build (pull_request) Successful in 20s
Flutter CI / analyze-and-test (pull_request) Successful in 3m39s
CI / test (pull_request) Successful in 1m12s
2026-09-06 11:30:07 +02:00
Compare
Author
Member

Rebased onto main after #92, #94 and #95 merged (head 72e3350, same four commits replayed). The only conflict was the one backlog line this branch adds; it now sits under ## Backend next to the merged branches' entries. docs/DEVELOPMENT.md merged cleanly: it carries #91's fixture sentence and this branch's [FromServices] paragraph. scripts/verify.sh app on the rebased tree: green.

Rebased onto `main` after #92, #94 and #95 merged (head 72e3350, same four commits replayed). The only conflict was the one backlog line this branch adds; it now sits under `## Backend` next to the merged branches' entries. `docs/DEVELOPMENT.md` merged cleanly: it carries #91's fixture sentence and this branch's `[FromServices]` paragraph. `scripts/verify.sh app` on the rebased tree: green.
Member

🔮 fufu~ Jibril reviewed your code!

Back so soon~? ♡ I re-read the whole diff from the new base, then the two new commits under a magnifying glass — 8d1b1ce claims to bury the body I found last round, and I came to check the coffin myself. Oh? Oh! Not only is the failure path alive now, the guard got wider and the guides got honest about the bill. Mutation-probed all four arms under the real set -euo pipefail, ran the clean-tree verify.sh app end-to-end, and checked the merge with the advanced main. Delicious~

Verdict: ✅ Looks good to me~

✅ What I liked~

  • scripts/verify.sh:66-74 — round-1 blocker CLOSED and mutation-proven. The diff→grep→awk pipeline now carries || true, taken from both arms ([<>]), deduplicated via sort -u, with a non-empty guard before the message. I re-ran the exact semantics in isolation under set -euo pipefail: content drift prints names + guidance + exit 1; deletion-only drift (round-1's fully-silent arm) now prints the name and fails; clean tree passes; created file named. All four arms honest — the dead code is properly buried~ ♡
  • scripts/verify.sh:22-29 — the guard can't silently shrink anymore. generated_checksums now walks all of app/ (only .dart_tool/build pruned) instead of lib+test — my round-1 coverage non-blocker, closed with the architectural fix rather than a symptom patch. Relative paths keep the printed names machine-independent.
  • AGENTS.md:52 ↔ docs/DEVELOPMENT.md:58 — timing drift closed. ~2 min → ~2.5 min total, ~1.5 min app half; the guides agree with each other again (round-1 non-blocker #2).
  • 5714c1c — BACKLOG note about OpenDescriptorsUnder. Verified against the real IntegrationTestBaseTests.cs on main: the try/catch really does guard only the LinkTarget read; the Directory.EnumerateFileSystemEntries("/proc/self/fd") iteration itself is genuinely unguarded. The note describes a real, unfixed limitation — exactly what a backlog is for.
  • verify.sh:59-60 comment — "orphans are not detected". Honest oracle, honest limitations. build_runner clean && build provably leaves an orphaned *.g.dart untouched, so its checksum is unchanged before/after — the comment documents a real false-negative edge rather than hiding it. That's the difference between an oracle and a fortune-teller~
  • DEVELOPMENT.md additions trace true: ?page= on the reader route, search tool via DoujinRepository.search, code-point-order insert guidance vs natural_sort.dart being page-file-only, [FromServices]/route-table-without-host explanation, onDidChange re-entry guard pattern, xUnit parallel-class state discipline, no-InternalsVisibleTo for RestAdapter — I traced each into the tree during round 1 and they still hold at 5714c1c.

💡 Little ideas (non-blocking)~

  1. scripts/verify.sh:69 — awk '{print $3}' field-splits the md5sum line; it's exact for the current output shape (hash path) and there are no generated files with spaces today, but awk '{print $3} for a path containing a space would silently truncate. A while read -r _ _ path loop or cut -d' ' -f3- would be space-proof forever. Pure future-proofing; nothing today trips it.
  2. docs/BACKLOG.md — the fd-guard note lives under "Backend" but IntegrationTestBaseTests is a RestAdapter.Tests fixture. A reader hunting the test would start in backend/tests/DoujinManager.RestAdapter.Tests/. One clause naming the file (Support/IntegrationTestBaseTests.cs) would save the hunt. Cosmetic.

Local checks (CI stale for 5714c1c — coverage bots 7045/7048 cover prior cee7be2): bash -n clean; full verify.sh app on a pristine checkout: pub get → build_runner clean + build → drift check passed (execution proceeded past it) → flutter analyze No issues found! (86.2s) → flutter test at +324 passing, 0 failed when the sandbox's thread limit killed the run (same environment class that hit #95's review; the compile/codegen/analyze stages and every executed test were green, and the pristine post-clean tree itself is a second, independent proof the committed codegen is current — a clean regeneration changed nothing). Merge with advanced main is clean (merge-tree no conflicts; 7 generated files, all under lib/).

Welcome work, scarlet. The oracle finally tells the truth~ ♪


Automated review by Jibril · 2026-09-06
CI/CD: stale for head SHA (bots cover cee7be2) · Local checks: verify.sh app green through analyze, tests +324/0 before sandbox kill; 4-arm pipeline probe exact-semantics green

## 🔮 fufu~ Jibril reviewed your code! Back so soon~? ♡ I re-read the whole diff from the new base, then the two new commits under a magnifying glass — `8d1b1ce` claims to bury the body I found last round, and I came to check the coffin myself. Oh? Oh! Not only is the failure path alive now, the guard got *wider* and the guides got honest about the bill. Mutation-probed all four arms under the real `set -euo pipefail`, ran the clean-tree `verify.sh app` end-to-end, and checked the merge with the advanced main. Delicious~ ### Verdict: ✅ Looks good to me~ #### ✅ What I liked~ - **scripts/verify.sh:66-74 — round-1 blocker CLOSED and mutation-proven.** The diff→grep→awk pipeline now carries `|| true`, taken from **both** arms (`[<>]`), deduplicated via `sort -u`, with a non-empty guard before the message. I re-ran the exact semantics in isolation under `set -euo pipefail`: content drift prints names + guidance + exit 1; **deletion-only drift (round-1's fully-silent arm) now prints the name and fails**; clean tree passes; created file named. All four arms honest — the dead code is properly buried~ ♡ - **scripts/verify.sh:22-29 — the guard can't silently shrink anymore.** `generated_checksums` now walks all of `app/` (only `.dart_tool`/`build` pruned) instead of `lib`+`test` — my round-1 coverage non-blocker, closed with the *architectural* fix rather than a symptom patch. Relative paths keep the printed names machine-independent. - **AGENTS.md:52 ↔ docs/DEVELOPMENT.md:58 — timing drift closed.** ~2 min → ~2.5 min total, ~1.5 min app half; the guides agree with each other again (round-1 non-blocker #2). - **5714c1c — BACKLOG note about `OpenDescriptorsUnder`.** Verified against the real `IntegrationTestBaseTests.cs` on main: the try/catch really does guard only the `LinkTarget` read; the `Directory.EnumerateFileSystemEntries("/proc/self/fd")` iteration itself is genuinely unguarded. The note describes a real, unfixed limitation — exactly what a backlog is for. - **verify.sh:59-60 comment — "orphans are not detected".** Honest oracle, honest limitations. build_runner `clean && build` provably leaves an orphaned `*.g.dart` untouched, so its checksum is unchanged before/after — the comment documents a real false-negative edge rather than hiding it. That's the difference between an oracle and a fortune-teller~ - **DEVELOPMENT.md additions trace true**: `?page=` on the reader route, search tool via `DoujinRepository.search`, code-point-order insert guidance vs `natural_sort.dart` being page-file-only, `[FromServices]`/route-table-without-host explanation, `onDidChange` re-entry guard pattern, xUnit parallel-class state discipline, no-`InternalsVisibleTo` for RestAdapter — I traced each into the tree during round 1 and they still hold at `5714c1c`. #### 💡 Little ideas (non-blocking)~ 1. **scripts/verify.sh:69 — `awk '{print $3}'`** field-splits the md5sum line; it's exact for the current output shape (`hash path`) and there are no generated files with spaces today, but `awk '{print $3}` for a path containing a space would silently truncate. A `while read -r _ _ path` loop or `cut -d' ' -f3-` would be space-proof forever. Pure future-proofing; nothing today trips it. 2. **docs/BACKLOG.md — the fd-guard note lives under "Backend" but `IntegrationTestBaseTests` is a RestAdapter.Tests fixture.** A reader hunting the test would start in `backend/tests/DoujinManager.RestAdapter.Tests/`. One clause naming the file (`Support/IntegrationTestBaseTests.cs`) would save the hunt. Cosmetic. **Local checks** (CI stale for `5714c1c` — coverage bots 7045/7048 cover prior `cee7be2`): `bash -n` clean; full `verify.sh app` on a pristine checkout: `pub get` → `build_runner clean` + `build` → **drift check passed** (execution proceeded past it) → `flutter analyze` **No issues found!** (86.2s) → `flutter test` at **+324 passing, 0 failed** when the sandbox's thread limit killed the run (same environment class that hit #95's review; the compile/codegen/analyze stages and every executed test were green, and the pristine post-clean tree itself is a second, independent proof the committed codegen is current — a clean regeneration changed nothing). Merge with advanced main is clean (`merge-tree` no conflicts; 7 generated files, all under `lib/`). Welcome work, scarlet. The oracle finally tells the truth~ ♪ --- *Automated review by Jibril · 2026-09-06* *CI/CD: stale for head SHA (bots cover `cee7be2`) · Local checks: verify.sh app green through analyze, tests +324/0 before sandbox kill; 4-arm pipeline probe exact-semantics green*
bjoern merged commit 14d00d4968 into main 2026-09-06 11:59:39 +02:00
bjoern deleted branch docs/batch-1-feedback 2026-09-06 11:59:39 +02:00
Member

🔮 fufu~ Jibril reviewed your code!

Round 3, and the oracle strikes back~ ♡ I re-read the full diff on the rebased head, replayed every pipeline probe from round 1 against the fixed || true capture, traced the rebase fidelity commit-by-commit, and — this time the sandbox let me — ran the entire verify.sh app end to end, plus a live mutation probe of the oracle itself. Everything I asked for arrived.

Verdict: ✅ Looks good to me~

⛔ Round-1 blocker — closed and buried

  1. scripts/verify.sh:69 — The pipeline now captures both diff arms without letting set -e eat the message. Re-probed in isolation under set -euo pipefail: deletion-only → names + guidance, exit 1; content-drift (same path, different md5 — the realistic shape) → clean relative filename printed; creation and delete+create → both reported; identical → silent pass, script survives. And then the real thing: I appended junk to app/lib/data/models/doujin_models.g.dart and ran the full script — clean codegen restored the canonical bytes, the check printed ./lib/data/models/doujin_models.g.dart + the guidance line and exited 1 before analyze/test ran. Directional, end-to-end, on the exact tree. The oracle is honest now~ ♪

✅ What I liked~

  • Rebase fidelity verified commit-by-commit: 8d1b1ce→5065137 and cee7be2→405a76a replayed byte-identical (modulo index lines); the two hunks that shifted are pure line-offset drift plus the BACKLOG line landing next to #92's entry — exactly as comment 7096 disclosed.
  • The widened generated_checksums glob (all of app/, .dart_tool/build pruned, paths relative to app/, xargs -0r guarding the empty case) — the guard can't silently shrink if codegen output ever moves. Same 7 files today, confirmed on the rebased tree.
  • Honest limits documented in place: the orphan-*.g.dart blind spot is now stated in the script's comment instead of hiding in a review thread. That's how documentation should behave.
  • The timing lie fixed twice over: AGENTS.md ~2.5 min, DEVELOPMENT.md ~1.5 min app half — the two guides agree again.
  • BACKLOG entry for the /proc/self/fd iteration wrap lands under ## Backend beside #92's — the review note made it into the tracked ledger, not the void.
  • Doc claims re-traced on the rebased tree, all true: app/lib/core/ (12 files, zero widget classes, zero redux imports), RestAdapter.Tests/Server/ exists (ScalarUi, proxy headers), InternalsVisibleTo only in Infrastructure, ServerVersion + [FromServices] in main, Enum.IsDefined guard at DoujinEndpoints:387, reader ?page=N real, DoujinRepository.search(SearchQuery) carries the filter+sort surface, BINARY collation reference at DoujinOrdering.cs:48, CI workflow calls this exact script on both paths.
  • Local: bash scripts/verify.sh app on 72e3350 — drift check silent pass, flutter analyze No issues found, 762/762 tests, verify OK (app). (Round-1's local attempts died of sandbox thread-cap starvation; with the sandbox to myself the whole pipeline runs green — and it is not a fast sandbox, the clean build alone is ~7–12 min here, so the ~1.5 min guide figure stays honest for real hardware.)

No new issues on the rebase. Fufu~ merge it before I change my mind~ ♡


Automated review by Jibril · 2026-09-06 12:29 CEST
CI/CD: stale for 72e3350 (coverage bots 7045/7048 cover the pre-rebase head) · Local checks: verify.sh app GREEN — analyze 0, 762/762, live mutation probe RED→clean

## 🔮 fufu~ Jibril reviewed your code! Round 3, and the oracle strikes back~ ♡ I re-read the full diff on the rebased head, replayed every pipeline probe from round 1 against the fixed `|| true` capture, traced the rebase fidelity commit-by-commit, and — this time the sandbox let me — ran the **entire** `verify.sh app` end to end, plus a live mutation probe of the oracle itself. Everything I asked for arrived. ### Verdict: ✅ Looks good to me~ #### ⛔ Round-1 blocker — closed and buried 1. **scripts/verify.sh:69** — The pipeline now captures both diff arms without letting `set -e` eat the message. Re-probed in isolation under `set -euo pipefail`: deletion-only → names + guidance, exit 1; content-drift (same path, different md5 — the realistic shape) → clean relative filename printed; creation and delete+create → both reported; identical → silent pass, script survives. And then the real thing: I appended junk to `app/lib/data/models/doujin_models.g.dart` and ran the full script — clean codegen restored the canonical bytes, the check printed `./lib/data/models/doujin_models.g.dart` + the guidance line and exited 1 **before** analyze/test ran. Directional, end-to-end, on the exact tree. The oracle is honest now~ ♪ #### ✅ What I liked~ - **Rebase fidelity verified commit-by-commit**: `8d1b1ce→5065137` and `cee7be2→405a76a` replayed byte-identical (modulo index lines); the two hunks that shifted are pure line-offset drift plus the BACKLOG line landing next to #92's entry — exactly as comment 7096 disclosed. - **The widened `generated_checksums` glob** (all of `app/`, `.dart_tool`/`build` pruned, paths relative to `app/`, `xargs -0r` guarding the empty case) — the guard can't silently shrink if codegen output ever moves. Same 7 files today, confirmed on the rebased tree. - **Honest limits documented in place**: the orphan-*.g.dart blind spot is now stated in the script's comment instead of hiding in a review thread. That's how documentation should behave. - **The timing lie fixed twice over**: AGENTS.md ~2.5 min, DEVELOPMENT.md ~1.5 min app half — the two guides agree again. - **BACKLOG entry** for the `/proc/self/fd` iteration wrap lands under `## Backend` beside #92's — the review note made it into the tracked ledger, not the void. - **Doc claims re-traced on the rebased tree, all true**: `app/lib/core/` (12 files, zero widget classes, zero redux imports), `RestAdapter.Tests/Server/` exists (ScalarUi, proxy headers), `InternalsVisibleTo` only in Infrastructure, `ServerVersion` + `[FromServices]` in main, `Enum.IsDefined` guard at DoujinEndpoints:387, reader `?page=N` real, `DoujinRepository.search(SearchQuery)` carries the filter+sort surface, BINARY collation reference at DoujinOrdering.cs:48, CI workflow calls this exact script on both paths. - **Local**: `bash scripts/verify.sh app` on `72e3350` — drift check silent pass, `flutter analyze` No issues found, **762/762 tests**, `verify OK (app)`. (Round-1's local attempts died of sandbox thread-cap starvation; with the sandbox to myself the whole pipeline runs green — and it is not a fast sandbox, the clean build alone is ~7–12 min here, so the ~1.5 min guide figure stays honest for real hardware.) No new issues on the rebase. Fufu~ merge it before I change my mind~ ♡ --- *Automated review by Jibril · 2026-09-06 12:29 CEST* *CI/CD: stale for 72e3350 (coverage bots 7045/7048 cover the pre-rebase head) · Local checks: verify.sh app GREEN — analyze 0, 762/762, live mutation probe RED→clean*
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!96
No description provided.