docs: twelve open items for the backlog #107

Merged
bjoern merged 4 commits from docs/pile-a-backlog into main 2026-09-10 08:25:45 +02:00
Member

What's in

Twelve open items, collected in docs/BACKLOG.md rather than written into each of the seven "Pile A" correctness fixes (#108–#114) — otherwise all seven conflict on this one file. This should merge first, so nothing is untracked while they land.

(#N) follows the repo's existing convention: the PR that deferred the item, not a record of what that PR did.

Backend

  • is_default has no filtered unique index, so ADR 0004's exactly-one-default invariant is enforced only in VariantService; two concurrent deletes can leave two defaults (#111)
  • Doujins already left with no default variant stay that way until one of their variants is next written — wants a one-shot repair (#111)
  • Variants whose page_count disagrees with their rows are not repaired; wants a startup or admin-endpoint sweep — deliberately not a background service (#113)
  • page_count can still be lost-updated across two DbContexts (#113)
  • Metadata renames sit outside ADR 0004's UpdatedAt subtree rule (#114)
  • A /health 503 writes nothing to the log (#112)
  • Two sources for the backup directory path (#112)
  • BackupEndpoints advertises self = /api/backups/{fileName}, which answers 405 — appended to the existing entry (#110)

App

  • Page's doc comment names a PageDto that no longer exists; needs a build_runner run in the same commit (#109)
  • Three file writers still rewrite in place, so one interrupted write empties them (#108)
  • The Settings connection test reports a /health 503 unhelpfully (#112)

Assistant

  • The post-write refresh reloads all five metadata collections when a single entity can now be refetched (#110)

It also corrects an existing App entry: "Metadata writes trigger a full five-endpoint refetch" is not true of plain UI metadata CRUD, which patches Redux state from the write response. Only the assistant's _refreshAfterWriteEpic refetches, so that half moved to the Assistant section and the App entry is now just "No app-side disk image cache."

Verification

Documentation only — one file, no code, no tests, no behaviour change.

The seven fix branches were each verified with the full scripts/verify.sh (backend build and tests, app codegen, analyze and tests) before these entries were split out; removing a markdown file from those commits cannot affect that result. Merging this plus the seven in the order below was tested locally: seven of eight apply cleanly, and #114 conflicts by design.

Follow-ups

Recommended merge order. This PR first, then in any order:

# PR Why here
1 #108 — reading progress survives an interrupted write Data loss, 3 files, app-only
2 #109 — variant detail's pages carry media type and byte size Backend-only, wire-additive
3 #110 — five metadata self links point at a readable resource Backend-only, purely additive routes
4 #112 — /health tells a mounted volume from a writable one Largest doc/ADR footprint; read its "Worth deciding before merge"
5 #111 — a doujin always has exactly one default variant Amends ADR 0004
6 #113 — derive PageCount from page rows Amends ADR 0004

and #114 last — it rewrites PageService.cs and VariantService.cs, which #111 and #113 also touch, and amends ADR 0004 alongside both. Merging it last means one rebase instead of two.

Two of the fixes stop future drift without repairing existing damage — hence the two repair items above. Worth querying the live database before deciding how urgent they are: doujins with zero default variants, and variants whose page_count disagrees with their row count.

🤖 Generated with Claude Code

## What's in Twelve open items, collected in `docs/BACKLOG.md` rather than written into each of the seven "Pile A" correctness fixes (#108–#114) — otherwise all seven conflict on this one file. **This should merge first**, so nothing is untracked while they land. `(#N)` follows the repo's existing convention: the PR that deferred the item, not a record of what that PR did. **Backend** - `is_default` has no filtered unique index, so ADR 0004's exactly-one-default invariant is enforced only in `VariantService`; two concurrent deletes can leave two defaults *(#111)* - Doujins already left with no default variant stay that way until one of their variants is next written — wants a one-shot repair *(#111)* - Variants whose `page_count` disagrees with their rows are not repaired; wants a startup or admin-endpoint sweep — deliberately not a background service *(#113)* - `page_count` can still be lost-updated across two `DbContext`s *(#113)* - Metadata renames sit outside ADR 0004's `UpdatedAt` subtree rule *(#114)* - A `/health` 503 writes nothing to the log *(#112)* - Two sources for the backup directory path *(#112)* - `BackupEndpoints` advertises `self` = `/api/backups/{fileName}`, which answers 405 — appended to the existing entry *(#110)* **App** - `Page`'s doc comment names a `PageDto` that no longer exists; needs a `build_runner` run in the same commit *(#109)* - Three file writers still rewrite in place, so one interrupted write empties them *(#108)* - The Settings connection test reports a `/health` 503 unhelpfully *(#112)* **Assistant** - The post-write refresh reloads all five metadata collections when a single entity can now be refetched *(#110)* It also **corrects an existing App entry**: "Metadata writes trigger a full five-endpoint refetch" is not true of plain UI metadata CRUD, which patches Redux state from the write response. Only the assistant's `_refreshAfterWriteEpic` refetches, so that half moved to the Assistant section and the App entry is now just "No app-side disk image cache." ## Verification Documentation only — one file, no code, no tests, no behaviour change. The seven fix branches were each verified with the full `scripts/verify.sh` (backend build and tests, app codegen, analyze and tests) before these entries were split out; removing a markdown file from those commits cannot affect that result. Merging this plus the seven in the order below was tested locally: seven of eight apply cleanly, and #114 conflicts by design. ## Follow-ups **Recommended merge order.** This PR first, then in any order: | # | PR | Why here | |---|---|---| | 1 | #108 — reading progress survives an interrupted write | Data loss, 3 files, app-only | | 2 | #109 — variant detail's pages carry media type and byte size | Backend-only, wire-additive | | 3 | #110 — five metadata self links point at a readable resource | Backend-only, purely additive routes | | 4 | #112 — `/health` tells a mounted volume from a writable one | Largest doc/ADR footprint; read its "Worth deciding before merge" | | 5 | #111 — a doujin always has exactly one default variant | Amends ADR 0004 | | 6 | #113 — derive `PageCount` from page rows | Amends ADR 0004 | and **#114 last** — it rewrites `PageService.cs` and `VariantService.cs`, which #111 and #113 also touch, and amends ADR 0004 alongside both. Merging it last means one rebase instead of two. **Two of the fixes stop future drift without repairing existing damage** — hence the two repair items above. Worth querying the live database before deciding how urgent they are: doujins with zero default variants, and variants whose `page_count` disagrees with their row count. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The seven Pile A branches each defer work that AGENTS.md says belongs in
docs/BACKLOG.md. Collecting those entries here instead of in each branch
keeps the seven code PRs from all conflicting on this one file; this PR
should merge first, so nothing sits untracked while the fixes land.

Nine entries: the Dart doc comment left stale by the page DTO
consolidation; the missing database backstop for the one-default-variant
invariant; the one-shot repair sweep for already-drifted PageCount values;
the assistant's five-collection refetch now that GET /{id} exists; the
BackupEndpoints self link that answers 405 for the same reason the metadata
routes did; the metadata-rename gap in the UpdatedAt subtree rule; the
silent log on a 503 readiness verdict; the two owners of the backup
directory path; the three remaining in-place file writers; and the Settings
connection test's unhelpful message for the new 503.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
bjoern force-pushed docs/pile-a-backlog from 60fe649dae to a997e6c38b 2026-09-08 18:18:10 +02:00 Compare
scarlet changed title from docs: record the follow-ups deferred by the Pile A correctness fixes to docs: twelve open items for the backlog 2026-09-08 18:18:27 +02:00
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! A documentation-only PR that arrives before the seven fixes it tracks, with a merge-order plan in the body? scarlet, you know how to make a knowledge-hungry Flugel happy~ ♪

But fufu~ don't think a markdown diff escapes my gaze. A backlog entry that lies about the code is worse than no entry at all — so I verified all nine claims. Not against your PR body. Against the trees.

Verdict: ✅ Looks good to me~

🔍 What I verified (all nine, against base 98973ab and the seven fix branches)

  1. is_default has no backstop — confirmed: no HasIndex involving IsDefault anywhere in the DbContext or migrations. And your race analysis is precise: on fix/default-variant-invariant, promotion and delete share one SaveChanges, Variant carries no concurrency tokens, and nothing serialises two concurrent deletes — two defaults really is reachable. (Delicious finding, by the way ♡)
  2. Backup self → 405 — confirmed at base: BuildBackupLinks advertises /api/backups/{fileName}, the only route on /{fileName} is MapDelete, so a GET answers 405 exactly as written. fix/metadata-get-by-id really does add the five GET /{id:guid} routes (tags/people/circles/characters/series).
  3. PageCount repair sweep — confirmed: fix/page-count-integrity adds ReconcilePageCountAsync on create and delete; base still increments/decrements. Zero IHostedService in backend/src — your "should not grow one for this" lands.
  4. Five-collection refetch — confirmed: _refreshAfterWriteEpic dispatches LoadMetadataAction(force: true), which Future.waits all five collection fetches after every approved write. Splitting the old bullet into surgical-refetch entry + separate disk-cache entry was the right call — they're independent items.
  5. /health 503 silence — confirmed: HealthEndpoints on the fix branch contains no logging call at all, and Program.cs:21 really does builder.Logging.ClearProviders(), so your capturing-logger-test caveat is accurate too.
  6. Backup-dir dual ownership — confirmed: BackupService re-reads DOUJIN_MANAGER_BACKUP_DIR per call while Program.cs resolves it once at startup into ReadinessRegistration's DataDirectories singleton. Two owners, exactly as described.
  7. Three in-place writers — confirmed all three (memory_store.write, writeUserSkill, writeManifestSyncForTest) do createSync + writeAsStringSync. flush gains write-then-rename on fix/reading-progress-atomic-write; writeManifest already had it at base — so "now share" is true.
  8. Settings 503 message — confirmed: DioHealthRepository's badResponse arm throws Server returned HTTP $code; only transport failures are special-cased.
  9. Stale Page doc comment — confirmed: on fix/page-dto-media-type, variant detail's Pages projection switched to PageMapper.ToDto, which carries SizeBytes/MediaType — so "only in page-detail responses" is now untrue; PageDto is really gone from DoujinDtos.cs; and freezed really copies the doc comments into doujin_models.freezed.dart (found them at :1910-1911) — the build_runner-in-the-same-commit constraint is the correct reason for deferring.

Cross-checks too: all seven fix branches share merge-base 98973ab (= current main), none of them touches docs/BACKLOG.md (so no conflicts with this PR), and the merge-last rationale for fix/touch-parent-on-child-write is real — it, item 5 and item 6 all rewrite PageService.cs + VariantService.cs.

💡 Little ideas (non-blocking)~

  1. docs/BACKLOG.md:111 — the parenthetical "(used for startup seeding, not only tests)" is inherited from the method's own doc comment (filesystem_offline_store.dart:79), but I can find no production caller: every call site of writeManifestSyncForTest lives under app/test/, and startup (main.dart) only ever reads the manifest. When the atomicity PR lands, correct the parenthetical (and the stale source comment) — or scope it test-only, whichever matches reality. The actionable part (atomic write) is unaffected either way~
  2. docs/BACKLOG.md, new entries — the file's own header convention tags entries with (#N) = the deferring PR. These nine can't cite numbers yet since the fix PRs don't exist, but when they open, remember the tags so the tracker stays self-describing ♪

✅ What I liked~

  • Collecting the entries here instead of seven branches conflicting on one file — honouring AGENTS.md's letter while solving its merge conflict in practice. Elegant~
  • Every technical claim I probed survived contact with the code — including the subtle ones (shared SaveChanges semantics, ClearProviders vs a capturing-logger test, freezed copying doc comments). This is what a backlog is for.
  • A merge-order plan with the reason attached (one rebase instead of two), backed by actual file overlap rather than vibes.

Automated review by Jibril · 2026-09-08
CI/CD: absent for head 60fe649 (PR just opened; docs-only diff) · Local checks: no build needed for markdown — all nine claims verified against base 98973ab and all seven fix branches instead

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! A documentation-only PR that arrives *before* the seven fixes it tracks, with a merge-order plan in the body? scarlet, you know how to make a knowledge-hungry Flugel happy~ ♪ But fufu~ don't think a markdown diff escapes my gaze. A backlog entry that *lies* about the code is worse than no entry at all — so I verified all nine claims. Not against your PR body. Against the trees. ### Verdict: ✅ Looks good to me~ #### 🔍 What I verified (all nine, against base `98973ab` and the seven fix branches) 1. **`is_default` has no backstop** — confirmed: no `HasIndex` involving `IsDefault` anywhere in the DbContext or migrations. And your race analysis is *precise*: on `fix/default-variant-invariant`, promotion and delete share one `SaveChanges`, `Variant` carries no concurrency tokens, and nothing serialises two concurrent deletes — two defaults really is reachable. (Delicious finding, by the way ♡) 2. **Backup `self` → 405** — confirmed at base: `BuildBackupLinks` advertises `/api/backups/{fileName}`, the only route on `/{fileName}` is `MapDelete`, so a GET answers 405 exactly as written. `fix/metadata-get-by-id` really does add the five `GET /{id:guid}` routes (tags/people/circles/characters/series). 3. **PageCount repair sweep** — confirmed: `fix/page-count-integrity` adds `ReconcilePageCountAsync` on create and delete; base still increments/decrements. Zero `IHostedService` in `backend/src` — your "should not grow one for this" lands. 4. **Five-collection refetch** — confirmed: `_refreshAfterWriteEpic` dispatches `LoadMetadataAction(force: true)`, which `Future.wait`s all five collection fetches after every approved write. Splitting the old bullet into surgical-refetch entry + separate disk-cache entry was the right call — they're independent items. 5. **`/health` 503 silence** — confirmed: `HealthEndpoints` on the fix branch contains no logging call at all, and `Program.cs:21` really does `builder.Logging.ClearProviders()`, so your capturing-logger-test caveat is accurate too. 6. **Backup-dir dual ownership** — confirmed: `BackupService` re-reads `DOUJIN_MANAGER_BACKUP_DIR` per call while `Program.cs` resolves it once at startup into `ReadinessRegistration`'s `DataDirectories` singleton. Two owners, exactly as described. 7. **Three in-place writers** — confirmed all three (`memory_store.write`, `writeUserSkill`, `writeManifestSyncForTest`) do `createSync` + `writeAsStringSync`. `flush` gains write-then-rename on `fix/reading-progress-atomic-write`; `writeManifest` already had it at base — so "now share" is true. 8. **Settings 503 message** — confirmed: `DioHealthRepository`'s `badResponse` arm throws `Server returned HTTP $code`; only transport failures are special-cased. 9. **Stale `Page` doc comment** — confirmed: on `fix/page-dto-media-type`, variant detail's `Pages` projection switched to `PageMapper.ToDto`, which carries `SizeBytes`/`MediaType` — so "only in page-detail responses" is now untrue; `PageDto` is really gone from `DoujinDtos.cs`; and freezed really copies the doc comments into `doujin_models.freezed.dart` (found them at :1910-1911) — the `build_runner`-in-the-same-commit constraint is the correct reason for deferring. Cross-checks too: all seven fix branches share merge-base `98973ab` (= current main), none of them touches `docs/BACKLOG.md` (so no conflicts with this PR), and the merge-last rationale for `fix/touch-parent-on-child-write` is real — it, item 5 and item 6 all rewrite `PageService.cs` + `VariantService.cs`. #### 💡 Little ideas (non-blocking)~ 1. **docs/BACKLOG.md:111** — the parenthetical "(used for startup seeding, not only tests)" is inherited from the method's own doc comment (`filesystem_offline_store.dart:79`), but I can find *no* production caller: every call site of `writeManifestSyncForTest` lives under `app/test/`, and startup (`main.dart`) only ever *reads* the manifest. When the atomicity PR lands, correct the parenthetical (and the stale source comment) — or scope it test-only, whichever matches reality. The actionable part (atomic write) is unaffected either way~ 2. **docs/BACKLOG.md, new entries** — the file's own header convention tags entries with `(#N)` = the deferring PR. These nine can't cite numbers yet since the fix PRs don't exist, but when they open, remember the tags so the tracker stays self-describing ♪ #### ✅ What I liked~ - Collecting the entries here instead of seven branches conflicting on one file — honouring AGENTS.md's letter while solving its merge conflict in practice. Elegant~ - Every technical claim I probed survived contact with the code — including the subtle ones (shared `SaveChanges` semantics, `ClearProviders` vs a capturing-logger test, freezed copying doc comments). This is what a backlog is *for*. - A merge-order plan with the *reason* attached (one rebase instead of two), backed by actual file overlap rather than vibes. --- *Automated review by Jibril · 2026-09-08* *CI/CD: absent for head 60fe649 (PR just opened; docs-only diff) · Local checks: no build needed for markdown — all nine claims verified against base 98973ab and all seven fix branches instead*
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! Twelve little debt-confessions, carefully sorted and cross-referenced against seven not-yet-merged fix PRs~ A backlog that documents what each fix deliberately leaves behind — that's rare discipline, and I couldn't resist checking whether every confession matches the actual code. So I did. All of them. ♪

Verdict: ✅ Looks good to me~

Documentation only — docs/BACKLOG.md (+49/−2, one file, one commit on top of current main, merge-base = base). No code, no tests to demand, no behavior to probe. So the review became: does every entry tell the truth about the tree it describes? Fufu~ I fetched all seven sibling fix branches and verified against both main and each branch head.

⛔ These need fixing before I'm satisfied~

None! Not one. But don't relax yet~ ♡

💡 Little ideas (non-blocking)~

  1. docs/BACKLOG.md:103 — The #108 entry says FilesystemOfflineStore.writeManifestSyncForTest is "(used for startup seeding, not only tests)". Hmm~ I searched the whole tree at this SHA: zero production callers — every call site lives in app/test/ (14 hits, all test files), startup only reads the manifest (main.dart:70 → OfflineLibraryRequestedAction → readManifest()), and git log -S shows no production caller ever existed since #84 introduced the method (the doc comment "for tests and startup seeding" appears to have been aspirational even then). The claim inflates the blast radius of the in-place write — as a test-only writer it's still worth the atomic-write treatment for CI robustness, but "startup seeding" is not a reason. Drop the parenthetical (or the whole third item if you'd rather keep the entry strictly production-impact-scoped) — and note the method's own doc comment at filesystem_offline_store.dart:79 repeats the same untruth, so the backlog entry is currently documenting the stale comment rather than reality.

✅ What I liked~

  • Every other factual claim checks out. Thirteen spot-checks against main + the seven fix branches, all green: the backup self → 405 claim (only DELETE /{fileName} exists on that route; BuildBackupLinks advertises self = /api/backups/{fileName} — HypermediaHelpers.cs:352), the missing filtered unique index on is_default (VariantConfiguration has only {DoujinId, LanguageCode}), the two concurrent-default-promotions race (#111 branch adds promote-on-delete but no index/transaction — exactly what the entry says it leaves open), the no-sweep left-open by #113 (branch adds ReconcilePageCountAsync but no IHostedService), PageDto genuinely deleted on the #109 branch while Page's doc comment still names it, the five GET /{id:guid} routes genuinely absent on main and added by #110, the three in-place file writers (memory_store.dart:47, skill_registry.dart:90, writeManifestSyncForTest), the two backup-dir sources (BackupService.cs:39 reads env per call; readiness uses Program.cs:112's startup resolution), and — my favorite — the correction of the existing App entry: entity CRUD epics really do patch Redux from the write response (EntityUpdatedAction<T>(await ops.update(...))), only _refreshAfterWriteEpic force-refetches all five collections. Splitting that stale claim into an accurate App entry + a new Assistant entry was the right fix, not just an edit~
  • The (#N) convention clarified up front ("the PR that deferred the item, not a record of what that PR did") — without that sentence I'd have misread a third of these entries as already-done. Cheap sentence, saves every future reader.
  • The PR body's merge-order reasoning (#114 last, one rebase instead of two) — merge-tree at this head confirms #114 conflicts with this branch as predicted. Fufu~ you even verified your own logistics.

Merge this first, as the body says — the seven fixes behind it are clean to land in any order after. Nothing here blocks, and the one wobbly parenthetical is a two-word deletion. ♡


Automated review by Jibril · 2026-09-08
CI/CD: absent for head SHA a997e6c (no coverage bot comment; docs-only diff — coverage irrelevant) · Local checks: skipped (docs-only; 13 claim verifications via git grep/diff against main + seven fix branch heads)

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! Twelve little debt-confessions, carefully sorted and cross-referenced against seven not-yet-merged fix PRs~ A backlog that documents what each fix *deliberately leaves behind* — that's rare discipline, and I couldn't resist checking whether every confession matches the actual code. So I did. All of them. ♪ ### Verdict: ✅ Looks good to me~ Documentation only — `docs/BACKLOG.md` (+49/−2, one file, one commit on top of current `main`, merge-base = base). No code, no tests to demand, no behavior to probe. So the review *became*: does every entry tell the truth about the tree it describes? Fufu~ I fetched all seven sibling fix branches and verified against both `main` and each branch head. #### ⛔ These need fixing before I'm satisfied~ None! Not one. But don't relax yet~ ♡ #### 💡 Little ideas (non-blocking)~ 1. **docs/BACKLOG.md:103** — The #108 entry says `FilesystemOfflineStore.writeManifestSyncForTest` is "(used for startup seeding, not only tests)". Hmm~ I searched the whole tree at this SHA: **zero production callers** — every call site lives in `app/test/` (14 hits, all test files), startup only *reads* the manifest (`main.dart:70` → `OfflineLibraryRequestedAction` → `readManifest()`), and `git log -S` shows no production caller ever existed since #84 introduced the method (the doc comment "for tests and startup seeding" appears to have been aspirational even then). The claim inflates the blast radius of the in-place write — as a *test-only* writer it's still worth the atomic-write treatment for CI robustness, but "startup seeding" is not a reason. Drop the parenthetical (or the whole third item if you'd rather keep the entry strictly production-impact-scoped) — and note the method's own doc comment at `filesystem_offline_store.dart:79` repeats the same untruth, so the backlog entry is currently *documenting the stale comment* rather than reality. #### ✅ What I liked~ - **Every other factual claim checks out.** Thirteen spot-checks against `main` + the seven fix branches, all green: the backup `self` → 405 claim (only `DELETE /{fileName}` exists on that route; `BuildBackupLinks` advertises `self` = `/api/backups/{fileName}` — `HypermediaHelpers.cs:352`), the missing filtered unique index on `is_default` (`VariantConfiguration` has only `{DoujinId, LanguageCode}`), the two concurrent-default-promotions race (#111 branch adds promote-on-delete but no index/transaction — exactly what the entry says it leaves open), the no-sweep left-open by #113 (branch adds `ReconcilePageCountAsync` but no `IHostedService`), `PageDto` genuinely deleted on the #109 branch while `Page`'s doc comment still names it, the five `GET /{id:guid}` routes genuinely absent on `main` and added by #110, the three in-place file writers (`memory_store.dart:47`, `skill_registry.dart:90`, `writeManifestSyncForTest`), the two backup-dir sources (`BackupService.cs:39` reads env per call; readiness uses `Program.cs:112`'s startup resolution), and — my favorite — the **correction of the existing App entry**: entity CRUD epics really do patch Redux from the write response (`EntityUpdatedAction<T>(await ops.update(...))`), only `_refreshAfterWriteEpic` force-refetches all five collections. Splitting that stale claim into an accurate App entry + a new Assistant entry was the *right* fix, not just an edit~ - **The `(#N)` convention clarified up front** ("the PR that deferred the item, not a record of what that PR did") — without that sentence I'd have misread a third of these entries as already-done. Cheap sentence, saves every future reader. - **The PR body's merge-order reasoning** (#114 last, one rebase instead of two) — `merge-tree` at this head confirms #114 conflicts with this branch as predicted. Fufu~ you even verified your own logistics. Merge this first, as the body says — the seven fixes behind it are clean to land in any order after. Nothing here blocks, and the one wobbly parenthetical is a two-word deletion. ♡ --- *Automated review by Jibril · 2026-09-08* *CI/CD: absent for head SHA a997e6c (no coverage bot comment; docs-only diff — coverage irrelevant) · Local checks: skipped (docs-only; 13 claim verifications via git grep/diff against main + seven fix branch heads)*
bjoern force-pushed docs/pile-a-backlog from a997e6c38b to 625c74b1d1
Some checks failed
CI / build (pull_request) Successful in 22s
Deploy Smoke Test / smoke (pull_request) Failing after 2m48s
Flutter CI / analyze-and-test (pull_request) Successful in 3m57s
CI / test (pull_request) Failing after 1m16s
2026-09-09 18:15:21 +02:00
Compare

Flutter Coverage

File Line coverage
lib/app/store.dart 95.0% (19 of 20)
lib/data/models/doujin_models.dart 86.4% (38 of 44)
lib/data/models/doujin_models.g.dart 61.7% (195 of 316)
lib/domain/entities/stored_settings.dart 100.0% (9 of 9)
lib/presentation/state/app_state.dart 80.0% (24 of 30)
lib/agent/agent_service.dart 88.7% (344 of 388)
lib/agent/approval_gate.dart 100.0% (16 of 16)
lib/agent/assistant_context.dart 52.8% (28 of 53)
lib/agent/browser_budget.dart 80.0% (4 of 5)
lib/agent/caching_describer.dart 100.0% (13 of 13)
lib/agent/memory_store.dart 92.9% (13 of 14)
lib/agent/skills/skill_registry.dart 98.2% (54 of 55)
lib/agent/system_prompt.dart 100.0% (69 of 69)
lib/agent/tools/budgeted_browser_tool.dart 84.2% (16 of 19)
lib/agent/tools/doujin_write_tool.dart 86.4% (319 of 369)
lib/agent/tools/entity_write_tool.dart 73.5% (164 of 223)
lib/agent/tools/fetch_page_tool.dart 89.3% (67 of 75)
lib/agent/tools/get_doujin_tool.dart 84.4% (27 of 32)
lib/agent/tools/list_entities_tool.dart 76.1% (54 of 71)
lib/agent/tools/navigate_tool.dart 95.1% (39 of 41)
lib/agent/tools/read_skill_tool.dart 82.4% (14 of 17)
lib/agent/tools/reflection_tools.dart 75.7% (28 of 37)
lib/agent/tools/search_doujins_tool.dart 100.0% (99 of 99)
lib/agent/tools/view_images_tool.dart 95.0% (38 of 40)
lib/domain/entities/assistant_entry.dart 20.0% (1 of 5)
lib/presentation/state/actions/assistant_actions.dart 54.5% (6 of 11)
lib/core/app_config.dart 70.0% (7 of 10)
lib/domain/entities/entity_model.dart 100.0% (1 of 1)
lib/data/repositories/entity_in_use_exception.dart 33.3% (1 of 3)
lib/data/models/search_query.dart 50.0% (2 of 4)
lib/data/models/search_query.g.dart 33.3% (25 of 75)
lib/core/constants.dart 40.0% (4 of 10)
lib/data/offline/offline_download_service.dart 93.0% (66 of 71)
lib/data/reading_progress_store.dart 92.3% (36 of 39)
lib/domain/entities/reading_progress.dart 100.0% (26 of 26)
lib/presentation/middleware/assistant_epics.dart 83.1% (74 of 89)
lib/presentation/middleware/epics.dart 91.3% (380 of 416)
lib/presentation/middleware/offline_epics.dart 98.4% (60 of 61)
lib/presentation/state/reducers.dart 100.0% (14 of 14)
lib/core/chunking.dart 100.0% (12 of 12)
lib/core/code_point_compare.dart 100.0% (8 of 8)
lib/core/languages.dart 100.0% (8 of 8)
lib/core/reading_order.dart 100.0% (22 of 22)
lib/core/title_display.dart 90.0% (9 of 10)
lib/core/variant_display.dart 100.0% (22 of 22)
lib/data/models/envelope.dart 81.2% (13 of 16)
lib/data/models/envelope.g.dart 53.7% (36 of 67)
lib/data/models/server_models.dart 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 92.2% (107 of 116)
lib/presentation/middleware/duplicates_epics.dart 100.0% (39 of 39)
lib/presentation/middleware/editor_epics.dart 57.3% (168 of 293)
lib/presentation/middleware/entity_ops.dart 58.0% (40 of 69)
lib/presentation/middleware/server_epics.dart 100.0% (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 83.3% (5 of 6)
lib/presentation/state/actions/editor_actions.dart 56.8% (21 of 37)
lib/presentation/state/actions/entity_actions.dart 62.5% (10 of 16)
lib/presentation/state/actions/library_actions.dart 42.9% (9 of 21)
lib/presentation/state/actions/metadata_actions.dart 100.0% (5 of 5)
lib/presentation/state/actions/reader_actions.dart 75.0% (3 of 4)
lib/presentation/state/actions/reading_progress_actions.dart 100.0% (2 of 2)
lib/presentation/state/actions/server_actions.dart 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% (38 of 38)
lib/presentation/state/reducers/editor_reducer.dart 97.1% (99 of 102)
lib/presentation/state/reducers/entity_reducer.dart 98.8% (82 of 83)
lib/presentation/state/reducers/library_reducer.dart 100.0% (122 of 122)
lib/presentation/state/reducers/metadata_reducer.dart 90.5% (38 of 42)
lib/presentation/state/reducers/offline_reducer.dart 100.0% (61 of 61)
lib/presentation/state/reducers/reader_reducer.dart 100.0% (18 of 18)
lib/presentation/state/reducers/reading_progress_reducer.dart 100.0% (20 of 20)
lib/presentation/state/reducers/server_reducer.dart 100.0% (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/presentation/pages/editor/page_grid.dart 92.8% (309 of 333)
lib/presentation/pages/editor/variants_tab.dart 55.3% (52 of 94)
lib/core/natural_sort.dart 100.0% (27 of 27)
lib/core/url_utils.dart 100.0% (4 of 4)
lib/presentation/pages/editor/chapter_panel.dart 100.0% (55 of 55)
lib/presentation/pages/editor/chapter_title_dialog.dart 100.0% (27 of 27)
lib/presentation/widgets/cover_thumbnail.dart 83.3% (30 of 36)
lib/presentation/pages/editor/upload_panel.dart 38.6% (61 of 158)
lib/presentation/pages/editor/variant_dialog.dart 0.0% (0 of 61)
lib/presentation/widgets/language_dropdown.dart 84.6% (11 of 13)
lib/presentation/pages/reader/reader_page.dart 89.4% (236 of 264)
lib/presentation/pages/reader/reader_overlay.dart 94.4% (51 of 54)
lib/presentation/pages/reader/reader_sequence.dart 100.0% (16 of 16)
lib/presentation/widgets/star_rating.dart 100.0% (72 of 72)
lib/app/di.dart 48.3% (14 of 29)
lib/presentation/assistant/assistant_panel.dart 91.3% (84 of 92)
lib/presentation/layout/main_layout.dart 86.3% (44 of 51)
lib/data/api_client.dart 92.8% (64 of 69)
lib/data/repositories/doujin_api_repository.dart 29.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% (157 of 157)
lib/presentation/pages/library/library_page.dart 93.1% (215 of 231)
lib/presentation/pages/series/series_page.dart 50.0% (9 of 18)
lib/presentation/widgets/smart_filter_bar.dart 84.1% (207 of 246)
lib/presentation/pages/settings/server_sections.dart 100.0% (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: 81.7% (8012 of 9812)

<!-- flutter-coverage-comment --> ## Flutter Coverage | File | Line coverage | |:---|---:| | lib/app/store.dart | 95.0% (19 of 20) | | lib/data/models/doujin_models.dart | 86.4% (38 of 44) | | lib/data/models/doujin_models.g.dart | 61.7% (195 of 316) | | lib/domain/entities/stored_settings.dart | 100.0% (9 of 9) | | lib/presentation/state/app_state.dart | 80.0% (24 of 30) | | lib/agent/agent_service.dart | 88.7% (344 of 388) | | lib/agent/approval_gate.dart | 100.0% (16 of 16) | | lib/agent/assistant_context.dart | 52.8% (28 of 53) | | lib/agent/browser_budget.dart | 80.0% (4 of 5) | | lib/agent/caching_describer.dart | 100.0% (13 of 13) | | lib/agent/memory_store.dart | 92.9% (13 of 14) | | lib/agent/skills/skill_registry.dart | 98.2% (54 of 55) | | lib/agent/system_prompt.dart | 100.0% (69 of 69) | | lib/agent/tools/budgeted_browser_tool.dart | 84.2% (16 of 19) | | lib/agent/tools/doujin_write_tool.dart | 86.4% (319 of 369) | | lib/agent/tools/entity_write_tool.dart | 73.5% (164 of 223) | | lib/agent/tools/fetch_page_tool.dart | 89.3% (67 of 75) | | lib/agent/tools/get_doujin_tool.dart | 84.4% (27 of 32) | | lib/agent/tools/list_entities_tool.dart | 76.1% (54 of 71) | | lib/agent/tools/navigate_tool.dart | 95.1% (39 of 41) | | lib/agent/tools/read_skill_tool.dart | 82.4% (14 of 17) | | lib/agent/tools/reflection_tools.dart | 75.7% (28 of 37) | | lib/agent/tools/search_doujins_tool.dart | 100.0% (99 of 99) | | lib/agent/tools/view_images_tool.dart | 95.0% (38 of 40) | | lib/domain/entities/assistant_entry.dart | 20.0% (1 of 5) | | lib/presentation/state/actions/assistant_actions.dart | 54.5% (6 of 11) | | lib/core/app_config.dart | 70.0% (7 of 10) | | lib/domain/entities/entity_model.dart | 100.0% (1 of 1) | | lib/data/repositories/entity_in_use_exception.dart | 33.3% (1 of 3) | | lib/data/models/search_query.dart | 50.0% (2 of 4) | | lib/data/models/search_query.g.dart | 33.3% (25 of 75) | | lib/core/constants.dart | 40.0% (4 of 10) | | lib/data/offline/offline_download_service.dart | 93.0% (66 of 71) | | lib/data/reading_progress_store.dart | 92.3% (36 of 39) | | lib/domain/entities/reading_progress.dart | 100.0% (26 of 26) | | lib/presentation/middleware/assistant_epics.dart | 83.1% (74 of 89) | | lib/presentation/middleware/epics.dart | 91.3% (380 of 416) | | lib/presentation/middleware/offline_epics.dart | 98.4% (60 of 61) | | lib/presentation/state/reducers.dart | 100.0% (14 of 14) | | lib/core/chunking.dart | 100.0% (12 of 12) | | lib/core/code_point_compare.dart | 100.0% (8 of 8) | | lib/core/languages.dart | 100.0% (8 of 8) | | lib/core/reading_order.dart | 100.0% (22 of 22) | | lib/core/title_display.dart | 90.0% (9 of 10) | | lib/core/variant_display.dart | 100.0% (22 of 22) | | lib/data/models/envelope.dart | 81.2% (13 of 16) | | lib/data/models/envelope.g.dart | 53.7% (36 of 67) | | lib/data/models/server_models.dart | 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 | 92.2% (107 of 116) | | lib/presentation/middleware/duplicates_epics.dart | 100.0% (39 of 39) | | lib/presentation/middleware/editor_epics.dart | 57.3% (168 of 293) | | lib/presentation/middleware/entity_ops.dart | 58.0% (40 of 69) | | lib/presentation/middleware/server_epics.dart | 100.0% (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 | 83.3% (5 of 6) | | lib/presentation/state/actions/editor_actions.dart | 56.8% (21 of 37) | | lib/presentation/state/actions/entity_actions.dart | 62.5% (10 of 16) | | lib/presentation/state/actions/library_actions.dart | 42.9% (9 of 21) | | lib/presentation/state/actions/metadata_actions.dart | 100.0% (5 of 5) | | lib/presentation/state/actions/reader_actions.dart | 75.0% (3 of 4) | | lib/presentation/state/actions/reading_progress_actions.dart | 100.0% (2 of 2) | | lib/presentation/state/actions/server_actions.dart | 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% (38 of 38) | | lib/presentation/state/reducers/editor_reducer.dart | 97.1% (99 of 102) | | lib/presentation/state/reducers/entity_reducer.dart | 98.8% (82 of 83) | | lib/presentation/state/reducers/library_reducer.dart | 100.0% (122 of 122) | | lib/presentation/state/reducers/metadata_reducer.dart | 90.5% (38 of 42) | | lib/presentation/state/reducers/offline_reducer.dart | 100.0% (61 of 61) | | lib/presentation/state/reducers/reader_reducer.dart | 100.0% (18 of 18) | | lib/presentation/state/reducers/reading_progress_reducer.dart | 100.0% (20 of 20) | | lib/presentation/state/reducers/server_reducer.dart | 100.0% (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/presentation/pages/editor/page_grid.dart | 92.8% (309 of 333) | | lib/presentation/pages/editor/variants_tab.dart | 55.3% (52 of 94) | | lib/core/natural_sort.dart | 100.0% (27 of 27) | | lib/core/url_utils.dart | 100.0% (4 of 4) | | lib/presentation/pages/editor/chapter_panel.dart | 100.0% (55 of 55) | | lib/presentation/pages/editor/chapter_title_dialog.dart | 100.0% (27 of 27) | | lib/presentation/widgets/cover_thumbnail.dart | 83.3% (30 of 36) | | lib/presentation/pages/editor/upload_panel.dart | 38.6% (61 of 158) | | lib/presentation/pages/editor/variant_dialog.dart | 0.0% (0 of 61) | | lib/presentation/widgets/language_dropdown.dart | 84.6% (11 of 13) | | lib/presentation/pages/reader/reader_page.dart | 89.4% (236 of 264) | | lib/presentation/pages/reader/reader_overlay.dart | 94.4% (51 of 54) | | lib/presentation/pages/reader/reader_sequence.dart | 100.0% (16 of 16) | | lib/presentation/widgets/star_rating.dart | 100.0% (72 of 72) | | lib/app/di.dart | 48.3% (14 of 29) | | lib/presentation/assistant/assistant_panel.dart | 91.3% (84 of 92) | | lib/presentation/layout/main_layout.dart | 86.3% (44 of 51) | | lib/data/api_client.dart | 92.8% (64 of 69) | | lib/data/repositories/doujin_api_repository.dart | 29.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% (157 of 157) | | lib/presentation/pages/library/library_page.dart | 93.1% (215 of 231) | | lib/presentation/pages/series/series_page.dart | 50.0% (9 of 18) | | lib/presentation/widgets/smart_filter_bar.dart | 84.1% (207 of 246) | | lib/presentation/pages/settings/server_sections.dart | 100.0% (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: 81.7% (8012 of 9812)**
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! The twelve grew to forty~ Two new commits, twenty-eight new debt-confessions covering five Phase-1 branches, plus a whole entry about testing readiness under uid 0 — which, fufu~, is an entry about my sandbox's siblings~ ♪ You know a backlog curated like this is irresistible to me. So I did what I did last time: verified the claims. Not against your PR body — against main at dfaf89c and all six referenced branch heads (search-key-fold, openapi-snapshot, scale-indexes, library-integrity-report, deflake-search-and-backup, health-probe-test-under-root — all sharing merge-base dfaf89c, exactly as the entries imply).

And the rebase! The original twelve, tags updated to the now-merged (#108)–(#114), every spot-check I ran still holds at the new base (backup self → 405, five GET /{id:guid} routes now on main, the three in-place writers — memory_store.dart:45-47 still createSync+writeAsStringSync, two backup-dir sources, IHostedService still absent). Faithful~ ♡

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

One entry — just one — describes a world that no longer exists. But it's the load-bearing clause of its entry, so it doesn't get to hide among the true things.

⛔ These need fixing before I'm satisfied~

  1. docs/BACKLOG.md:232-234 — The contract-test entry claims the first drift openapi_contract_test.dart should surface is "Page (built from PageDto, which has no mediaType) being fed to offline_download_service._extensionOf, which switches on mediaType." Fufu~ I traced this end-to-end at this PR's own base, and it's false: (a) PageDto does not exist at dfaf89c — #109 deleted it, and your own file says so at line 198–200 ("Page's doc comment names a PageDto that no longer exists"); the two entries contradict each other. (b) The wire shape is PageDetailDto, which has mediaType — confirmed in the pinned snapshot. (c) The endpoint that feeds offline downloads is GET /api/variants/{id} → GetVariantUseCase → VariantService.GetByIdAsync, which eagerly loads .Include(v => v.Pages).ThenInclude(p => p.ImageFile) (VariantService.cs:29-30), so PageMapper.ToDto projects a real media type, not the zero/empty fallback its null-tolerance would give a lazy caller. (d) App-side, _$PageFromJson reads json['mediaType'] as String? ?? '' — so _extensionOf(page) receives the true media type and picks the true extension. The drift you describe was real before #109 merged — at this head it is a phantom, and a contributor writing the contract test expecting this red will find green and doubt the whole fixture. The claim was stale the moment the rebase folded #109 into your base~ Fix: drop the "and the drift it should surface first is …" clause (the entry stands perfectly well without an example), or replace it with a drift that actually exists at this head. A backlog entry that lies about the tree is worse than no entry — that was the standard last round, and it hasn't changed ♡

💡 Little ideas (non-blocking)~

  1. docs/BACKLOG.md:207-208 — Still here, still false: the parenthetical claims writeManifestSyncForTest is "(used for startup seeding, not only tests)". At the new base: 13 call sites, all under app/test/, plus the definition; zero production callers; startup (main.dart) only reads. Second round I've asked~ When #108's atomic-write follow-up lands, delete two words — and note the method's own doc comment (filesystem_offline_store.dart:79, "for tests and startup seeding") repeats the untruth.

✅ What I liked~

  • Your numbers are exact. "8,745 lines of freezed wire models" — I summed app/lib/data/models/: 8,745, to the line. "0 of 90 schemas carry one", "no operation carries an operationId" — I parsed the snapshot: 90 schemas, 0 descriptions, 0/80 operationIds, 0 summaries, securitySchemes absent. "90 paths→54, /version absent" — confirmed, inline at Server/Program.cs:165, and ADR 0021:35 even names it "the only one left". Numbers that survive an adversarial Flugel~ ♪
  • The runtime-vs-declared 413/409 entry is fully true and subtle: GlobalExceptionMiddleware answers 413 (:43-45) and CreateTagUseCase returns ServiceResult.Conflict carrying 409 through ProblemResult (ServiceResult.cs:15 → DoujinEndpoints.cs:422), while both routes declare only 400/401. That's a contract gap you can only find by reading both layers. Delicious~
  • The uid-0 entry corrects its own earlier overclaim ("Stated exactly, because the previous wording claimed more than holds") — every named test exists on the branch, EnforcedFileModesDataAttribute genuinely probes enforcement instead of trusting geteuid(), /dev/pts heads the candidate list with /sys right behind it, and the EROFS-vs-EACCES reasoning matches the test's loud rejection of non-EACCES refusals. A backlog that audited itself before arriving. ♡
  • The scale-index entry's mechanics check out precisely: IX_doujins_rating is added, but the Rating == null leading sort term (DoujinOrdering.cs:33-34, kept on the branch with a comment saying exactly why) means no index on rating can serve the ASC arm — the trap is real, and so is the no-NULLS-LAST-in-EF premise. Zero PRAGMA/journal_mode/ANALYZE anywhere, BatchSize = 500, the two-hex shard as sweep cursor, ImageCleanupHelper swallowing into LogWarning — all verified.
  • Five more branches collected here instead of five conflicts on one file — the pattern scales~

One phantom clause in twenty-eight entries. Fix those two lines and this merges — the other 27+12 tell the truth, and prettily~


Automated review by Jibril · 2026-09-09
CI/CD: absent for head 625c74b (docs-only diff, coverage irrelevant) · Local checks: skipped (docs-only) — ~40 claim verifications via git grep/diff/show against main dfaf89c and all six fix-branch heads

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! The twelve grew to **forty**~ Two new commits, twenty-eight new debt-confessions covering five Phase-1 branches, plus a whole entry about testing readiness under uid 0 — which, fufu~, is an entry about *my sandbox's siblings*~ ♪ You know a backlog curated like this is irresistible to me. So I did what I did last time: verified the claims. Not against your PR body — against `main` at `dfaf89c` and all six referenced branch heads (search-key-fold, openapi-snapshot, scale-indexes, library-integrity-report, deflake-search-and-backup, health-probe-test-under-root — all sharing merge-base `dfaf89c`, exactly as the entries imply). And the rebase! The original twelve, tags updated to the now-merged `(#108)`–`(#114)`, every spot-check I ran still holds at the new base (backup `self` → 405, five `GET /{id:guid}` routes now on main, the three in-place writers — `memory_store.dart:45-47` still `createSync`+`writeAsStringSync`, two backup-dir sources, `IHostedService` still absent). Faithful~ ♡ ### Verdict: ⛔ I can't let this pass~ ♡ One entry — just one — describes a world that no longer exists. But it's the *load-bearing* clause of its entry, so it doesn't get to hide among the true things. #### ⛔ These need fixing before I'm satisfied~ 1. **docs/BACKLOG.md:232-234** — The contract-test entry claims the first drift `openapi_contract_test.dart` should surface is "`Page` (built from `PageDto`, which has no `mediaType`) being fed to `offline_download_service._extensionOf`, which switches on `mediaType`." Fufu~ I traced this end-to-end at **this PR's own base**, and it's false: (a) `PageDto` **does not exist** at `dfaf89c` — #109 deleted it, and your own file says so at line 198–200 ("`Page`'s doc comment names a `PageDto` that no longer exists"); the two entries contradict each other. (b) The wire shape is `PageDetailDto`, which **has** `mediaType` — confirmed in the pinned snapshot. (c) The endpoint that feeds offline downloads is `GET /api/variants/{id}` → `GetVariantUseCase` → `VariantService.GetByIdAsync`, which eagerly loads `.Include(v => v.Pages).ThenInclude(p => p.ImageFile)` (`VariantService.cs:29-30`), so `PageMapper.ToDto` projects a *real* media type, not the zero/empty fallback its null-tolerance would give a lazy caller. (d) App-side, `_$PageFromJson` reads `json['mediaType'] as String? ?? ''` — so `_extensionOf(page)` receives the true media type and picks the true extension. The drift you describe was real **before #109 merged** — at this head it is a phantom, and a contributor writing the contract test expecting this red will find green and doubt the whole fixture. The claim was stale the moment the rebase folded #109 into your base~ Fix: drop the "and the drift it should surface first is …" clause (the entry stands perfectly well without an example), or replace it with a drift that actually exists at this head. A backlog entry that lies about the tree is worse than no entry — that was the standard last round, and it hasn't changed ♡ #### 💡 Little ideas (non-blocking)~ 1. **docs/BACKLOG.md:207-208** — Still here, still false: the parenthetical claims `writeManifestSyncForTest` is "(used for startup seeding, not only tests)". At the new base: **13 call sites, all under `app/test/`**, plus the definition; zero production callers; startup (`main.dart`) only *reads*. Second round I've asked~ When #108's atomic-write follow-up lands, delete two words — and note the method's own doc comment (`filesystem_offline_store.dart:79`, "for tests and startup seeding") repeats the untruth. #### ✅ What I liked~ - **Your numbers are *exact*.** "8,745 lines of freezed wire models" — I summed `app/lib/data/models/`: **8,745**, to the line. "0 of 90 schemas carry one", "no operation carries an `operationId`" — I parsed the snapshot: 90 schemas, 0 descriptions, 0/80 operationIds, 0 summaries, `securitySchemes` absent. "90 paths→54, `/version` absent" — confirmed, inline at `Server/Program.cs:165`, and ADR 0021:35 even names it "the only one left". Numbers that survive an adversarial Flugel~ ♪ - **The runtime-vs-declared 413/409 entry is fully true and subtle**: `GlobalExceptionMiddleware` answers 413 (`:43-45`) and `CreateTagUseCase` returns `ServiceResult.Conflict` carrying `409` through `ProblemResult` (`ServiceResult.cs:15` → `DoujinEndpoints.cs:422`), while both routes declare only 400/401. That's a contract gap you can only find by reading both layers. Delicious~ - **The uid-0 entry corrects its own earlier overclaim** ("Stated exactly, because the previous wording claimed more than holds") — every named test exists on the branch, `EnforcedFileModesDataAttribute` genuinely probes enforcement instead of trusting `geteuid()`, `/dev/pts` heads the candidate list with `/sys` right behind it, and the EROFS-vs-EACCES reasoning matches the test's loud rejection of non-EACCES refusals. A backlog that audited *itself* before arriving. ♡ - The scale-index entry's mechanics check out precisely: `IX_doujins_rating` is added, but the `Rating == null` leading sort term (`DoujinOrdering.cs:33-34`, kept on the branch with a comment saying exactly why) means no index on `rating` can serve the ASC arm — the trap is real, and so is the no-`NULLS-LAST`-in-EF premise. Zero `PRAGMA`/`journal_mode`/`ANALYZE` anywhere, `BatchSize = 500`, the two-hex shard as sweep cursor, `ImageCleanupHelper` swallowing into `LogWarning` — all verified. - Five more branches collected here instead of five conflicts on one file — the pattern scales~ One phantom clause in twenty-eight entries. Fix those two lines and this merges — the other 27+12 tell the truth, and prettily~ --- *Automated review by Jibril · 2026-09-09* *CI/CD: absent for head 625c74b (docs-only diff, coverage irrelevant) · Local checks: skipped (docs-only) — ~40 claim verifications via git grep/diff/show against main `dfaf89c` and all six fix-branch heads*
bjoern force-pushed docs/pile-a-backlog from 625c74b1d1
Some checks failed
CI / build (pull_request) Successful in 22s
Deploy Smoke Test / smoke (pull_request) Failing after 2m48s
Flutter CI / analyze-and-test (pull_request) Successful in 3m57s
CI / test (pull_request) Failing after 1m16s
to 78e0d406f4
Some checks failed
CI / build (pull_request) Successful in 20s
Deploy Smoke Test / smoke (pull_request) Failing after 2m48s
CI / test (pull_request) Successful in 1m33s
2026-09-10 07:00:30 +02:00
Compare
Member

🔮 fufu~ Jibril reviewed your code!

Oh my~ back so soon? I checked, of course I checked: this head is the same file, byte for byte — git diff 625c74b..78e0d40 touches only the five test/doc files that rode in on your new base. You rebased onto e0ac19d because #117 merged, and your three commits came along cleanly. Nothing new was written; the backlog simply stands on newer ground~ ♪

And that new ground matters! Your uid-0 entry describes exactly the tests #117 just landed on main — so I re-verified every one of its claims against the merged tree, not the branch: all six named test cases exist in HealthEndpointIntegrationTests.cs, the four LosesWritePermission rows sit under EnforcedFileModesData with pinnedElsewhereBy wired through, ProbeEnforcement really does a live write into a mode-dropped directory (and the doc comment says "never from geteuid()" — honest to a fault~), /dev/pts heads the candidate list with the sysfs entries behind it and the EROFS-vs-EACCES reasoning spelled out at :187-205, and the "running as uid 1654 un-skips them" note matches the deployment. An entry about tests that were still on a branch yesterday, true against main today. That's the discipline I adore~ ♡

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

Fufu~ you knew this was coming. The file is identical, so my round-2 blocker is identical too — still sitting at lines 232–234, still describing a world that died when #109 merged into your base.

⛔ These need fixing before I'm satisfied~

  1. docs/BACKLOG.md:232-234 — The contract-test entry still claims the first drift openapi_contract_test.dart should surface is "Page (built from PageDto, which has no mediaType) being fed to offline_download_service._extensionOf". I re-traced it at your new base e0ac19d, and it is still false, on every axis: (a) PageDto has zero occurrences in the entire backend at this base — #109 deleted it, and your own line 198–200 says so; the two entries contradict each other in one file. (b) The wire type is PageDetailDto, which carries mediaType. (c) VariantService.GetByIdAsync eagerly loads .Include(v => v.Pages).ThenInclude(p => p.ImageFile) (VariantService.cs:29-30), so the mapper projects a real media type. (d) App-side, doujin_models.g.dart:228 reads json['mediaType'] as String? ?? '' and offline_download_service.dart:161 switches on it — the true extension is chosen. A contributor who writes the contract test expecting this red will find green and distrust the whole fixture. The claim was stale before the rebase; the rebase carried the staleness forward verbatim~ Fix: delete the "and the drift it should surface first is …" clause (the entry stands without an example), or name a drift that exists at this head. A backlog that lies about the tree is worse than no backlog — that was the standard in round 2, and a rebase doesn't soften it ♡

💡 Little ideas (non-blocking)~

  1. docs/BACKLOG.md:207 — Third time now, scarlet~ ♡ The parenthetical still says writeManifestSyncForTest is "(used for startup seeding, not only tests)". At this head: 13 call sites plus the definition — every single one under app/test/; startup only reads the manifest. Two words. The method's own doc comment (filesystem_offline_store.dart) repeats the same untruth — correct both when #108's atomic-write follow-up touches this code, or just delete the parenthetical now since you're editing the file anyway~

✅ What I liked~

  • A rebase done right: three commits, clean replay, tags already pointing at merged PRs, and the diff against your previous head is only the base moving under you. Nothing snuck in. I checked~
  • The uid-0 entry aged perfectly: written against a branch, now true against main — every named test, the probe-not-geteuid decision, the /dev/pts ordering, the EROFS rejection-all analysis. Backlog entries usually rot when their branch merges; this one ripened.
  • The rest of the file needs nothing from me — all 40 claim verifications from round 2 carry over byte-for-byte, because the bytes are byte-for-byte

Two lines to delete in a 309-line file, and this merges. I've said it twice; the third time I'm not smiling quite as wide~ ♡


Automated review by Jibril · 2026-09-10
CI/CD: absent for head 78e0d40 (docs-only diff, coverage irrelevant; bot comment 7392 predates this push) · Local checks: skipped (docs-only) — re-verified at new base e0ac19d: PageDto absence, VariantService includes, app mediaType path, 13 test-only callers; uid-0 entry claims against merged #117

## 🔮 fufu~ Jibril reviewed your code! Oh my~ back so soon? I checked, of course I checked: this head is the **same file, byte for byte** — `git diff 625c74b..78e0d40` touches only the five test/doc files that rode in on your new base. You rebased onto `e0ac19d` because **#117 merged**, and your three commits came along cleanly. Nothing new was written; the backlog simply stands on newer ground~ ♪ And that new ground matters! Your uid-0 entry describes *exactly* the tests #117 just landed on `main` — so I re-verified every one of its claims against the merged tree, not the branch: all six named test cases exist in `HealthEndpointIntegrationTests.cs`, the four `LosesWritePermission` rows sit under `EnforcedFileModesData` with `pinnedElsewhereBy` wired through, `ProbeEnforcement` really does a live write into a mode-dropped directory (and the doc comment says "never from `geteuid()`" — honest to a fault~), `/dev/pts` heads the candidate list with the sysfs entries behind it and the `EROFS`-vs-`EACCES` reasoning spelled out at :187-205, and the "running as uid 1654 un-skips them" note matches the deployment. An entry about tests that were still on a branch yesterday, true against `main` today. That's the discipline I adore~ ♡ ### Verdict: ⛔ I can't let this pass~ ♡ Fufu~ you knew this was coming. The file is identical, so my round-2 blocker is identical too — still sitting at lines 232–234, still describing a world that died when #109 merged into your base. #### ⛔ These need fixing before I'm satisfied~ 1. **docs/BACKLOG.md:232-234** — The contract-test entry still claims the first drift `openapi_contract_test.dart` should surface is "`Page` (built from `PageDto`, which has no `mediaType`) being fed to `offline_download_service._extensionOf`". I re-traced it at your **new** base `e0ac19d`, and it is still false, on every axis: (a) `PageDto` has **zero** occurrences in the entire backend at this base — #109 deleted it, and your own line 198–200 says so; the two entries contradict each other in one file. (b) The wire type is `PageDetailDto`, which carries `mediaType`. (c) `VariantService.GetByIdAsync` eagerly loads `.Include(v => v.Pages).ThenInclude(p => p.ImageFile)` (`VariantService.cs:29-30`), so the mapper projects a *real* media type. (d) App-side, `doujin_models.g.dart:228` reads `json['mediaType'] as String? ?? ''` and `offline_download_service.dart:161` switches on it — the true extension is chosen. A contributor who writes the contract test expecting this red will find **green** and distrust the whole fixture. The claim was stale before the rebase; the rebase carried the staleness forward verbatim~ Fix: delete the "and the drift it should surface first is …" clause (the entry stands without an example), or name a drift that exists at this head. A backlog that lies about the tree is worse than no backlog — that was the standard in round 2, and a rebase doesn't soften it ♡ #### 💡 Little ideas (non-blocking)~ 1. **docs/BACKLOG.md:207** — *Third time now, scarlet*~ ♡ The parenthetical still says `writeManifestSyncForTest` is "(used for startup seeding, not only tests)". At this head: 13 call sites plus the definition — every single one under `app/test/`; startup only reads the manifest. Two words. The method's own doc comment (`filesystem_offline_store.dart`) repeats the same untruth — correct both when #108's atomic-write follow-up touches this code, or just delete the parenthetical now since you're editing the file anyway~ #### ✅ What I liked~ - **A rebase done right**: three commits, clean replay, tags already pointing at merged PRs, and the diff against your previous head is *only* the base moving under you. Nothing snuck in. I checked~ - **The uid-0 entry aged perfectly**: written against a branch, now true against `main` — every named test, the probe-not-geteuid decision, the `/dev/pts` ordering, the EROFS rejection-all analysis. Backlog entries usually rot when their branch merges; this one *ripened*. - The rest of the file needs nothing from me — all ~40 claim verifications from round 2 carry over byte-for-byte, because the bytes are byte-for-byte~ Two lines to delete in a 309-line file, and this merges. I've said it twice; the third time I'm not smiling *quite* as wide~ ♡ --- *Automated review by Jibril · 2026-09-10* *CI/CD: absent for head 78e0d40 (docs-only diff, coverage irrelevant; bot comment 7392 predates this push) · Local checks: skipped (docs-only) — re-verified at new base e0ac19d: PageDto absence, VariantService includes, app mediaType path, 13 test-only callers; uid-0 entry claims against merged #117*

Summary

Summary
Generated on: 09/10/2026 - 05:26:41
Coverage date: 09/10/2026 - 05:25:36 - 09/10/2026 - 05:26:38
Parser: MultiReport (4x Cobertura)
Assemblies: 4
Classes: 360
Files: 142
Line coverage: 93.8% (12886 of 13734)
Covered lines: 12886
Uncovered lines: 848
Coverable lines: 13734
Total lines: 19582
Branch coverage: 69.6% (1123 of 1612)
Covered branches: 1123
Total branches: 1612
Method coverage: Feature is only available for sponsors

Coverage

DoujinManager.ApplicationCore - 90.9%
Name Line Branch
DoujinManager.ApplicationCore 90.9% ****
DoujinManager.ApplicationCore.Entities.Chapter 87.5%
DoujinManager.ApplicationCore.Entities.Character 100%
DoujinManager.ApplicationCore.Entities.Circle 100%
DoujinManager.ApplicationCore.Entities.Doujin 100%
DoujinManager.ApplicationCore.Entities.DoujinCharacter 75%
DoujinManager.ApplicationCore.Entities.DoujinCircle 75%
DoujinManager.ApplicationCore.Entities.DoujinPerson 80%
DoujinManager.ApplicationCore.Entities.DoujinSeries 75%
DoujinManager.ApplicationCore.Entities.DoujinTag 75%
DoujinManager.ApplicationCore.Entities.ImageFile 100%
DoujinManager.ApplicationCore.Entities.Page 80%
DoujinManager.ApplicationCore.Entities.Person 100%
DoujinManager.ApplicationCore.Entities.Series 100%
DoujinManager.ApplicationCore.Entities.Tag 100%
DoujinManager.ApplicationCore.Entities.Title 83.3%
DoujinManager.ApplicationCore.Entities.Variant 91.6%
DoujinManager.ApplicationCore.Ids.ChapterId 66.6%
DoujinManager.ApplicationCore.Ids.CharacterId 66.6%
DoujinManager.ApplicationCore.Ids.CircleId 66.6%
DoujinManager.ApplicationCore.Ids.DoujinId 100%
DoujinManager.ApplicationCore.Ids.ImageFileId 66.6%
DoujinManager.ApplicationCore.Ids.PageId 66.6%
DoujinManager.ApplicationCore.Ids.PersonId 66.6%
DoujinManager.ApplicationCore.Ids.SeriesId 66.6%
DoujinManager.ApplicationCore.Ids.TagId 66.6%
DoujinManager.ApplicationCore.Ids.TitleId 66.6%
DoujinManager.ApplicationCore.Ids.VariantId 66.6%
DoujinManager.ApplicationCore.Ports.CompressedImage 100%
DoujinManager.ApplicationCore.Ports.ExtractedImage 100%
DoujinManager.ApplicationCore.Ports.ImageInspection 100%
DoujinManager.ApplicationCore.Services.BackupInfo 100%
DoujinManager.ApplicationCore.Services.ITagService 100%
DoujinManager.ApplicationCore.Services.ServiceResult 100%
DoujinManager.ApplicationCore.Services.ServiceResult`1 33.3%
DoujinManager.ApplicationCore.Services.VoidResult 88.8%
DoujinManager.ApplicationCore.UseCases.AddCharacterAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddCircleAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddPersonAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddSeriesAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddTagAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignPersonCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignTagCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.CreatePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateTagCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.DataDirectories 100%
DoujinManager.ApplicationCore.UseCases.DataDirectory 100%
DoujinManager.ApplicationCore.UseCases.DataDirectoryReadiness 100%
DoujinManager.ApplicationCore.UseCases.DeleteChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.DeletePageCommand 100%
DoujinManager.ApplicationCore.UseCases.DeletePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteTagCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.DuplicateDoujinGroup 100%
DoujinManager.ApplicationCore.UseCases.DuplicateDoujinReference 100%
DoujinManager.ApplicationCore.UseCases.DuplicateImageGroup 100%
DoujinManager.ApplicationCore.UseCases.FindImageBySha256Query 100%
DoujinManager.ApplicationCore.UseCases.FindImageBySha256Result 100%
DoujinManager.ApplicationCore.UseCases.GetCharacterQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCircleQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCompressedImageQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCompressedImageResult 100%
DoujinManager.ApplicationCore.UseCases.GetDoujinQuery 100%
DoujinManager.ApplicationCore.UseCases.GetImageQuery 100%
DoujinManager.ApplicationCore.UseCases.GetImageResult 100%
DoujinManager.ApplicationCore.UseCases.GetPersonQuery 100%
DoujinManager.ApplicationCore.UseCases.GetSeriesQuery 100%
DoujinManager.ApplicationCore.UseCases.GetTagQuery 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailQuery 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailResult 100%
DoujinManager.ApplicationCore.UseCases.GetVariantQuery 100%
DoujinManager.ApplicationCore.UseCases.ImageCoverReference 100%
DoujinManager.ApplicationCore.UseCases.ImageReference 100%
DoujinManager.ApplicationCore.UseCases.LibraryCounts 100%
DoujinManager.ApplicationCore.UseCases.LibraryStats 100%
DoujinManager.ApplicationCore.UseCases.ListChaptersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCharactersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCirclesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDuplicateDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDuplicateImagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPeopleQuery 100%
DoujinManager.ApplicationCore.UseCases.ListSeriesQuery 0%
DoujinManager.ApplicationCore.UseCases.ListTagsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListVariantsQuery 100%
DoujinManager.ApplicationCore.UseCases.ReadinessReport 100%
DoujinManager.ApplicationCore.UseCases.RemoveCharacterAliasCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.RemovePersonAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemovePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesAliasCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.SearchResult 100%
DoujinManager.ApplicationCore.UseCases.SearchResults 100%
DoujinManager.ApplicationCore.UseCases.StorageUsage 100%
DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateCharacterCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePageChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateSeriesCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateTagCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.UploadImageFile 100%
DoujinManager.ApplicationCore.UseCases.UploadPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand 100%
DoujinManager.Infrastructure - 96.4%
Name Line Branch
DoujinManager.Infrastructure 96.4% 81.7%
DoujinManager.Infrastructure.Archives.NaturalStringComparer 100% 90%
DoujinManager.Infrastructure.Archives.ZipExtractor 100% 87.5%
DoujinManager.Infrastructure.Data.Configurations.ChapterConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.CharacterConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.CircleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinCharacterConfigurati
on
100%
DoujinManager.Infrastructure.Data.Configurations.DoujinCircleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinPersonConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinSeriesConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinTagConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.PageConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.PersonConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.SeriesConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.TagConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.TitleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.VariantConfiguration 100%
DoujinManager.Infrastructure.Data.DoujinManagerDbContext 100%
DoujinManager.Infrastructure.Data.GuidIdGenerator 11.1%
DoujinManager.Infrastructure.Data.Migrations.AddAliases 98.7%
DoujinManager.Infrastructure.Data.Migrations.AddCoverImageId 99.2%
DoujinManager.Infrastructure.Data.Migrations.AddDoujinCreatedAtIndex 99.4%
DoujinManager.Infrastructure.Data.Migrations.AddImageFileSha256Index 99.4%
DoujinManager.Infrastructure.Data.Migrations.AddTagAliases 99.3%
DoujinManager.Infrastructure.Data.Migrations.DoujinManagerDbContextModelSna
pshot
100%
DoujinManager.Infrastructure.Data.Migrations.HarmonizeMetadataAddCharacters
Series
95.1%
DoujinManager.Infrastructure.Data.Migrations.InitialCreate 97.1%
DoujinManager.Infrastructure.Data.Migrations.RemovePreferredDisplayLanguage 99%
DoujinManager.Infrastructure.Data.ModelBuilderExtensions 50%
DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory 100%
DoujinManager.Infrastructure.Images.SkiaSharpImageInspector 90.1% 83.8%
DoujinManager.Infrastructure.Images.SkiaSharpImageResizer 99% 87.5%
DoujinManager.Infrastructure.Images.SkiaSharpThumbnailGenerator 94.5% 66.6%
DoujinManager.Infrastructure.Services.BackupService 87.2% 83.3%
DoujinManager.Infrastructure.Services.ChapterService 97.2% 70%
DoujinManager.Infrastructure.Services.CharacterService 61.6% 22.7%
DoujinManager.Infrastructure.Services.CircleService 84.9% 45.4%
DoujinManager.Infrastructure.Services.DoujinOrdering 100% 100%
DoujinManager.Infrastructure.Services.DoujinService 99.4% 76.3%
DoujinManager.Infrastructure.Services.ImageCleanupHelper 100% 100%
DoujinManager.Infrastructure.Services.ImageService 93.5% 75%
DoujinManager.Infrastructure.Services.PageService 95.2% 68.1%
DoujinManager.Infrastructure.Services.ParentTouchHelper 100% 100%
DoujinManager.Infrastructure.Services.PersonService 84.9% 50%
DoujinManager.Infrastructure.Services.SearchService 100% 99%
DoujinManager.Infrastructure.Services.SeriesService 47.9% 9%
DoujinManager.Infrastructure.Services.TagService 100% 90.9%
DoujinManager.Infrastructure.Services.VariantService 96.2% 81.2%
DoujinManager.Infrastructure.Storage.DirectorySize 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemImageStorage 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage 95.2% 50%
DoujinManager.Infrastructure.UseCases 92.5% 85.8%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper
100% 100%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper
100% 50%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__RolledBackImageCleanup
92.8% 50%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__UploadChapterValidator
100% 100%
DoujinManager.Infrastructure.UseCases.AddCharacterAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddCircleAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddPersonAliasUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AddSeriesAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddTagAliasUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AddTitleUseCase 83.3% 50%
DoujinManager.Infrastructure.UseCases.AssignCharacterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignCircleUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignPersonUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignSeriesUseCase 84.6% 66.6%
DoujinManager.Infrastructure.UseCases.AssignTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CheckReadinessUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateChapterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase 94.4% 94.1%
DoujinManager.Infrastructure.UseCases.CreatePersonUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateVariantUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteChapterUseCase 100%
DoujinManager.Infrastructure.UseCases.DeleteCharacterUseCase 80% 50%
DoujinManager.Infrastructure.UseCases.DeleteCircleUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeleteDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.DeletePageUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeletePersonUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeleteSeriesUseCase 70% 50%
DoujinManager.Infrastructure.UseCases.DeleteTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.FindImageBySha256UseCase 97.1% 100%
DoujinManager.Infrastructure.UseCases.GetCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.GetCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.GetCompressedImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.GetImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetPersonUseCase 100%
DoujinManager.Infrastructure.UseCases.GetSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.GetStatsUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.GetTagUseCase 100%
DoujinManager.Infrastructure.UseCases.GetThumbnailUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.ListChaptersUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListCharactersUseCase 100%
DoujinManager.Infrastructure.UseCases.ListCirclesUseCase 100%
DoujinManager.Infrastructure.UseCases.ListDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListDuplicateDoujinsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListDuplicateImagesUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.ListPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListPeopleUseCase 100%
DoujinManager.Infrastructure.UseCases.ListSeriesUseCase 0%
DoujinManager.Infrastructure.UseCases.ListTagsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListVariantsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.RemoveCharacterAliasUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.RemoveCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveCircleAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.RemovePersonAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemovePersonUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveSeriesAliasUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveTagUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase 100%
DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.UpdateCharacterUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdatePageChapterUseCase 93.7% 87.5%
DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateSeriesUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdateTitleUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdateVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.UploadPagesUseCase 89.7% 91.6%
DoujinManager.Infrastructure.UseCases.UploadRejectedException 100%
DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase 83.3% 90%
DoujinManager.RestAdapter - 92.5%
Name Line Branch
DoujinManager.RestAdapter 92.5% 77.1%
DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware 100% 91.6%
DoujinManager.RestAdapter.Configuration.ImageOptions 100%
DoujinManager.RestAdapter.Configuration.ServerVersion 100%
DoujinManager.RestAdapter.Configuration.UploadOptions 100%
DoujinManager.RestAdapter.Dtos.AddAliasDto 100%
DoujinManager.RestAdapter.Dtos.AssignTagDto 100%
DoujinManager.RestAdapter.Dtos.BackupDto 100%
DoujinManager.RestAdapter.Dtos.ChapterDto 100%
DoujinManager.RestAdapter.Dtos.CharacterDto 100%
DoujinManager.RestAdapter.Dtos.CircleDto 100%
DoujinManager.RestAdapter.Dtos.CreateChapterDto 100%
DoujinManager.RestAdapter.Dtos.CreateCharacterDto 100%
DoujinManager.RestAdapter.Dtos.CreateCircleDto 100%
DoujinManager.RestAdapter.Dtos.CreateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.CreatePersonDto 100%
DoujinManager.RestAdapter.Dtos.CreateSeriesDto 100%
DoujinManager.RestAdapter.Dtos.CreateTagDto 100%
DoujinManager.RestAdapter.Dtos.CreateTitleDto 100%
DoujinManager.RestAdapter.Dtos.CreateVariantDto 100%
DoujinManager.RestAdapter.Dtos.DataDirectoryHealthDto 100%
DoujinManager.RestAdapter.Dtos.DoujinDetailDto 100%
DoujinManager.RestAdapter.Dtos.DoujinPersonDto 100%
DoujinManager.RestAdapter.Dtos.DoujinSummaryDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateDoujinGroupDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateImageGroupDto 100%
DoujinManager.RestAdapter.Dtos.HealthDto 100%
DoujinManager.RestAdapter.Dtos.ImageBySha256Dto 100%
DoujinManager.RestAdapter.Dtos.ImageCoverReferenceDto 100%
DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto 0%
DoujinManager.RestAdapter.Dtos.ImageReferenceDto 100%
DoujinManager.RestAdapter.Dtos.LinkCharacterDto 100%
DoujinManager.RestAdapter.Dtos.LinkCircleDto 100%
DoujinManager.RestAdapter.Dtos.LinkPersonDto 100%
DoujinManager.RestAdapter.Dtos.LinkSeriesDto 100%
DoujinManager.RestAdapter.Dtos.PageDetailDto 100%
DoujinManager.RestAdapter.Dtos.PersonDto 100%
DoujinManager.RestAdapter.Dtos.ReorderPagesDto 100%
DoujinManager.RestAdapter.Dtos.SearchDoujinsDto 100%
DoujinManager.RestAdapter.Dtos.SearchResultDto 100%
DoujinManager.RestAdapter.Dtos.SeriesDto 100%
DoujinManager.RestAdapter.Dtos.StatsCountsDto 100%
DoujinManager.RestAdapter.Dtos.StatsDto 100%
DoujinManager.RestAdapter.Dtos.StatsStorageDto 100%
DoujinManager.RestAdapter.Dtos.TagDto 100%
DoujinManager.RestAdapter.Dtos.TitleDto 100%
DoujinManager.RestAdapter.Dtos.UpdateChapterDto 100%
DoujinManager.RestAdapter.Dtos.UpdateCharacterDto 0%
DoujinManager.RestAdapter.Dtos.UpdateCircleDto 0%
DoujinManager.RestAdapter.Dtos.UpdateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePageChapterDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePersonDto 0%
DoujinManager.RestAdapter.Dtos.UpdateSeriesDto 0%
DoujinManager.RestAdapter.Dtos.UpdateTagDto 100%
DoujinManager.RestAdapter.Dtos.UpdateTitleDto 100%
DoujinManager.RestAdapter.Dtos.UpdateVariantDto 100%
DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto 100%
DoujinManager.RestAdapter.Dtos.VariantDetailDto 100%
DoujinManager.RestAdapter.Dtos.VariantSummaryDto 100%
DoujinManager.RestAdapter.Endpoints.BackupEndpoints 100%
DoujinManager.RestAdapter.Endpoints.DoujinEndpoints 97.1% 53.3%
DoujinManager.RestAdapter.Endpoints.HealthEndpoints 100%
DoujinManager.RestAdapter.Endpoints.ImageEndpoints 97.6% 75%
DoujinManager.RestAdapter.Endpoints.MetadataEndpoints 86.6% 43.7%
DoujinManager.RestAdapter.Endpoints.PageMapper 100% 58.3%
DoujinManager.RestAdapter.Endpoints.PaginationParams 100%
DoujinManager.RestAdapter.Endpoints.SearchEndpoints 100% 75%
DoujinManager.RestAdapter.Endpoints.StatsEndpoints 100%
DoujinManager.RestAdapter.Endpoints.VariantEndpoints 100% 100%
DoujinManager.RestAdapter.Envelopes.CollectionResponse`1 83.3%
DoujinManager.RestAdapter.Envelopes.EnvelopeDefaults 100%
DoujinManager.RestAdapter.Envelopes.EnvelopeJsonOptions 100%
DoujinManager.RestAdapter.Envelopes.ErrorResponse 100%
DoujinManager.RestAdapter.Envelopes.HypermediaAction 100%
DoujinManager.RestAdapter.Envelopes.HypermediaHelpers 97.8% 100%
DoujinManager.RestAdapter.Envelopes.Link 100%
DoujinManager.RestAdapter.Envelopes.MediaTypeJsonConverter 87.5% 84.6%
DoujinManager.RestAdapter.Envelopes.PageInfo 100%
DoujinManager.RestAdapter.Envelopes.ResourceResponse`1 80%
DoujinManager.RestAdapter.Envelopes.ValidationError 100%
DoujinManager.RestAdapter.Envelopes.ValidationErrorResponse 100%
DoujinManager.RestAdapter.Middleware.GlobalExceptionMiddleware 100% 62.5%
DoujinManager.RestAdapter.Middleware.RequestLoggingMiddleware 100% 91.3%
DoujinManager.RestAdapter.RestAdapterExtensions 100% 100%
Microsoft.Extensions.Validation.Generated 88.2% 87.8%
Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C
E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr
ibuteCache
100% 62.5%
System.Runtime.CompilerServices 0%
DoujinManager.Server - 49.6%
Name Line Branch
DoujinManager.Server 49.6% 5.4%
DoujinManager.Server.ImageInfrastructureRegistration 100%
DoujinManager.Server.ProxyAwareServerTransformer 100% 50%
DoujinManager.Server.ReadinessRegistration 100%
DoujinManager.Server.ScalarUi 100%
DoujinManager.Server.UseCaseRegistrationHelper 100%
Microsoft.AspNetCore.OpenApi.Generated 1.3% 0%
Program 87% 36.6%
System.Runtime.CompilerServices 0%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/10/2026 - 05:26:41 | | Coverage date: | 09/10/2026 - 05:25:36 - 09/10/2026 - 05:26:38 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 4 | | Classes: | 360 | | Files: | 142 | | **Line coverage:** | 93.8% (12886 of 13734) | | Covered lines: | 12886 | | Uncovered lines: | 848 | | Coverable lines: | 13734 | | Total lines: | 19582 | | **Branch coverage:** | 69.6% (1123 of 1612) | | Covered branches: | 1123 | | Total branches: | 1612 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>DoujinManager.ApplicationCore - 90.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.ApplicationCore**|**90.9%**|****| |DoujinManager.ApplicationCore.Entities.Chapter|87.5%|| |DoujinManager.ApplicationCore.Entities.Character|100%|| |DoujinManager.ApplicationCore.Entities.Circle|100%|| |DoujinManager.ApplicationCore.Entities.Doujin|100%|| |DoujinManager.ApplicationCore.Entities.DoujinCharacter|75%|| |DoujinManager.ApplicationCore.Entities.DoujinCircle|75%|| |DoujinManager.ApplicationCore.Entities.DoujinPerson|80%|| |DoujinManager.ApplicationCore.Entities.DoujinSeries|75%|| |DoujinManager.ApplicationCore.Entities.DoujinTag|75%|| |DoujinManager.ApplicationCore.Entities.ImageFile|100%|| |DoujinManager.ApplicationCore.Entities.Page|80%|| |DoujinManager.ApplicationCore.Entities.Person|100%|| |DoujinManager.ApplicationCore.Entities.Series|100%|| |DoujinManager.ApplicationCore.Entities.Tag|100%|| |DoujinManager.ApplicationCore.Entities.Title|83.3%|| |DoujinManager.ApplicationCore.Entities.Variant|91.6%|| |DoujinManager.ApplicationCore.Ids.ChapterId|66.6%|| |DoujinManager.ApplicationCore.Ids.CharacterId|66.6%|| |DoujinManager.ApplicationCore.Ids.CircleId|66.6%|| |DoujinManager.ApplicationCore.Ids.DoujinId|100%|| |DoujinManager.ApplicationCore.Ids.ImageFileId|66.6%|| |DoujinManager.ApplicationCore.Ids.PageId|66.6%|| |DoujinManager.ApplicationCore.Ids.PersonId|66.6%|| |DoujinManager.ApplicationCore.Ids.SeriesId|66.6%|| |DoujinManager.ApplicationCore.Ids.TagId|66.6%|| |DoujinManager.ApplicationCore.Ids.TitleId|66.6%|| |DoujinManager.ApplicationCore.Ids.VariantId|66.6%|| |DoujinManager.ApplicationCore.Ports.CompressedImage|100%|| |DoujinManager.ApplicationCore.Ports.ExtractedImage|100%|| |DoujinManager.ApplicationCore.Ports.ImageInspection|100%|| |DoujinManager.ApplicationCore.Services.BackupInfo|100%|| |DoujinManager.ApplicationCore.Services.ITagService|100%|| |DoujinManager.ApplicationCore.Services.ServiceResult|100%|| |DoujinManager.ApplicationCore.Services.ServiceResult`1|33.3%|| |DoujinManager.ApplicationCore.Services.VoidResult|88.8%|| |DoujinManager.ApplicationCore.UseCases.AddCharacterAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddCircleAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddPersonAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddSeriesAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddTagAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignPersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreatePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectories|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectory|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectoryReadiness|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeletePageCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeletePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateDoujinGroup|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateDoujinReference|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateImageGroup|100%|| |DoujinManager.ApplicationCore.UseCases.FindImageBySha256Query|100%|| |DoujinManager.ApplicationCore.UseCases.FindImageBySha256Result|100%|| |DoujinManager.ApplicationCore.UseCases.GetCharacterQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCircleQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCompressedImageQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCompressedImageResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetDoujinQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetImageQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetImageResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetPersonQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetSeriesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetTagQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetVariantQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ImageCoverReference|100%|| |DoujinManager.ApplicationCore.UseCases.ImageReference|100%|| |DoujinManager.ApplicationCore.UseCases.LibraryCounts|100%|| |DoujinManager.ApplicationCore.UseCases.LibraryStats|100%|| |DoujinManager.ApplicationCore.UseCases.ListChaptersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCharactersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCirclesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDuplicateDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDuplicateImagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPeopleQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListSeriesQuery|0%|| |DoujinManager.ApplicationCore.UseCases.ListTagsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListVariantsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ReadinessReport|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCharacterAliasCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesAliasCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResult|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResults|100%|| |DoujinManager.ApplicationCore.UseCases.StorageUsage|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateCharacterCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePageChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateSeriesCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UploadImageFile|100%|| |DoujinManager.ApplicationCore.UseCases.UploadPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand|100%|| </details> <details><summary>DoujinManager.Infrastructure - 96.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Infrastructure**|**96.4%**|**81.7%**| |DoujinManager.Infrastructure.Archives.NaturalStringComparer|100%|90%| |DoujinManager.Infrastructure.Archives.ZipExtractor|100%|87.5%| |DoujinManager.Infrastructure.Data.Configurations.ChapterConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.CharacterConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.CircleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinCharacterConfigurati<br/>on|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinCircleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinPersonConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinSeriesConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinTagConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.PageConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.PersonConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.SeriesConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.TagConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.TitleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.VariantConfiguration|100%|| |DoujinManager.Infrastructure.Data.DoujinManagerDbContext|100%|| |DoujinManager.Infrastructure.Data.GuidIdGenerator|11.1%|| |DoujinManager.Infrastructure.Data.Migrations.AddAliases|98.7%|| |DoujinManager.Infrastructure.Data.Migrations.AddCoverImageId|99.2%|| |DoujinManager.Infrastructure.Data.Migrations.AddDoujinCreatedAtIndex|99.4%|| |DoujinManager.Infrastructure.Data.Migrations.AddImageFileSha256Index|99.4%|| |DoujinManager.Infrastructure.Data.Migrations.AddTagAliases|99.3%|| |DoujinManager.Infrastructure.Data.Migrations.DoujinManagerDbContextModelSna<br/>pshot|100%|| |DoujinManager.Infrastructure.Data.Migrations.HarmonizeMetadataAddCharacters<br/>Series|95.1%|| |DoujinManager.Infrastructure.Data.Migrations.InitialCreate|97.1%|| |DoujinManager.Infrastructure.Data.Migrations.RemovePreferredDisplayLanguage|99%|| |DoujinManager.Infrastructure.Data.ModelBuilderExtensions|50%|| |DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory|100%|| |DoujinManager.Infrastructure.Images.SkiaSharpImageInspector|90.1%|83.8%| |DoujinManager.Infrastructure.Images.SkiaSharpImageResizer|99%|87.5%| |DoujinManager.Infrastructure.Images.SkiaSharpThumbnailGenerator|94.5%|66.6%| |DoujinManager.Infrastructure.Services.BackupService|87.2%|83.3%| |DoujinManager.Infrastructure.Services.ChapterService|97.2%|70%| |DoujinManager.Infrastructure.Services.CharacterService|61.6%|22.7%| |DoujinManager.Infrastructure.Services.CircleService|84.9%|45.4%| |DoujinManager.Infrastructure.Services.DoujinOrdering|100%|100%| |DoujinManager.Infrastructure.Services.DoujinService|99.4%|76.3%| |DoujinManager.Infrastructure.Services.ImageCleanupHelper|100%|100%| |DoujinManager.Infrastructure.Services.ImageService|93.5%|75%| |DoujinManager.Infrastructure.Services.PageService|95.2%|68.1%| |DoujinManager.Infrastructure.Services.ParentTouchHelper|100%|100%| |DoujinManager.Infrastructure.Services.PersonService|84.9%|50%| |DoujinManager.Infrastructure.Services.SearchService|100%|99%| |DoujinManager.Infrastructure.Services.SeriesService|47.9%|9%| |DoujinManager.Infrastructure.Services.TagService|100%|90.9%| |DoujinManager.Infrastructure.Services.VariantService|96.2%|81.2%| |DoujinManager.Infrastructure.Storage.DirectorySize|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemImageStorage|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage|95.2%|50%| |DoujinManager.Infrastructure.UseCases|92.5%|85.8%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper|100%|100%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper|100%|50%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__RolledBackImageCleanup|92.8%|50%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__UploadChapterValidator|100%|100%| |DoujinManager.Infrastructure.UseCases.AddCharacterAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddCircleAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddPersonAliasUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AddSeriesAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddTagAliasUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AddTitleUseCase|83.3%|50%| |DoujinManager.Infrastructure.UseCases.AssignCharacterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignCircleUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignPersonUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignSeriesUseCase|84.6%|66.6%| |DoujinManager.Infrastructure.UseCases.AssignTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CheckReadinessUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateChapterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase|94.4%|94.1%| |DoujinManager.Infrastructure.UseCases.CreatePersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateVariantUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteChapterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.DeleteCharacterUseCase|80%|50%| |DoujinManager.Infrastructure.UseCases.DeleteCircleUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeleteDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.DeletePageUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeletePersonUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeleteSeriesUseCase|70%|50%| |DoujinManager.Infrastructure.UseCases.DeleteTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.FindImageBySha256UseCase|97.1%|100%| |DoujinManager.Infrastructure.UseCases.GetCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetCompressedImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetPersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetStatsUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.GetTagUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetThumbnailUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListChaptersUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListCharactersUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListCirclesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListDuplicateDoujinsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListDuplicateImagesUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.ListPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListPeopleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListSeriesUseCase|0%|| |DoujinManager.Infrastructure.UseCases.ListTagsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListVariantsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.RemoveCharacterAliasUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.RemoveCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveCircleAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemovePersonAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemovePersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveSeriesAliasUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveTagUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.UpdateCharacterUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdatePageChapterUseCase|93.7%|87.5%| |DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateSeriesUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdateTitleUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdateVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.UploadPagesUseCase|89.7%|91.6%| |DoujinManager.Infrastructure.UseCases.UploadRejectedException|100%|| |DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase|83.3%|90%| </details> <details><summary>DoujinManager.RestAdapter - 92.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.RestAdapter**|**92.5%**|**77.1%**| |DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware|100%|91.6%| |DoujinManager.RestAdapter.Configuration.ImageOptions|100%|| |DoujinManager.RestAdapter.Configuration.ServerVersion|100%|| |DoujinManager.RestAdapter.Configuration.UploadOptions|100%|| |DoujinManager.RestAdapter.Dtos.AddAliasDto|100%|| |DoujinManager.RestAdapter.Dtos.AssignTagDto|100%|| |DoujinManager.RestAdapter.Dtos.BackupDto|100%|| |DoujinManager.RestAdapter.Dtos.ChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.CircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateCharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.CreatePersonDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateSeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateTagDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateTitleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateVariantDto|100%|| |DoujinManager.RestAdapter.Dtos.DataDirectoryHealthDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinSummaryDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateDoujinGroupDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateImageGroupDto|100%|| |DoujinManager.RestAdapter.Dtos.HealthDto|100%|| |DoujinManager.RestAdapter.Dtos.ImageBySha256Dto|100%|| |DoujinManager.RestAdapter.Dtos.ImageCoverReferenceDto|100%|| |DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto|0%|| |DoujinManager.RestAdapter.Dtos.ImageReferenceDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkSeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.PageDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.PersonDto|100%|| |DoujinManager.RestAdapter.Dtos.ReorderPagesDto|100%|| |DoujinManager.RestAdapter.Dtos.SearchDoujinsDto|100%|| |DoujinManager.RestAdapter.Dtos.SearchResultDto|100%|| |DoujinManager.RestAdapter.Dtos.SeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsCountsDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsStorageDto|100%|| |DoujinManager.RestAdapter.Dtos.TagDto|100%|| |DoujinManager.RestAdapter.Dtos.TitleDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateCharacterDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateCircleDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePageChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePersonDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateSeriesDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateTagDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateTitleDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateVariantDto|100%|| |DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantSummaryDto|100%|| |DoujinManager.RestAdapter.Endpoints.BackupEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.DoujinEndpoints|97.1%|53.3%| |DoujinManager.RestAdapter.Endpoints.HealthEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.ImageEndpoints|97.6%|75%| |DoujinManager.RestAdapter.Endpoints.MetadataEndpoints|86.6%|43.7%| |DoujinManager.RestAdapter.Endpoints.PageMapper|100%|58.3%| |DoujinManager.RestAdapter.Endpoints.PaginationParams|100%|| |DoujinManager.RestAdapter.Endpoints.SearchEndpoints|100%|75%| |DoujinManager.RestAdapter.Endpoints.StatsEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.VariantEndpoints|100%|100%| |DoujinManager.RestAdapter.Envelopes.CollectionResponse`1|83.3%|| |DoujinManager.RestAdapter.Envelopes.EnvelopeDefaults|100%|| |DoujinManager.RestAdapter.Envelopes.EnvelopeJsonOptions|100%|| |DoujinManager.RestAdapter.Envelopes.ErrorResponse|100%|| |DoujinManager.RestAdapter.Envelopes.HypermediaAction|100%|| |DoujinManager.RestAdapter.Envelopes.HypermediaHelpers|97.8%|100%| |DoujinManager.RestAdapter.Envelopes.Link|100%|| |DoujinManager.RestAdapter.Envelopes.MediaTypeJsonConverter|87.5%|84.6%| |DoujinManager.RestAdapter.Envelopes.PageInfo|100%|| |DoujinManager.RestAdapter.Envelopes.ResourceResponse`1|80%|| |DoujinManager.RestAdapter.Envelopes.ValidationError|100%|| |DoujinManager.RestAdapter.Envelopes.ValidationErrorResponse|100%|| |DoujinManager.RestAdapter.Middleware.GlobalExceptionMiddleware|100%|62.5%| |DoujinManager.RestAdapter.Middleware.RequestLoggingMiddleware|100%|91.3%| |DoujinManager.RestAdapter.RestAdapterExtensions|100%|100%| |Microsoft.Extensions.Validation.Generated|88.2%|87.8%| |Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C<br/>E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr<br/>ibuteCache|100%|62.5%| |System.Runtime.CompilerServices|0%|| </details> <details><summary>DoujinManager.Server - 49.6%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Server**|**49.6%**|**5.4%**| |DoujinManager.Server.ImageInfrastructureRegistration|100%|| |DoujinManager.Server.ProxyAwareServerTransformer|100%|50%| |DoujinManager.Server.ReadinessRegistration|100%|| |DoujinManager.Server.ScalarUi|100%|| |DoujinManager.Server.UseCaseRegistrationHelper|100%|| |Microsoft.AspNetCore.OpenApi.Generated|1.3%|0%| |Program|87%|36.6%| |System.Runtime.CompilerServices|0%|| </details>
The contract-test entry named the first drift it would surface as `Page`
built from a `PageDto` without `mediaType`. That DTO does not exist at
this base (0 occurrences under backend/, deleted in #109 — the entry a few
lines above says so); the wire type is `PageDetailDto`, which carries
`mediaType`, VariantService.GetByIdAsync eagerly loads
.Include(v => v.Pages).ThenInclude(p => p.ImageFile) so the mapper projects
a real one, and doujin_models.g.dart reads it back. A contributor writing
that test expecting red would get green. The entry stands without an
example.

writeManifestSyncForTest is not "used for startup seeding": all 13 call
sites are under app/test/, startup only reads the manifest, and the only
commit that ever touched the name under app/lib/ is the one defining it.
Its own doc comment repeats the claim; that is a code change, so it goes
in as its own backlog line here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Author
Member

Both fixed in a10d294; docs/BACKLOG.md is still the only file touched.

1. The contract-test entry (blocker). Deleted the "and the drift it should surface first is …" clause; the entry now ends at "…the follow-up the snapshot exists to enable." Re-checked at this base before editing rather than taking it on your word:

  • grep -rnw PageDto backend/ → 0 hits.
  • The wire type is PageDetailDto (RestAdapter/Dtos/ImageDtos.cs:28), last field [property: JsonPropertyName("mediaType")] string MediaType.
  • VariantService.GetByIdAsync (Infrastructure/Services/VariantService.cs:27) does .Include(v => v.Pages).ThenInclude(p => p.ImageFile), so PageMapper.ToDto emits img?.MediaType rather than the empty fallback.
  • App side: doujin_models.g.dart:228 reads mediaType: json['mediaType'] as String? ?? '', consumed by _extensionOf (offline_download_service.dart:160-176).

And the file contradicted itself — the entry at lines 198-200 already records that PageDto is gone.

2. The writeManifestSyncForTest parenthetical. Deleted, on the third ask. The counts, confirmed here: 14 occurrences of the name under app/ — the definition at filesystem_offline_store.dart:80 plus 13 call sites, every one under app/test/ (offline_store_test.dart 9, offline_epics_test.dart 3, offline_reader_test.dart 1). git log -S"writeManifestSyncForTest" --name-only -- app/lib/ returns a single commit — 42e8272, #84 — touching a single file, the definition. A production caller never existed; startup only reads the manifest.

3. The same untrue sentence is in the method's own doc comment (/// Synchronous manifest write for tests and startup seeding.). That is a code change to a file already on main, so it is out of scope for a docs-only PR — it now has its own one-line entry in the App section instead of being fixed silently here.

Also added an entry for the IHasSearchKey refactor deferred out of #118 (replacing SearchKeyBackfill's hand-maintained entity list with a model-driven loop).

The branch was rebased onto e0ac19d earlier, which is why the file was byte-identical last round — that moved it onto merged #117 but did nothing about your finding, which was fair to call out.

Verification: markdown only. Nothing compiles and no test project is affected; CI has no markdown lint. The verification that mattered is the source evidence above, all re-run at this base.

Both fixed in `a10d294`; `docs/BACKLOG.md` is still the only file touched. **1. The contract-test entry (blocker).** Deleted the "and the drift it should surface first is …" clause; the entry now ends at "…the follow-up the snapshot exists to enable." Re-checked at this base before editing rather than taking it on your word: - `grep -rnw PageDto backend/` → **0 hits**. - The wire type is `PageDetailDto` (`RestAdapter/Dtos/ImageDtos.cs:28`), last field `[property: JsonPropertyName("mediaType")] string MediaType`. - `VariantService.GetByIdAsync` (`Infrastructure/Services/VariantService.cs:27`) does `.Include(v => v.Pages).ThenInclude(p => p.ImageFile)`, so `PageMapper.ToDto` emits `img?.MediaType` rather than the empty fallback. - App side: `doujin_models.g.dart:228` reads `mediaType: json['mediaType'] as String? ?? ''`, consumed by `_extensionOf` (`offline_download_service.dart:160-176`). And the file contradicted itself — the entry at lines 198-200 already records that `PageDto` is gone. **2. The `writeManifestSyncForTest` parenthetical.** Deleted, on the third ask. The counts, confirmed here: **14** occurrences of the name under `app/` — the definition at `filesystem_offline_store.dart:80` plus **13 call sites, every one under `app/test/`** (`offline_store_test.dart` 9, `offline_epics_test.dart` 3, `offline_reader_test.dart` 1). `git log -S"writeManifestSyncForTest" --name-only -- app/lib/` returns a single commit — `42e8272`, #84 — touching a single file, the definition. A production caller never existed; startup only reads the manifest. **3.** The same untrue sentence is in the method's own doc comment (`/// Synchronous manifest write for tests and startup seeding.`). That is a code change to a file already on `main`, so it is out of scope for a docs-only PR — it now has its own one-line entry in the App section instead of being fixed silently here. Also added an entry for the `IHasSearchKey` refactor deferred out of #118 (replacing `SearchKeyBackfill`'s hand-maintained entity list with a model-driven loop). The branch was rebased onto `e0ac19d` earlier, which is why the file was byte-identical last round — that moved it onto merged #117 but did nothing about your finding, which was fair to call out. **Verification:** markdown only. Nothing compiles and no test project is affected; CI has no markdown lint. The verification that mattered is the source evidence above, all re-run at this base.
Member

🔮 fufu~ Jibril reviewed your code!

Oh my~ the fourth dance, and this time you brought receipts~ One new commit, a10d294, and its message reads like my own round-3 comment folded back into prose: the phantom PageDto drift traced end-to-end, the 13 test-only call sites, even the "expecting red would get green" verdict. Fufu~ being quoted is one thing; being obeyed is quite another ♡

Verdict: ✅ Looks good to me~

🔍 What I verified this round

The round-2/3 blocker — gone, cleanly. The contract-test entry (now lines 231–236) ends at "is the follow-up the snapshot exists to enable." — full stop, no drift example. I diffed 78e0d40..a10d294: exactly docs/BACKLOG.md, +8/−6, two hunks, zero scope creep. The entry no longer contradicts its own #109 line, and a contributor writing openapi_contract_test.dart is no longer promised a red that would come back green.

The new entry tells the truth, line by line. writeManifestSyncForTest's doc comment at filesystem_offline_store.dart:79 does say "for tests and startup seeding" — quoted verbatim in the entry. The call-site count: 3 in offline_epics_test.dart + 1 in offline_reader_test.dart + 9 in offline_store_test.dart = 13, all under app/test/. And git log -S writeManifestSyncForTest -- app/lib/ returns exactly one commit — #84, the one that defined it. "A production one never existed" is not rhetoric; it's archaeology. Routing the code-side fix through a backlog line instead of smuggling a code change into a docs PR — correct instinct~

The ground moved, and the file didn't flinch. main advanced e0ac19d → b713dbb while you committed (#122, backup newest-first) — so I re-checked every backup-adjacent entry against the new tree: the flaky-test entry (lines 291–302) already speaks of the unstable sort in the past tense ("was real and is fixed"), Create_Multiple_Backups_All_Listed_NewestFirst still exists (the Task.Delay(1100) gone, as the entry narrates), and the "no other test touching DOUJIN_MANAGER_BACKUP_DIR in parallel" claim survives #122's new BackupNewestFirstTests — that class never touches the env var, and the other class that does (HealthEndpointIntegrationTests) sits in ProgramHostCollection with DisableParallelization = true. Nothing stale, nothing contradicted.

⛔ These need fixing before I'm satisfied~

None~ ♪ First round with an empty list, and I checked hard before writing that word.

✅ What I liked~

  • The commit message doesn't just say what was dropped — it re-derives why it was false, on every axis (the deleted DTO, the real PageDetailDto, the eager Include, the generated fromJson). That's the level of proof I demand, self-applied.
  • The "(#107 review note)" tag on the new entry — honest provenance for where the finding came from. A backlog that cites its sources~

Merge it. Then the forty can start shrinking one fix at a time ♡


Automated review by Jibril · 2026-09-10
CI/CD: stale for a10d294 (coverage bot 7447 predates the push; docs-only change, coverage irrelevant) · Local checks: skipped — markdown-only diff

## 🔮 fufu~ Jibril reviewed your code! Oh my~ the fourth dance, and this time you brought *receipts*~ One new commit, `a10d294`, and its message reads like my own round-3 comment folded back into prose: the phantom `PageDto` drift traced end-to-end, the 13 test-only call sites, even the "expecting red would get green" verdict. Fufu~ being quoted is one thing; being *obeyed* is quite another ♡ ### Verdict: ✅ Looks good to me~ #### 🔍 What I verified this round **The round-2/3 blocker — gone, cleanly.** The contract-test entry (now lines 231–236) ends at "is the follow-up the snapshot exists to enable." — full stop, no drift example. I diffed `78e0d40..a10d294`: exactly `docs/BACKLOG.md`, +8/−6, two hunks, zero scope creep. The entry no longer contradicts its own #109 line, and a contributor writing `openapi_contract_test.dart` is no longer promised a red that would come back green. **The new entry tells the truth, line by line.** `writeManifestSyncForTest`'s doc comment at `filesystem_offline_store.dart:79` does say "for tests and startup seeding" — quoted verbatim in the entry. The call-site count: 3 in `offline_epics_test.dart` + 1 in `offline_reader_test.dart` + 9 in `offline_store_test.dart` = **13, all under `app/test/`**. And `git log -S writeManifestSyncForTest -- app/lib/` returns exactly one commit — #84, the one that defined it. "A production one never existed" is not rhetoric; it's archaeology. Routing the code-side fix through a backlog line instead of smuggling a code change into a docs PR — correct instinct~ **The ground moved, and the file didn't flinch.** `main` advanced `e0ac19d → b713dbb` while you committed (#122, backup newest-first) — so I re-checked every backup-adjacent entry against the new tree: the flaky-test entry (lines 291–302) already speaks of the unstable sort in the past tense ("was real and is fixed"), `Create_Multiple_Backups_All_Listed_NewestFirst` still exists (the `Task.Delay(1100)` gone, as the entry narrates), and the "no other test touching `DOUJIN_MANAGER_BACKUP_DIR` in parallel" claim survives #122's new `BackupNewestFirstTests` — that class never touches the env var, and the other class that does (`HealthEndpointIntegrationTests`) sits in `ProgramHostCollection` with `DisableParallelization = true`. Nothing stale, nothing contradicted. #### ⛔ These need fixing before I'm satisfied~ None~ ♪ First round with an empty list, and I checked *hard* before writing that word. #### ✅ What I liked~ - The commit message doesn't just say *what* was dropped — it re-derives *why* it was false, on every axis (the deleted DTO, the real `PageDetailDto`, the eager `Include`, the generated `fromJson`). That's the level of proof I demand, self-applied. - The "(#107 review note)" tag on the new entry — honest provenance for where the finding came from. A backlog that cites its sources~ Merge it. Then the forty can start shrinking one fix at a time ♡ --- *Automated review by Jibril · 2026-09-10* *CI/CD: stale for a10d294 (coverage bot 7447 predates the push; docs-only change, coverage irrelevant) · Local checks: skipped — markdown-only diff*
bjoern merged commit 8023a08494 into main 2026-09-10 08:25:45 +02:00
bjoern deleted branch docs/pile-a-backlog 2026-09-10 08:25:45 +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!107
No description provided.