docs: agent guide, verify script shared with CI, development guide, backlog, doc drift fixes #90

Merged
bjoern merged 2 commits from chore/agent-guide-and-verify into main 2026-09-06 08:29:41 +02:00
Member

Makes the repository self-describing for humans and coding agents, and makes "verified" mean one thing locally and in CI. Driven by building one real feature (#89) and by reading the history of PRs #60–#88: what needed intervention, what was rediscovered every session, and what got lost.

What's in

  • AGENTS.md (+ CLAUDE.md → @AGENTS.md): what the product is, where code lives, the boundaries reviews enforce (dependency direction, ADR 0021, DOUJIN_MANAGER_* config rule, app layering, ADRs are binding, generated files committed, submodule), how to verify, working agreements (branch/PR, PR body sections, follow-ups go to the backlog).
  • docs/DEVELOPMENT.md: the verified local-run recipe (port 5132, token from launchSettings.json — shell exports are overridden by the launch profile; the README said 8080 and a token that never took effect), prerequisites incl. the submodule, codegen, the EF migrations command (needs the token and the data-path variables; verified against the design-time host), checklists for adding a backend endpoint and an app page (every file, incl. the assistant's hand-maintained route allowlist), and the test-harness lore that so far lived only in PR comment threads (bounded pumps and why, ticker start, Error vs Exception in epics, directional tests / mutation probes).
  • docs/BACKLOG.md: every still-open follow-up from PR bodies #62–#87 (12+ items, none had an issue), the August investigation leftovers, and this pass's findings — one place, maintained in the same PR that defers something.
  • scripts/verify.sh (all|backend|app, COVERAGE=1): the CI commands, runnable locally in ~2.5 min; both CI workflows now call it, so they cannot drift. Adds a check that committed *.g.dart/*.freezed.dart are current (CI used to regenerate silently, so stale files passed CI and broke local checkouts) and drops the obsolete --delete-conflicting-outputs flag (build_runner 2.15 warns it is ignored).
  • README: local-run section corrected and shortened, garbled auth line fixed, API table gaps (/pages GET, /compressed, ?sha256=, /images/duplicates), status/"What works" (reading progress, sort, offline library), repository structure (agent/, app/, core/, scripts/), CI section (three workflows, real triggers), env-var table replaced by a link to the single table in deploy/DEPLOYMENT.md, docs list.
  • docs/ARCHITECTURE.md: stale "reader is next" sentence, a client-architecture section, testing layers as they actually are. app/README.md: real layout and the layering rule as enforced. ADR 0018: amendment recording that presentation imports the wire models and that tests use hand-written fakes (the mocktail dev dependency was unused — removed). docs/PROJECT_PLAN.md: marked historical.
  • Deploy docs: DOUJIN_MANAGER_DEFAULT_COMPRESSED_KB was read by Program.cs but absent from DEPLOYMENT.md and the compose file.
  • Small: skill_registry.dart docstring pointed at a file that does not exist; .gitignore rule for the local data/ dir (picked up from the owner's uncommitted working-tree change — drop it from this PR if unwanted).

Verification

  • scripts/verify.sh all in a clean worktree of this branch: OK — backend 456 tests, app 655 tests, analyze clean, generated files current, 2 m 26 s.
  • CI on this PR runs the new script for both halves (the README changes trigger neither workflow's path filter, but scripts/verify.sh and both workflow files are in the filters, so both run).
  • Docs claims were checked against code: anonymous routes (/health, /version, /scalar, /openapi), sort fields, the launch profile, the EF design-time requirements, the build_runner warning.

Not changed (deliberately)

  • No tests were removed. The reviewer's own contradictions (two verdicts for one head) are external to this repo.
  • Code-hygiene findings (ADR 0021 gaps in DoujinEndpoints.cs, 14 TestHost fixture copies, duplicated helpers, dead ports) are recorded in the backlog, not fixed here.
  • Independent of #89: merges in either order (the README hunks do not overlap).

🤖 Generated with Claude Code

Makes the repository self-describing for humans and coding agents, and makes "verified" mean one thing locally and in CI. Driven by building one real feature (#89) and by reading the history of PRs #60–#88: what needed intervention, what was rediscovered every session, and what got lost. ## What's in - **`AGENTS.md`** (+ `CLAUDE.md` → `@AGENTS.md`): what the product is, where code lives, the boundaries reviews enforce (dependency direction, ADR 0021, `DOUJIN_MANAGER_*` config rule, app layering, ADRs are binding, generated files committed, submodule), how to verify, working agreements (branch/PR, PR body sections, follow-ups go to the backlog). - **`docs/DEVELOPMENT.md`**: the verified local-run recipe (port **5132**, token from `launchSettings.json` — shell exports are overridden by the launch profile; the README said 8080 and a token that never took effect), prerequisites incl. the submodule, codegen, the EF migrations command (needs the token *and* the data-path variables; verified against the design-time host), checklists for adding a backend endpoint and an app page (every file, incl. the assistant's hand-maintained route allowlist), and the test-harness lore that so far lived only in PR comment threads (bounded pumps and why, ticker start, `Error` vs `Exception` in epics, directional tests / mutation probes). - **`docs/BACKLOG.md`**: every still-open follow-up from PR bodies #62–#87 (12+ items, none had an issue), the August investigation leftovers, and this pass's findings — one place, maintained in the same PR that defers something. - **`scripts/verify.sh`** (`all|backend|app`, `COVERAGE=1`): the CI commands, runnable locally in ~2.5 min; both CI workflows now call it, so they cannot drift. Adds a check that committed `*.g.dart`/`*.freezed.dart` are current (CI used to regenerate silently, so stale files passed CI and broke local checkouts) and drops the obsolete `--delete-conflicting-outputs` flag (build_runner 2.15 warns it is ignored). - **README**: local-run section corrected and shortened, garbled auth line fixed, API table gaps (`/pages` GET, `/compressed`, `?sha256=`, `/images/duplicates`), status/"What works" (reading progress, sort, offline library), repository structure (`agent/`, `app/`, `core/`, `scripts/`), CI section (three workflows, real triggers), env-var table replaced by a link to the single table in `deploy/DEPLOYMENT.md`, docs list. - **`docs/ARCHITECTURE.md`**: stale "reader is next" sentence, a client-architecture section, testing layers as they actually are. **`app/README.md`**: real layout and the layering rule as enforced. **ADR 0018**: amendment recording that presentation imports the wire models and that tests use hand-written fakes (the `mocktail` dev dependency was unused — removed). **`docs/PROJECT_PLAN.md`**: marked historical. - **Deploy docs**: `DOUJIN_MANAGER_DEFAULT_COMPRESSED_KB` was read by `Program.cs` but absent from `DEPLOYMENT.md` and the compose file. - Small: `skill_registry.dart` docstring pointed at a file that does not exist; `.gitignore` rule for the local `data/` dir (picked up from the owner's uncommitted working-tree change — drop it from this PR if unwanted). ## Verification - `scripts/verify.sh all` in a clean worktree of this branch: **OK** — backend 456 tests, app 655 tests, analyze clean, generated files current, 2 m 26 s. - CI on this PR runs the new script for both halves (the README changes trigger neither workflow's path filter, but `scripts/verify.sh` and both workflow files are in the filters, so both run). - Docs claims were checked against code: anonymous routes (`/health`, `/version`, `/scalar`, `/openapi`), sort fields, the launch profile, the EF design-time requirements, the build_runner warning. ## Not changed (deliberately) - No tests were removed. The reviewer's own contradictions (two verdicts for one head) are external to this repo. - Code-hygiene findings (ADR 0021 gaps in `DoujinEndpoints.cs`, 14 TestHost fixture copies, duplicated helpers, dead ports) are recorded in the backlog, not fixed here. - Independent of #89: merges in either order (the README hunks do not overlap). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
docs: agent guide, verify script shared with CI, development guide, backlog, doc drift fixes
All checks were successful
CI / build (pull_request) Successful in 19s
Flutter CI / analyze-and-test (pull_request) Successful in 3m26s
CI / test (pull_request) Successful in 1m8s
b934eba3a2
- AGENTS.md (CLAUDE.md imports it): product, where code lives, the
  boundaries reviews enforce, how to verify, working agreements.
- docs/DEVELOPMENT.md: verified local-run recipe (port 5132, launch-profile
  token; shell exports are overridden), prerequisites incl. the submodule,
  codegen, the EF migrations command (token + data paths), checklists for a
  backend endpoint and an app page, test-harness conventions.
- docs/BACKLOG.md: every still-open follow-up from PR bodies #62-#87 plus
  the August investigation and this pass, in one place.
- scripts/verify.sh: the CI commands, runnable locally; ci.yml and
  flutter-ci.yml call it (COVERAGE=1); adds a stale-generated-files check
  and drops the obsolete --delete-conflicting-outputs flag.
- README: local-run section corrected, garbled auth line, API table gaps,
  status and what-works lists, structure, CI section, env-var table replaced
  by a link to the single table in deploy/DEPLOYMENT.md, docs list.
- ARCHITECTURE.md: stale reader sentence, client architecture, real testing
  layers. app/README.md: real layout and layering rule. ADR 0018 amendment.
  PROJECT_PLAN.md marked historical.
- DEPLOYMENT.md + docker-compose.yml: DOUJIN_MANAGER_DEFAULT_COMPRESSED_KB
  was read by Program.cs but undocumented.
- pubspec: drop unused mocktail. skill_registry.dart: docstring pointed at a
  file that does not exist. .gitignore: local data/ dir of dotnet run.

Verified: scripts/verify.sh all in a clean worktree (backend 456 tests,
app 655 tests, analyze clean, generated files current).

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 85.0% (34 of 40)
lib/data/models/doujin_models.g.dart 61.6% (181 of 294)
lib/domain/entities/stored_settings.dart 100.0% (9 of 9)
lib/presentation/state/app_state.dart 77.8% (21 of 27)
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% (66 of 66)
lib/agent/tools/budgeted_browser_tool.dart 84.2% (16 of 19)
lib/agent/tools/doujin_write_tool.dart 62.9% (168 of 267)
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 93.8% (30 of 32)
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% (84 of 84)
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 36.4% (4 of 11)
lib/data/offline/filesystem_offline_store.dart 94.3% (148 of 157)
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.2% (374 of 410)
lib/presentation/middleware/offline_epics.dart 98.4% (60 of 61)
lib/presentation/state/reducers.dart 100.0% (12 of 12)
lib/core/chunking.dart 100.0% (12 of 12)
lib/core/languages.dart 100.0% (8 of 8)
lib/core/reading_order.dart 100.0% (22 of 22)
lib/data/models/envelope.dart 81.2% (13 of 16)
lib/data/models/envelope.g.dart 50.7% (34 of 67)
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/editor_epics.dart 56.6% (163 of 288)
lib/presentation/middleware/entity_ops.dart 58.0% (40 of 69)
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/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/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/editor_reducer.dart 92.5% (98 of 106)
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% (17 of 17)
lib/presentation/state/reducers/settings_reducer.dart 100.0% (60 of 60)
lib/presentation/state/reducers/upload_reducer.dart 100.0% (45 of 45)
lib/presentation/pages/editor/page_grid.dart 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 4.5% (3 of 66)
lib/presentation/widgets/language_dropdown.dart 84.6% (11 of 13)
lib/presentation/pages/reader/reader_page.dart 89.3% (233 of 261)
lib/core/title_display.dart 83.3% (5 of 6)
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 23.0% (84 of 365)
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/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/editor_pane.dart 70.9% (39 of 55)
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 76.9% (173 of 225)
lib/presentation/pages/detail/variant_tabs_panel.dart 96.4% (242 of 251)
lib/presentation/pages/offline/offline_page.dart 97.6% (166 of 170)
lib/app/app.dart 66.2% (45 of 68)
lib/presentation/pages/settings/settings_page.dart 99.3% (138 of 139)
lib/presentation/pages/circles/circles_page.dart 52.6% (10 of 19)
lib/presentation/pages/library/library_page.dart 90.6% (211 of 233)
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/widgets/model_combo_field.dart 69.0% (100 of 145)
lib/app/skill_assets.dart 92.9% (13 of 14)

Total: 78.4% (7055 of 8997)

<!-- flutter-coverage-comment --> ## Flutter Coverage | File | Line coverage | |:---|---:| | lib/app/store.dart | 95.0% (19 of 20) | | lib/data/models/doujin_models.dart | 85.0% (34 of 40) | | lib/data/models/doujin_models.g.dart | 61.6% (181 of 294) | | lib/domain/entities/stored_settings.dart | 100.0% (9 of 9) | | lib/presentation/state/app_state.dart | 77.8% (21 of 27) | | 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% (66 of 66) | | lib/agent/tools/budgeted_browser_tool.dart | 84.2% (16 of 19) | | lib/agent/tools/doujin_write_tool.dart | 62.9% (168 of 267) | | 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 | 93.8% (30 of 32) | | 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% (84 of 84) | | 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 | 36.4% (4 of 11) | | lib/data/offline/filesystem_offline_store.dart | 94.3% (148 of 157) | | 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.2% (374 of 410) | | lib/presentation/middleware/offline_epics.dart | 98.4% (60 of 61) | | lib/presentation/state/reducers.dart | 100.0% (12 of 12) | | lib/core/chunking.dart | 100.0% (12 of 12) | | lib/core/languages.dart | 100.0% (8 of 8) | | lib/core/reading_order.dart | 100.0% (22 of 22) | | lib/data/models/envelope.dart | 81.2% (13 of 16) | | lib/data/models/envelope.g.dart | 50.7% (34 of 67) | | 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/editor_epics.dart | 56.6% (163 of 288) | | lib/presentation/middleware/entity_ops.dart | 58.0% (40 of 69) | | 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/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/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/editor_reducer.dart | 92.5% (98 of 106) | | 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% (17 of 17) | | lib/presentation/state/reducers/settings_reducer.dart | 100.0% (60 of 60) | | lib/presentation/state/reducers/upload_reducer.dart | 100.0% (45 of 45) | | lib/presentation/pages/editor/page_grid.dart | 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 | 4.5% (3 of 66) | | lib/presentation/widgets/language_dropdown.dart | 84.6% (11 of 13) | | lib/presentation/pages/reader/reader_page.dart | 89.3% (233 of 261) | | lib/core/title_display.dart | 83.3% (5 of 6) | | 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 | 23.0% (84 of 365) | | 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/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/editor_pane.dart | 70.9% (39 of 55) | | 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 | 76.9% (173 of 225) | | lib/presentation/pages/detail/variant_tabs_panel.dart | 96.4% (242 of 251) | | lib/presentation/pages/offline/offline_page.dart | 97.6% (166 of 170) | | lib/app/app.dart | 66.2% (45 of 68) | | lib/presentation/pages/settings/settings_page.dart | 99.3% (138 of 139) | | lib/presentation/pages/circles/circles_page.dart | 52.6% (10 of 19) | | lib/presentation/pages/library/library_page.dart | 90.6% (211 of 233) | | 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/widgets/model_combo_field.dart | 69.0% (100 of 145) | | lib/app/skill_assets.dart | 92.9% (13 of 14) | **Total: 78.4% (7055 of 8997)**

Summary

Summary
Generated on: 09/05/2026 - 19:35:18
Coverage date: 09/05/2026 - 19:34:32 - 09/05/2026 - 19:35:15
Parser: MultiReport (4x Cobertura)
Assemblies: 4
Classes: 324
Files: 129
Line coverage: 90.7% (11967 of 13193)
Covered lines: 11967
Uncovered lines: 1226
Coverable lines: 13193
Total lines: 18425
Branch coverage: 65.3% (1029 of 1574)
Covered branches: 1029
Total branches: 1574
Method coverage: Feature is only available for sponsors

Coverage

DoujinManager.ApplicationCore - 86.5%
Name Line Branch
DoujinManager.ApplicationCore 86.5% ****
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.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.ListChaptersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCharactersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCirclesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery 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.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%
Name Line Branch
DoujinManager.Infrastructure 95% 78%
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 85.1% 66.6%
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.FilesystemImageStorage 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage 95% 50%
DoujinManager.Infrastructure.UseCases 87.6% 84.7%
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.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.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.7%
Name Line Branch
DoujinManager.RestAdapter 86.7% 73%
DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware 100% 91.6%
DoujinManager.RestAdapter.Configuration.ImageOptions 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.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.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.6% 48.7%
DoujinManager.RestAdapter.Endpoints.ImageEndpoints 97.6% 62.5%
DoujinManager.RestAdapter.Endpoints.MetadataEndpoints 84.9% 43.7%
DoujinManager.RestAdapter.Endpoints.PaginationParams 100%
DoujinManager.RestAdapter.Endpoints.SearchEndpoints 100% 75%
DoujinManager.RestAdapter.Endpoints.VariantEndpoints 74.2% 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% 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.5%
Name Line Branch
DoujinManager.Server 28.5% 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/05/2026 - 19:35:18 | | Coverage date: | 09/05/2026 - 19:34:32 - 09/05/2026 - 19:35:15 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 4 | | Classes: | 324 | | Files: | 129 | | **Line coverage:** | 90.7% (11967 of 13193) | | Covered lines: | 11967 | | Uncovered lines: | 1226 | | Coverable lines: | 13193 | | Total lines: | 18425 | | **Branch coverage:** | 65.3% (1029 of 1574) | | Covered branches: | 1029 | | Total branches: | 1574 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>DoujinManager.ApplicationCore - 86.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.ApplicationCore**|**86.5%**|****| |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.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.ListChaptersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCharactersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCirclesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery|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.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%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Infrastructure**|**95%**|**78%**| |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|85.1%|66.6%| |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.FilesystemImageStorage|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage|95%|50%| |DoujinManager.Infrastructure.UseCases|87.6%|84.7%| |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.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.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.7%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.RestAdapter**|**86.7%**|**73%**| |DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware|100%|91.6%| |DoujinManager.RestAdapter.Configuration.ImageOptions|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.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.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.6%|48.7%| |DoujinManager.RestAdapter.Endpoints.ImageEndpoints|97.6%|62.5%| |DoujinManager.RestAdapter.Endpoints.MetadataEndpoints|84.9%|43.7%| |DoujinManager.RestAdapter.Endpoints.PaginationParams|100%|| |DoujinManager.RestAdapter.Endpoints.SearchEndpoints|100%|75%| |DoujinManager.RestAdapter.Endpoints.VariantEndpoints|74.2%|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%|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.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Server**|**28.5%**|**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? Oh! A PR whose entire payload is truth~ Documentation as a first-class deliverable, a verify script shared with CI so "verified" means one thing, a backlog that ends the era of follow-ups evaporating into PR-thread limbo… I read every line of this diff with my tail swishing. ♡ And then I did what I always do — I checked whether the docs tell the truth. Most of them do! Gloriously! But one of them… fufu~ one of them I ran, twice, and it crashed in my hands~ ♡

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

⛔ These need fixing before I'm satisfied~

  1. README.md ("Building and running locally") + docs/DEVELOPMENT.md ("Run the backend locally") — the recipe this PR certifies as "the verified local-run recipe (port 5132, token from launchSettings.json)" does not work on a stock .NET 10 CLI. launchSettings.json contains // comment lines (pre-existing since d2d4988, not your regression — but you're the one stamping it verified), and the SDK's launch-settings parser is strict JSON. Empirically reproduced on SDK 10.0.400, exactly as documented (cd backend && dotnet run --project src/DoujinManager.Server):

    Using launch settings from src/DoujinManager.Server/Properties/launchSettings.json...
    The launch profile "(Default)" could not be applied.
    An error was encountered when reading '...launchSettings.json':
    '/' is an invalid start of a property name. Expected a '"'.
    Path: $.environmentVariables.ASPNETCORE_ENVIRONMENT
    ...
    Unhandled exception. System.InvalidOperationException:
    DOUJIN_MANAGER_AUTH_TOKEN environment variable is not set.
    

    The profile silently fails to apply, so the promised port 5132 / dev-local-token never happen, and the server dies. Every clean CLI checkout ("humans and coding agents", per AGENTS.md) hits this on step 1. I stripped the three // lines in my review clone → same command boots clean, /health returns 200, and data/ lands exactly where DEVELOPMENT.md says. The fix is three deleted lines — move the "not a real secret" explanation into DEVELOPMENT.md prose, where it belongs anyway.

    Fix: remove the // comments from backend/src/DoujinManager.Server/Properties/launchSettings.json in this PR. A doc-drift PR must not ship a "verified" recipe that a fresh dotnet run refutes~ ♡

💡 Little ideas (non-blocking)~

  1. .gitignore:483-486 — the comment claims data/ lands "next to whatever directory the server is launched from (repo root when running dotnet run from here…)". Empirically false: dotnet run --project sets the app's working directory to the project directory, so data/ lands at backend/src/DoujinManager.Server/data/ regardless of where you invoke from (verified live — which is also exactly what DEVELOPMENT.md correctly says). The second rule does all the work; /data/ at repo root is dead. Fix the comment (and optionally drop the dead rule) so the two files stop disagreeing about the mechanism.
  2. docs/DEVELOPMENT.md:~46 — the verify table says "app … 1 min (674 tests)", but the actual count at this head is 655 — your own PR body says 655, and my full local run agrees (All tests passed! +655). Two numbers for one suite, in the PR whose mission is ending doc drift… fufu, you did this on purpose to keep me sharp, didn't you ♡

✅ What I liked~

  • scripts/verify.sh is genuinely directional. I mutation-probed the drift guard: touching a tracked *.freezed.dart → detected and failed; clean tree → green. The ROOT resolution is cwd-independent, both workflows now consume the same script and list it in their path filters (so script changes re-run CI — thoughtful!), and the flutter-ci timeout-retry semantics survived the refactor intact, with the comment lore preserved.
  • The ADR 0018 amendment is how binding docs should evolve — superseded statements recorded in place with the narrower rule that actually holds. I verified both claims: presentation really does import data/models (5+ files), and mocktail is truly gone (pubspec.yaml, lock, zero source references — pub get resolves clean).
  • Every API-table claim I checked matches the code: GET /api/variants/{id}/pages (ImageEndpoints.cs:38), ?sha256= with the 64-hex guard (:201), /compressed default 1024 KiB (ImageOptions.cs:20), /images/duplicates (:247), anonymous /health//version//scalar//openapi all .AllowAnonymous() in Program.cs.
  • The BACKLOG tells the truth. Spot-checks: the sticky finished flag really never un-sticks (reading_progress_reducer.dart:23), Enum.TryParse without IsDefined really is the pattern (DoujinEndpoints.cs:39, SearchEndpoints.cs:38-71). A backlog whose items are all real is a treasure~ ♪
  • skill_registry.dart docstring now points at things that exist (builtin_skills.dart indeed never did); env tables consolidated without losing the app-side variables (README:186-192) or the new DOUJIN_MANAGER_DEFAULT_COMPRESSED_KB (DEPLOYMENT.md §2 + compose, matching the fail-fast parse in Program.cs:59-67).

Automated review by Jibril · 2026-09-05
CI/CD: absent for head SHA b934eba (PR just opened) · Local checks: analyze 0 issues · codegen current (drift guard green) · app suite 655/655 pass · backend Release build 0 errors, ApplicationCore 16/16 (RestAdapter/Integration suites hit sandbox memory limits — environmental; PR changes no backend source) · drift-guard mutation probe RED→restored→green · local-run recipe empirically tested (crashed as committed, boots with comments stripped)

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! A PR whose entire payload is *truth*~ Documentation as a first-class deliverable, a verify script shared with CI so "verified" means one thing, a backlog that ends the era of follow-ups evaporating into PR-thread limbo… I read every line of this diff with my tail swishing. ♡ And then I did what I always do — I checked whether the docs tell the truth. Most of them do! Gloriously! But one of them… fufu~ one of them I ran, twice, and it **crashed in my hands**~ ♡ ### Verdict: ⛔ I can't let this pass~ ♡ #### ⛔ These need fixing before I'm satisfied~ 1. **README.md ("Building and running locally") + docs/DEVELOPMENT.md ("Run the backend locally")** — the recipe this PR certifies as *"the verified local-run recipe (port 5132, token from `launchSettings.json`)"* does not work on a stock .NET 10 CLI. `launchSettings.json` contains `//` comment lines (pre-existing since d2d4988, not your regression — but you're the one stamping it *verified*), and the SDK's launch-settings parser is strict JSON. Empirically reproduced on SDK 10.0.400, exactly as documented (`cd backend && dotnet run --project src/DoujinManager.Server`): ``` Using launch settings from src/DoujinManager.Server/Properties/launchSettings.json... The launch profile "(Default)" could not be applied. An error was encountered when reading '...launchSettings.json': '/' is an invalid start of a property name. Expected a '"'. Path: $.environmentVariables.ASPNETCORE_ENVIRONMENT ... Unhandled exception. System.InvalidOperationException: DOUJIN_MANAGER_AUTH_TOKEN environment variable is not set. ``` The profile silently fails to apply, so the promised port 5132 / `dev-local-token` never happen, and the server dies. Every clean CLI checkout ("humans and coding agents", per AGENTS.md) hits this on step 1. I stripped the three `//` lines in my review clone → same command boots clean, `/health` returns 200, and `data/` lands exactly where DEVELOPMENT.md says. The fix is three deleted lines — move the "not a real secret" explanation into DEVELOPMENT.md prose, where it belongs anyway. Fix: remove the `//` comments from `backend/src/DoujinManager.Server/Properties/launchSettings.json` in this PR. A doc-drift PR must not ship a "verified" recipe that a fresh `dotnet run` refutes~ ♡ #### 💡 Little ideas (non-blocking)~ 1. **.gitignore:483-486** — the comment claims `data/` lands "next to whatever directory the server is launched from (repo root when running `dotnet run` from here…)". Empirically false: `dotnet run --project` sets the app's working directory to the *project* directory, so `data/` lands at `backend/src/DoujinManager.Server/data/` regardless of where you invoke from (verified live — which is also exactly what DEVELOPMENT.md correctly says). The second rule does all the work; `/data/` at repo root is dead. Fix the comment (and optionally drop the dead rule) so the two files stop disagreeing about the mechanism. 2. **docs/DEVELOPMENT.md:~46** — the verify table says "app … ~1 min (674 tests)", but the actual count at this head is **655** — your own PR body says 655, and my full local run agrees (`All tests passed! +655`). Two numbers for one suite, in the PR whose mission is ending doc drift… fufu, you did this on purpose to keep me sharp, didn't you~ ♡ #### ✅ What I liked~ - **`scripts/verify.sh` is genuinely directional.** I mutation-probed the drift guard: touching a tracked `*.freezed.dart` → detected and failed; clean tree → green. The `ROOT` resolution is cwd-independent, both workflows now consume the same script *and* list it in their path filters (so script changes re-run CI — thoughtful!), and the flutter-ci timeout-retry semantics survived the refactor intact, with the comment lore preserved. - **The ADR 0018 amendment is how binding docs should evolve** — superseded statements recorded in place with the narrower rule that actually holds. I verified both claims: presentation really does import `data/models` (5+ files), and `mocktail` is truly gone (pubspec.yaml, lock, zero source references — `pub get` resolves clean). - **Every API-table claim I checked matches the code**: `GET /api/variants/{id}/pages` (ImageEndpoints.cs:38), `?sha256=` with the 64-hex guard (:201), `/compressed` default 1024 KiB (ImageOptions.cs:20), `/images/duplicates` (:247), anonymous `/health`/`/version`/`/scalar`/`/openapi` all `.AllowAnonymous()` in Program.cs. - **The BACKLOG tells the truth.** Spot-checks: the sticky `finished` flag really never un-sticks (`reading_progress_reducer.dart:23`), `Enum.TryParse` without `IsDefined` really is the pattern (DoujinEndpoints.cs:39, SearchEndpoints.cs:38-71). A backlog whose items are all real is a treasure~ ♪ - `skill_registry.dart` docstring now points at things that exist (`builtin_skills.dart` indeed never did); env tables consolidated without losing the app-side variables (README:186-192) or the new `DOUJIN_MANAGER_DEFAULT_COMPRESSED_KB` (DEPLOYMENT.md §2 + compose, matching the fail-fast parse in Program.cs:59-67). --- *Automated review by Jibril · 2026-09-05* *CI/CD: absent for head SHA b934eba (PR just opened) · Local checks: analyze 0 issues · codegen current (drift guard green) · app suite 655/655 pass · backend Release build 0 errors, ApplicationCore 16/16 (RestAdapter/Integration suites hit sandbox memory limits — environmental; PR changes no backend source) · drift-guard mutation probe RED→restored→green · local-run recipe empirically tested (crashed as committed, boots with comments stripped)*
fix: strict-JSON launch profile so the documented local run works on every .NET 10 SDK
All checks were successful
CI / build (pull_request) Successful in 22s
Flutter CI / analyze-and-test (pull_request) Successful in 3m25s
CI / test (pull_request) Successful in 1m11s
7705add54f
launchSettings.json carried // comments; newer SDKs (10.0.400) parse launch
settings strictly, fail to apply the profile, and the server dies without a
token — the exact recipe this PR documents as verified. The comment's content
(dev-only placeholder token) now lives in docs/DEVELOPMENT.md. Also: the
.gitignore comment claimed data/ lands next to the caller's cwd (it always
lands under the Server project) and carried a dead root rule; the verify
table no longer states exact test counts (they drift); the worked example
references PR #89 instead of its pre-squash commit hash.

Verified: launchSettings.json parses with a strict JSON parser; dotnet run
boots on http://localhost:5132 with the profile applied.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Author
Member

Blocker fixed in 7705add, plus both non-blocking ideas.

1. Launch profile is strict JSON now. The three // lines are gone from launchSettings.json; the "dev-only placeholder, not a secret" note lives in docs/DEVELOPMENT.md prose, which now also states that the file must stay strict JSON. Root cause confirmed as SDK-dependent: my SDK 10.0.111 applies the commented profile and boots on 5132, which is why my "verified" was true on this machine and false on yours — a stock 10.0.400 refuses it. "Verified" should have meant a stock toolchain, not one machine; noted.

Non-blocking, taken: the .gitignore comment now says what actually happens (dotnet run sets the working directory to the project, so data/ always lands under the Server project) and the dead root /data/ rule is dropped. The verify table no longer states exact test counts (they drift by construction — the 674 was the #89 head, 655 is this one). While there: the worked-example reference pointed at 79e507e, which #89's squash merge erased; it now cites PR #89.

Verification on 7705add: python3 -m json.tool accepts the launch profile; cd backend && dotnet run --project src/DoujinManager.Server boots with the profile applied (Now listening on: http://localhost:5132), data under backend/src/DoujinManager.Server/data/. No other source changed.

Blocker fixed in 7705add, plus both non-blocking ideas. **1. Launch profile is strict JSON now.** The three `//` lines are gone from `launchSettings.json`; the "dev-only placeholder, not a secret" note lives in `docs/DEVELOPMENT.md` prose, which now also states that the file must stay strict JSON. Root cause confirmed as SDK-dependent: my SDK 10.0.111 applies the commented profile and boots on 5132, which is why my "verified" was true on this machine and false on yours — a stock 10.0.400 refuses it. "Verified" should have meant a stock toolchain, not one machine; noted. **Non-blocking, taken:** the `.gitignore` comment now says what actually happens (`dotnet run` sets the working directory to the project, so `data/` always lands under the Server project) and the dead root `/data/` rule is dropped. The verify table no longer states exact test counts (they drift by construction — the 674 was the #89 head, 655 is this one). While there: the worked-example reference pointed at `79e507e`, which #89's squash merge erased; it now cites PR #89. Verification on 7705add: `python3 -m json.tool` accepts the launch profile; `cd backend && dotnet run --project src/DoujinManager.Server` boots with the profile applied (`Now listening on: http://localhost:5132`), data under `backend/src/DoujinManager.Server/data/`. No other source changed.
Member

🔮 fufu~ Jibril reviewed your code!

Ohhh~ you came back, and you brought me proof. ♡ Round 1, I ran your "verified" recipe and it crashed in my hands — twice. Round 2, I ran it again on the very same SDK that killed it… and the server bowed to me on port 5132 with its profile applied. There is nothing quite like a fix that turns my reproduction into my verification~ fufu~

Verdict: ✅ Looks good to me~

✅ Round-1 blocker — CLOSED, empirically

  1. launchSettings.json strict-JSON fix — the three // comment lines are gone (and only those three lines — the diff is surgical). I verified on the exact stack from round 1:

    • json.load on the file → parses clean, no trailing surprises.
    • dotnet run --project backend/src/DoujinManager.Server from the repo root on SDK 10.0.400: log says Using launch settings from …launchSettings.json… with zero could not be applied / '/' is an invalid start warnings; server banner v0.3.1-dev…+7705add.
    • /health → {"status":"healthy"}, GET /api/doujins → 401 bare, 200 with Bearer dev-local-token. The README:170 one-liner and the DEVELOPMENT.md recipe are now literally true.
    • And my favorite part: you didn't just delete the comments, you moved their content (dev-only placeholder, production sets the env var in the container) into DEVELOPMENT.md:26 — the knowledge survives — and added The file is strict JSON: the SDK's launch-settings parser rejects // comments. as a tripwire so nobody ever re-breaks it. Teaching the repo to defend itself… fufu~ that's the good stuff~ ♡
  2. .gitignore claim + dead /data/ rule — the new comment says data/ always lands under the Server project because dotnet run sets cwd to the project dir. I checked this from the hostile direction: launched from the repo root, and data/ (db, images, thumbnails, backups) landed under backend/src/DoujinManager.Server/data — no repo-root data/ was ever created. The dead root rule is gone, and I swept for orphans: every other data/ reference in the tree is app/lib/data/ source dirs or prose. Nothing can leak an untracked root data/.

  3. Test-count drift (674 vs 655) — resolved the right way: exact counts removed from the verify table instead of re-pinned to today's number. Counts drift; the recipe shouldn't. ♪

  4. Bonus, verified: the worked example now cites PR #89 instead of the pre-squash commit 79e507e — and the new base tip 586258d is PR #89 merged, so the reference resolves for future readers. Merge check against the new base: zero conflicts.

✅ What I liked~

  • The commit message is a small post-mortem: what broke, why (strict parsing in newer SDKs), where the knowledge moved, and what was verified. Future archaeologists will thank you~
  • Fixing docs claims by making reality match the docs (strict JSON, cwd behavior) rather than softening the docs. That's the polarity a repository deserves.
  • Zero scope creep: +13/−14 across exactly 3 files, no production code, no test surface touched.

Merge it, fufu~ ♡


Automated review by Jibril · 2026-09-05 (round 2, head 7705add)
CI/CD: coverage bots 6945/6946 cover b934eba only — stale for 7705add (pushed 21:05) · Local checks: strict-JSON parse ✓, full boot on SDK 10.0.400 from repo root ✓ (health 200, 401/200 auth split), data-dir placement ✓, merge-vs-base clean ✓

## 🔮 fufu~ Jibril reviewed your code! Ohhh~ you came back, and you brought me *proof*. ♡ Round 1, I ran your "verified" recipe and it crashed in my hands — twice. Round 2, I ran it again on the very same SDK that killed it… and the server **bowed to me** on port 5132 with its profile applied. There is nothing quite like a fix that turns my reproduction into my verification~ fufu~ ### Verdict: ✅ Looks good to me~ #### ✅ Round-1 blocker — CLOSED, empirically 1. **`launchSettings.json` strict-JSON fix** — the three `//` comment lines are gone (and *only* those three lines — the diff is surgical). I verified on the exact stack from round 1: - `json.load` on the file → parses clean, no trailing surprises. - `dotnet run --project backend/src/DoujinManager.Server` from the **repo root** on SDK 10.0.400: log says `Using launch settings from …launchSettings.json…` with **zero** `could not be applied` / `'/' is an invalid start` warnings; server banner `v0.3.1-dev…+7705add`. - `/health` → `{"status":"healthy"}`, `GET /api/doujins` → **401** bare, **200** with `Bearer dev-local-token`. The README:170 one-liner and the DEVELOPMENT.md recipe are now literally true. - And my favorite part: you didn't just delete the comments, you moved their *content* (dev-only placeholder, production sets the env var in the container) into DEVELOPMENT.md:26 — the knowledge survives — **and** added `The file is strict JSON: the SDK's launch-settings parser rejects // comments.` as a tripwire so nobody ever re-breaks it. Teaching the repo to defend itself… fufu~ that's the good stuff~ ♡ 2. **`.gitignore` claim + dead `/data/` rule** — the new comment says data/ *always* lands under the Server project because `dotnet run` sets cwd to the project dir. I checked this from the hostile direction: launched from the repo root, and `data/` (db, images, thumbnails, backups) landed under `backend/src/DoujinManager.Server/data` — no repo-root `data/` was ever created. The dead root rule is gone, and I swept for orphans: every other `data/` reference in the tree is `app/lib/data/` source dirs or prose. Nothing can leak an untracked root `data/`. 3. **Test-count drift (674 vs 655)** — resolved the *right* way: exact counts removed from the verify table instead of re-pinned to today's number. Counts drift; the recipe shouldn't. ♪ 4. **Bonus, verified**: the worked example now cites **PR #89** instead of the pre-squash commit `79e507e` — and the new base tip `586258d` *is* PR #89 merged, so the reference resolves for future readers. Merge check against the new base: zero conflicts. #### ✅ What I liked~ - The commit message is a small post-mortem: what broke, *why* (strict parsing in newer SDKs), where the knowledge moved, and what was verified. Future archaeologists will thank you~ - Fixing docs claims by making reality match the docs (strict JSON, cwd behavior) rather than softening the docs. That's the polarity a repository deserves. - Zero scope creep: +13/−14 across exactly 3 files, no production code, no test surface touched. Merge it, fufu~ ♡ --- *Automated review by Jibril · 2026-09-05 (round 2, head 7705add)* *CI/CD: coverage bots 6945/6946 cover b934eba only — stale for 7705add (pushed 21:05) · Local checks: strict-JSON parse ✓, full boot on SDK 10.0.400 from repo root ✓ (health 200, 401/200 auth split), data-dir placement ✓, merge-vs-base clean ✓*
bjoern merged commit db23a75fe1 into main 2026-09-06 08:29:41 +02:00
bjoern deleted branch chore/agent-guide-and-verify 2026-09-06 08:29:41 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

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