feat: retire the panel region kind (ADR 0027 amendment 1) #195

Merged
bjoern merged 2 commits from feat/retire-panel-regions into main 2026-09-21 11:42:55 +02:00
Member

Removes the panel region kind entirely — code, stored rows, wire value, and docs — per ADR 0027 amendment 1. The owner paged through the library with the overlay on (the deliverable phase 1 existed to produce), and the panel outlines were wrong often enough to be useless.

What changes

Enum (wire contract kept). ImageRegionKind.Panel is gone; 0 stays reserved forever, text 1 / balloon 2 keep their numbers. A legacy 0 on the client decodes as unknown and draws grey, like any future kind.

Migration RetirePanelRegions. Deletes kind = 0 rows and recomputes each analysis's denormalised region_count. Analysis rows are not invalidated — same model, same text/balloon rows; a re-sweep would answer identically and an hour of CPU would buy nothing.

Class map retired tier. frame/panel are recognised-and-dropped: the shipped model declares all three classes and stays loadable. Detections are skipped before any mask work. Unknown names still fail the container; a model whose every class is retired is refused with DOUJIN_MANAGER_DETECTOR_MODEL named.

Overlay. Purple case removed; regionColour now text/balloon/unknown.

Docs. ADR 0027 amendment 1 (supersedes the "panels are stored but not trusted" consequence), ADR 0028 amendment 1 (supply unchanged — chosen on text/balloon recall, frame was never the reason), TRANSLATION_PLAN story 1.1 note + phase 3 open question 4 rewritten (reading order can no longer lean on stored panels), DEPLOYMENT.md §2 wording.

Verification

  • scripts/verify.sh backend: ApplicationCore 136 ✅, Infrastructure 323 ✅ (5 real-model skips, no model in CI), RestAdapter 1048 ✅ (5 env-dependent skips). IntegrationTests suite aborted OOM in the parallel run — re-run in isolation: 1/1 ✅ (container memory, four testhosts at once).
  • scripts/verify.sh app: 1530 tests ✅, analyze clean, codegen checksums stable.
  • New directional tests: a retired class appears in no stored kind; a frame-only model is refused naming the setting; the refusal message names the retired names; wire-number test now pins 1/2 and documents 0 as retired-not-reused.
Removes the panel region kind entirely — code, stored rows, wire value, and docs — per ADR 0027 amendment 1. The owner paged through the library with the overlay on (the deliverable phase 1 existed to produce), and the panel outlines were wrong often enough to be useless. ## What changes **Enum (wire contract kept).** `ImageRegionKind.Panel` is gone; `0` stays reserved forever, text `1` / balloon `2` keep their numbers. A legacy `0` on the client decodes as `unknown` and draws grey, like any future kind. **Migration `RetirePanelRegions`.** Deletes `kind = 0` rows and recomputes each analysis's denormalised `region_count`. Analysis rows are **not** invalidated — same model, same text/balloon rows; a re-sweep would answer identically and an hour of CPU would buy nothing. **Class map retired tier.** `frame`/`panel` are recognised-and-dropped: the shipped model declares all three classes and stays loadable. Detections are skipped before any mask work. Unknown names still fail the container; a model whose *every* class is retired is refused with `DOUJIN_MANAGER_DETECTOR_MODEL` named. **Overlay.** Purple case removed; `regionColour` now text/balloon/unknown. **Docs.** ADR 0027 amendment 1 (supersedes the "panels are stored but not trusted" consequence), ADR 0028 amendment 1 (supply unchanged — chosen on text/balloon recall, `frame` was never the reason), TRANSLATION_PLAN story 1.1 note + phase 3 open question 4 rewritten (reading order can no longer lean on stored panels), DEPLOYMENT.md §2 wording. ## Verification - `scripts/verify.sh backend`: ApplicationCore 136 ✅, Infrastructure 323 ✅ (5 real-model skips, no model in CI), RestAdapter 1048 ✅ (5 env-dependent skips). IntegrationTests suite aborted OOM in the parallel run — re-run in isolation: 1/1 ✅ (container memory, four testhosts at once). - `scripts/verify.sh app`: 1530 tests ✅, analyze clean, codegen checksums stable. - New directional tests: a retired class appears in no stored kind; a frame-only model is refused naming the setting; the refusal message names the retired names; wire-number test now pins 1/2 and documents 0 as retired-not-reused.
feat: retire the panel region kind (ADR 0027 amendment 1)
Some checks failed
CI / build (pull_request) Successful in 28s
Deploy Smoke Test / smoke (pull_request) Failing after 3m1s
Flutter CI / analyze-and-test (pull_request) Failing after 12m3s
CI / test (pull_request) Successful in 2m39s
07c790624d
The owner paged through the library with the reader overlay on - the
quality gate phase 1 was built to produce - and the panel outlines were
wrong often enough to be useless. The kind is removed entirely:

- ImageRegionKind loses Panel; 0 stays reserved (wire + stored rows),
  text 1 / balloon 2 keep their numbers.
- RetirePanelRegions migration deletes panel rows and recomputes each
  analysis's region_count. Analyses stay valid: same model, same
  text/balloon rows, a re-sweep would answer identically.
- RegionClassMap gains a retired tier (frame, panel): the shipped model
  still declares all three classes, so those names parse and their
  detections are dropped before any mask work. A model whose every
  class is retired is refused with the setting named.
- The overlay loses the purple case; a legacy 0 decodes as unknown and
  draws grey like any future kind.
- ADR 0027 amendment 1, ADR 0028 amendment 1, TRANSLATION_PLAN stories
  1.1/1.5 notes and phase 3 open question 4, DEPLOYMENT.md wording.

Summary

Summary
Generated on: 09/21/2026 - 09:43:43
Coverage date: 09/21/2026 - 09:41:44 - 09/21/2026 - 09:43:39
Parser: MultiReport (4x Cobertura)
Assemblies: 4
Classes: 523
Files: 217
Line coverage: 96.9% (24598 of 25370)
Covered lines: 24598
Uncovered lines: 772
Coverable lines: 25370
Total lines: 39743
Branch coverage: 81.4% (2585 of 3173)
Covered branches: 2585
Total branches: 3173
Method coverage: Feature is only available for sponsors

Coverage

DoujinManager.ApplicationCore - 96.9%
Name Line Branch
DoujinManager.ApplicationCore 96.9% 95.1%
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.ImageAnalysis 90%
DoujinManager.ApplicationCore.Entities.ImageFile 100%
DoujinManager.ApplicationCore.Entities.ImageRegion 90%
DoujinManager.ApplicationCore.Entities.ImageShards 100% 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 87.5%
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.ImageRegionId 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.DetectedRegion 100%
DoujinManager.ApplicationCore.Ports.ExtractedImage 100%
DoujinManager.ApplicationCore.Ports.ImageInspection 100%
DoujinManager.ApplicationCore.Ports.RegionDetection 100%
DoujinManager.ApplicationCore.Ports.RegionDetectionFailedException 100%
DoujinManager.ApplicationCore.Ports.StoredImage 100%
DoujinManager.ApplicationCore.Services.BackupContent 100%
DoujinManager.ApplicationCore.Services.BackupDirectory 100%
DoujinManager.ApplicationCore.Services.BackupFailedException 100%
DoujinManager.ApplicationCore.Services.BackupInfo 100%
DoujinManager.ApplicationCore.Services.BackupSchedule 100% 100%
DoujinManager.ApplicationCore.Services.BackupScheduleStatus 100%
DoujinManager.ApplicationCore.Services.BackupStaging 100%
DoujinManager.ApplicationCore.Services.BackupVerification 100% 100%
DoujinManager.ApplicationCore.Services.ITagService 100%
DoujinManager.ApplicationCore.Services.LikePattern 100%
DoujinManager.ApplicationCore.Services.RegionGeometry 100% 100%
DoujinManager.ApplicationCore.Services.RegionPoint 100%
DoujinManager.ApplicationCore.Services.RegionSweepBatch 100%
DoujinManager.ApplicationCore.Services.RegionSweepJournal 100%
DoujinManager.ApplicationCore.Services.RestoreRequest 100%
DoujinManager.ApplicationCore.Services.RestoreTarget 100%
DoujinManager.ApplicationCore.Services.ScheduledBackupJournal 100%
DoujinManager.ApplicationCore.Services.ScheduledBackupOutcome 100% 100%
DoujinManager.ApplicationCore.Services.SearchIndex 100% 100%
DoujinManager.ApplicationCore.Services.SearchKey 100% 100%
DoujinManager.ApplicationCore.Services.ServiceResult 100%
DoujinManager.ApplicationCore.Services.ServiceResult`1 100%
DoujinManager.ApplicationCore.Services.SortKey 100% 90.9%
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.DeleteBackupCommand 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.DoujinMergeReport 100%
DoujinManager.ApplicationCore.UseCases.DoujinMergeResult 100%
DoujinManager.ApplicationCore.UseCases.DownloadBackupQuery 100%
DoujinManager.ApplicationCore.UseCases.DuplicateDoujinGroup 100%
DoujinManager.ApplicationCore.UseCases.DuplicateDoujinReference 100%
DoujinManager.ApplicationCore.UseCases.DuplicateImageGroup 100%
DoujinManager.ApplicationCore.UseCases.EntityMergeReport 100%
DoujinManager.ApplicationCore.UseCases.EntityMergeResult`1 100%
DoujinManager.ApplicationCore.UseCases.EntityMergeRoleConflict 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.GetImageRegionsQuery 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.ImageRegions 100%
DoujinManager.ApplicationCore.UseCases.ImageSweepQuery 100%
DoujinManager.ApplicationCore.UseCases.ImageSweepSlice 100%
DoujinManager.ApplicationCore.UseCases.ImportBackupCommand 100%
DoujinManager.ApplicationCore.UseCases.IntegrityCheck 100%
DoujinManager.ApplicationCore.UseCases.IntegrityChecks 93.3% 91.6%
DoujinManager.ApplicationCore.UseCases.IntegrityFinding 100%
DoujinManager.ApplicationCore.UseCases.IntegrityRepairResult 100%
DoujinManager.ApplicationCore.UseCases.IntegrityRepairs 96.9% 95.8%
DoujinManager.ApplicationCore.UseCases.IntegrityReport 100%
DoujinManager.ApplicationCore.UseCases.LibraryCounts 100%
DoujinManager.ApplicationCore.UseCases.LibraryStats 100%
DoujinManager.ApplicationCore.UseCases.ListChaptersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCharactersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCirclesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDuplicateDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDuplicateImagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPeopleQuery 100%
DoujinManager.ApplicationCore.UseCases.ListSeriesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListTagsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListVariantsQuery 100%
DoujinManager.ApplicationCore.UseCases.MergeCharactersCommand 100%
DoujinManager.ApplicationCore.UseCases.MergeCirclesCommand 100%
DoujinManager.ApplicationCore.UseCases.MergedEntity 100%
DoujinManager.ApplicationCore.UseCases.MergeDoujinsCommand 100%
DoujinManager.ApplicationCore.UseCases.MergePeopleCommand 100%
DoujinManager.ApplicationCore.UseCases.MergeRoleConflict 100%
DoujinManager.ApplicationCore.UseCases.MergeSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.ReadinessReport 100%
DoujinManager.ApplicationCore.UseCases.RegionDetectionStatus 100%
DoujinManager.ApplicationCore.UseCases.RegionSweepResult 100%
DoujinManager.ApplicationCore.UseCases.RemoveCharacterAliasCommand 100%
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 100%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.ReorderChaptersCommand 100%
DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.RepairIntegrityFindingCommand 100%
DoujinManager.ApplicationCore.UseCases.RequestBackupRestoreCommand 100%
DoujinManager.ApplicationCore.UseCases.RunScheduledBackupCommand 100%
DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.SearchResult 100%
DoujinManager.ApplicationCore.UseCases.SearchResults 100%
DoujinManager.ApplicationCore.UseCases.StorageUsage 100%
DoujinManager.ApplicationCore.UseCases.StoredRegion 100%
DoujinManager.ApplicationCore.UseCases.SweepRegionsCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePageChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateSeriesCommand 100%
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.ApplicationCore.UseCases.VerifyBackupCommand 100%
DoujinManager.Infrastructure - 97.7%
Name Line Branch
DoujinManager.Infrastructure 97.7% 88%
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.ImageAnalysisConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.ImageRegionConfiguration 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.AddEntitySearchIndex 96.5%
DoujinManager.Infrastructure.Data.Migrations.AddEntitySearchKey 96.9%
DoujinManager.Infrastructure.Data.Migrations.AddImageFileSha256Index 99.4%
DoujinManager.Infrastructure.Data.Migrations.AddImageRegionDetection 99.4%
DoujinManager.Infrastructure.Data.Migrations.AddScaleIndexes 98.2%
DoujinManager.Infrastructure.Data.Migrations.AddTagAliases 99.3%
DoujinManager.Infrastructure.Data.Migrations.AddTitleSearchKey 99.1%
DoujinManager.Infrastructure.Data.Migrations.AddTitleSortKey 98.6%
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.Migrations.RetirePanelRegions 99.8%
DoujinManager.Infrastructure.Data.ModelBuilderExtensions 50%
DoujinManager.Infrastructure.Data.SearchKeyBackfill 100% 100%
DoujinManager.Infrastructure.Data.SqliteDatabaseFile 100%
DoujinManager.Infrastructure.Data.SqliteMaintenance 78.2% 62.5%
DoujinManager.Infrastructure.Data.SqlitePragmaInterceptor 100% 75%
DoujinManager.Infrastructure.Data.SqliteRuntimeOptions 100% 100%
DoujinManager.Infrastructure.Data.StartupMigration 100% 100%
DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory 100%
DoujinManager.Infrastructure.Data.WalCheckpointResult 100%
DoujinManager.Infrastructure.Detection.DetectionOptions 100%
DoujinManager.Infrastructure.Detection.LetterboxTransform 100%
DoujinManager.Infrastructure.Detection.MaskContour 100% 98.5%
DoujinManager.Infrastructure.Detection.OnnxRegionDetector 93.6% 86%
DoujinManager.Infrastructure.Detection.PolygonSimplifier 100% 92.3%
DoujinManager.Infrastructure.Detection.RegionClassMap 100% 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.BackupExpectations 100%
DoujinManager.Infrastructure.Services.BackupExpectationsReader 84.7% 75%
DoujinManager.Infrastructure.Services.BackupInspection 100%
DoujinManager.Infrastructure.Services.BackupReceipts 90% 87.5%
DoujinManager.Infrastructure.Services.BackupRestoreCoordinator 71.6% 69.2%
DoujinManager.Infrastructure.Services.BackupRestoreService 93.6% 88%
DoujinManager.Infrastructure.Services.BackupService 86.7% 85.4%
DoujinManager.Infrastructure.Services.BackupStagingArea 100% 100%
DoujinManager.Infrastructure.Services.BackupVerifier 92.8% 89.1%
DoujinManager.Infrastructure.Services.ChapterService 98.1% 81.2%
DoujinManager.Infrastructure.Services.CharacterService 100% 77.7%
DoujinManager.Infrastructure.Services.CircleService 100% 77.7%
DoujinManager.Infrastructure.Services.DoujinOrdering 100% 100%
DoujinManager.Infrastructure.Services.DoujinService 100% 77.5%
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 100% 83.3%
DoujinManager.Infrastructure.Services.RestoreMarker 81.8% 83.3%
DoujinManager.Infrastructure.Services.RestoreOutcome 100%
DoujinManager.Infrastructure.Services.SearchService 100% 99%
DoujinManager.Infrastructure.Services.SeriesService 100% 77.7%
DoujinManager.Infrastructure.Services.TagService 100% 88.8%
DoujinManager.Infrastructure.Services.VariantService 96% 78.9%
DoujinManager.Infrastructure.Storage.DirectorySize 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemImageStorage 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage 97.8% 91.6%
DoujinManager.Infrastructure.UseCases 97.6% 90.3%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper
100% 100%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper
100% 87.5%
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.CancelBackupRestoreUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CheckReadinessUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateBackupUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateChapterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateCharacterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateCircleUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase 94.4% 94.1%
DoujinManager.Infrastructure.UseCases.CreatePersonUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateSeriesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateVariantUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteBackupUseCase 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.DownloadBackupUseCase 100%
DoujinManager.Infrastructure.UseCases.EntityDoujinLink 100%
DoujinManager.Infrastructure.UseCases.EntityMerge 95.4% 93.2%
DoujinManager.Infrastructure.UseCases.FindImageBySha256UseCase 97.3% 100%
DoujinManager.Infrastructure.UseCases.GetBackupRestoreRequestUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetBackupScheduleUseCase 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.GetImageRegionsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetIntegrityReportUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetPersonUseCase 100%
DoujinManager.Infrastructure.UseCases.GetRegionDetectionStatusUseCase 100% 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.GuidProbeNaming 100%
DoujinManager.Infrastructure.UseCases.ImportBackupUseCase 100%
DoujinManager.Infrastructure.UseCases.IntegrityQueries 83.3% 50%
DoujinManager.Infrastructure.UseCases.ListBackupsUseCase 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 100%
DoujinManager.Infrastructure.UseCases.ListTagsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListVariantsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.MergeCharactersUseCase 100%
DoujinManager.Infrastructure.UseCases.MergeCirclesUseCase 100%
DoujinManager.Infrastructure.UseCases.MergeDoujinsUseCase 100% 98.4%
DoujinManager.Infrastructure.UseCases.MergePeopleUseCase 100%
DoujinManager.Infrastructure.UseCases.MergeSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveCharacterAliasUseCase 100% 50%
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 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveTagUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase 100%
DoujinManager.Infrastructure.UseCases.ReorderChaptersUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.RepairIntegrityFindingUseCase 96.1% 80.7%
DoujinManager.Infrastructure.UseCases.RequestBackupRestoreUseCase 100%
DoujinManager.Infrastructure.UseCases.RunScheduledBackupUseCase 100% 81.8%
DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.SqliteHostingProbe 100% 83.3%
DoujinManager.Infrastructure.UseCases.SweepImageTreeUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.SweepRegionsUseCase 96.5% 100%
DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.UpdateCharacterUseCase 100% 83.3%
DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase 100% 83.3%
DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdatePageChapterUseCase 93.7% 87.5%
DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase 100% 83.3%
DoujinManager.Infrastructure.UseCases.UpdateSeriesUseCase 100% 83.3%
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.Infrastructure.UseCases.VerifyBackupUseCase 100%
System.Text.RegularExpressions.Generated 85% 67.3%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>FC6ABDEC12267CC0
9CDB080D11D98B3E0D3ADB24949B4D7D90DD87AD1CEB5845F__Entry_0
88% 71.4%
DoujinManager.RestAdapter - 98.9%
Name Line Branch
DoujinManager.RestAdapter 98.9% 81.9%
DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware 100% 91.6%
DoujinManager.RestAdapter.Configuration.ImageOptions 100%
DoujinManager.RestAdapter.Configuration.IntegrityOptions 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.BackupScheduleDto 100% 100%
DoujinManager.RestAdapter.Dtos.BackupVerificationDto 100% 100%
DoujinManager.RestAdapter.Dtos.ChapterDto 100%
DoujinManager.RestAdapter.Dtos.CharacterDto 100%
DoujinManager.RestAdapter.Dtos.CharacterMergeResultDto 100%
DoujinManager.RestAdapter.Dtos.CircleDto 100%
DoujinManager.RestAdapter.Dtos.CircleMergeResultDto 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.DetectionStatusDto 100% 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.EntityMergeReportDto 100%
DoujinManager.RestAdapter.Dtos.EntityMergeRoleConflictDto 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.ImageRegionDto 100% 100%
DoujinManager.RestAdapter.Dtos.ImageRegionsDto 100% 100%
DoujinManager.RestAdapter.Dtos.ImageSweepSliceDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityCheckDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityFindingDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityRepairResultDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityReportDto 100%
DoujinManager.RestAdapter.Dtos.LinkCharacterDto 100%
DoujinManager.RestAdapter.Dtos.LinkCircleDto 100%
DoujinManager.RestAdapter.Dtos.LinkPersonDto 100%
DoujinManager.RestAdapter.Dtos.LinkSeriesDto 100%
DoujinManager.RestAdapter.Dtos.MergedEntityDto 100%
DoujinManager.RestAdapter.Dtos.MergeDoujinsDto 100%
DoujinManager.RestAdapter.Dtos.MergeEntitiesDto 100%
DoujinManager.RestAdapter.Dtos.MergeReportDto 100%
DoujinManager.RestAdapter.Dtos.MergeResultDto 100%
DoujinManager.RestAdapter.Dtos.MergeRoleConflictDto 100%
DoujinManager.RestAdapter.Dtos.PageDetailDto 100%
DoujinManager.RestAdapter.Dtos.PersonDto 100%
DoujinManager.RestAdapter.Dtos.PersonMergeResultDto 100%
DoujinManager.RestAdapter.Dtos.RegionSweepBatchDto 16.6% 50%
DoujinManager.RestAdapter.Dtos.ReorderChaptersDto 100%
DoujinManager.RestAdapter.Dtos.ReorderPagesDto 100%
DoujinManager.RestAdapter.Dtos.RestoreRequestDto 100%
DoujinManager.RestAdapter.Dtos.ScheduledBackupRunDto 100% 100%
DoujinManager.RestAdapter.Dtos.SearchDoujinsDto 100%
DoujinManager.RestAdapter.Dtos.SearchResultDto 100%
DoujinManager.RestAdapter.Dtos.SeriesDto 100%
DoujinManager.RestAdapter.Dtos.SeriesMergeResultDto 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 100%
DoujinManager.RestAdapter.Dtos.UpdateCircleDto 100%
DoujinManager.RestAdapter.Dtos.UpdateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePageChapterDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePersonDto 100%
DoujinManager.RestAdapter.Dtos.UpdateSeriesDto 100%
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 99.2%
DoujinManager.RestAdapter.Endpoints.DetectionEndpoints 100%
DoujinManager.RestAdapter.Endpoints.DoujinEndpoints 98.1% 55.5%
DoujinManager.RestAdapter.Endpoints.HealthEndpoints 100%
DoujinManager.RestAdapter.Endpoints.ImageEndpoints 97.6% 75%
DoujinManager.RestAdapter.Endpoints.IntegrityEndpoints 100%
DoujinManager.RestAdapter.Endpoints.MetadataEndpoints 99.3% 50%
DoujinManager.RestAdapter.Endpoints.PageMapper 100% 58.3%
DoujinManager.RestAdapter.Endpoints.PaginationParams 100%
DoujinManager.RestAdapter.Endpoints.SearchEndpoints 100% 50%
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 98.1% 94.1%
DoujinManager.RestAdapter.Envelopes.Link 100%
DoujinManager.RestAdapter.Envelopes.MediaTypeJsonConverter 88.8% 87.5%
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 99.8% 94.4%
Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C
E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr
ibuteCache
100% 62.5%
System.Runtime.CompilerServices 0%
DoujinManager.Server - 67.9%
Name Line Branch
DoujinManager.Server 67.9% 36.5%
DoujinManager.Server.BackupRegistration 100% 100%
DoujinManager.Server.BackupScheduleOptions 100%
DoujinManager.Server.BearerSecuritySchemeTransformer 100% 78.5%
DoujinManager.Server.DatabaseRegistration 89.6%
DoujinManager.Server.DetectionConfiguration 100% 100%
DoujinManager.Server.DetectionRegistration 68.7% 56.2%
DoujinManager.Server.DetectionSettings 100%
DoujinManager.Server.ImageInfrastructureRegistration 100%
DoujinManager.Server.ProxyAwareServerTransformer 100% 75%
DoujinManager.Server.ReadinessRegistration 100%
DoujinManager.Server.RegionDetectionService 78.4% 80%
DoujinManager.Server.ScalarUi 100%
DoujinManager.Server.ScheduledBackupConfiguration 100% 91.1%
DoujinManager.Server.ScheduledBackupService 80.2% 75%
DoujinManager.Server.SqliteRuntimeConfiguration 96.5% 95%
DoujinManager.Server.UseCaseRegistrationHelper 100%
Microsoft.AspNetCore.OpenApi.Generated 1.3% 0%
Program 85.4% 30%
System.Runtime.CompilerServices 0%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/21/2026 - 09:43:43 | | Coverage date: | 09/21/2026 - 09:41:44 - 09/21/2026 - 09:43:39 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 4 | | Classes: | 523 | | Files: | 217 | | **Line coverage:** | 96.9% (24598 of 25370) | | Covered lines: | 24598 | | Uncovered lines: | 772 | | Coverable lines: | 25370 | | Total lines: | 39743 | | **Branch coverage:** | 81.4% (2585 of 3173) | | Covered branches: | 2585 | | Total branches: | 3173 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>DoujinManager.ApplicationCore - 96.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.ApplicationCore**|**96.9%**|**95.1%**| |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.ImageAnalysis|90%|| |DoujinManager.ApplicationCore.Entities.ImageFile|100%|| |DoujinManager.ApplicationCore.Entities.ImageRegion|90%|| |DoujinManager.ApplicationCore.Entities.ImageShards|100%|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|87.5%|| |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.ImageRegionId|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.DetectedRegion|100%|| |DoujinManager.ApplicationCore.Ports.ExtractedImage|100%|| |DoujinManager.ApplicationCore.Ports.ImageInspection|100%|| |DoujinManager.ApplicationCore.Ports.RegionDetection|100%|| |DoujinManager.ApplicationCore.Ports.RegionDetectionFailedException|100%|| |DoujinManager.ApplicationCore.Ports.StoredImage|100%|| |DoujinManager.ApplicationCore.Services.BackupContent|100%|| |DoujinManager.ApplicationCore.Services.BackupDirectory|100%|| |DoujinManager.ApplicationCore.Services.BackupFailedException|100%|| |DoujinManager.ApplicationCore.Services.BackupInfo|100%|| |DoujinManager.ApplicationCore.Services.BackupSchedule|100%|100%| |DoujinManager.ApplicationCore.Services.BackupScheduleStatus|100%|| |DoujinManager.ApplicationCore.Services.BackupStaging|100%|| |DoujinManager.ApplicationCore.Services.BackupVerification|100%|100%| |DoujinManager.ApplicationCore.Services.ITagService|100%|| |DoujinManager.ApplicationCore.Services.LikePattern|100%|| |DoujinManager.ApplicationCore.Services.RegionGeometry|100%|100%| |DoujinManager.ApplicationCore.Services.RegionPoint|100%|| |DoujinManager.ApplicationCore.Services.RegionSweepBatch|100%|| |DoujinManager.ApplicationCore.Services.RegionSweepJournal|100%|| |DoujinManager.ApplicationCore.Services.RestoreRequest|100%|| |DoujinManager.ApplicationCore.Services.RestoreTarget|100%|| |DoujinManager.ApplicationCore.Services.ScheduledBackupJournal|100%|| |DoujinManager.ApplicationCore.Services.ScheduledBackupOutcome|100%|100%| |DoujinManager.ApplicationCore.Services.SearchIndex|100%|100%| |DoujinManager.ApplicationCore.Services.SearchKey|100%|100%| |DoujinManager.ApplicationCore.Services.ServiceResult|100%|| |DoujinManager.ApplicationCore.Services.ServiceResult`1|100%|| |DoujinManager.ApplicationCore.Services.SortKey|100%|90.9%| |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.DeleteBackupCommand|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.DoujinMergeReport|100%|| |DoujinManager.ApplicationCore.UseCases.DoujinMergeResult|100%|| |DoujinManager.ApplicationCore.UseCases.DownloadBackupQuery|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateDoujinGroup|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateDoujinReference|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateImageGroup|100%|| |DoujinManager.ApplicationCore.UseCases.EntityMergeReport|100%|| |DoujinManager.ApplicationCore.UseCases.EntityMergeResult`1|100%|| |DoujinManager.ApplicationCore.UseCases.EntityMergeRoleConflict|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.GetImageRegionsQuery|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.ImageRegions|100%|| |DoujinManager.ApplicationCore.UseCases.ImageSweepQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ImageSweepSlice|100%|| |DoujinManager.ApplicationCore.UseCases.ImportBackupCommand|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityCheck|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityChecks|93.3%|91.6%| |DoujinManager.ApplicationCore.UseCases.IntegrityFinding|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityRepairResult|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityRepairs|96.9%|95.8%| |DoujinManager.ApplicationCore.UseCases.IntegrityReport|100%|| |DoujinManager.ApplicationCore.UseCases.LibraryCounts|100%|| |DoujinManager.ApplicationCore.UseCases.LibraryStats|100%|| |DoujinManager.ApplicationCore.UseCases.ListChaptersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCharactersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCirclesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDuplicateDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDuplicateImagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPeopleQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListSeriesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListTagsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListVariantsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.MergeCharactersCommand|100%|| |DoujinManager.ApplicationCore.UseCases.MergeCirclesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.MergedEntity|100%|| |DoujinManager.ApplicationCore.UseCases.MergeDoujinsCommand|100%|| |DoujinManager.ApplicationCore.UseCases.MergePeopleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.MergeRoleConflict|100%|| |DoujinManager.ApplicationCore.UseCases.MergeSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.ReadinessReport|100%|| |DoujinManager.ApplicationCore.UseCases.RegionDetectionStatus|100%|| |DoujinManager.ApplicationCore.UseCases.RegionSweepResult|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCharacterAliasCommand|100%|| |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|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.ReorderChaptersCommand|100%|| |DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RepairIntegrityFindingCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RequestBackupRestoreCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RunScheduledBackupCommand|100%|| |DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResult|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResults|100%|| |DoujinManager.ApplicationCore.UseCases.StorageUsage|100%|| |DoujinManager.ApplicationCore.UseCases.StoredRegion|100%|| |DoujinManager.ApplicationCore.UseCases.SweepRegionsCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePageChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateSeriesCommand|100%|| |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.ApplicationCore.UseCases.VerifyBackupCommand|100%|| </details> <details><summary>DoujinManager.Infrastructure - 97.7%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Infrastructure**|**97.7%**|**88%**| |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.ImageAnalysisConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.ImageRegionConfiguration|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.AddEntitySearchIndex|96.5%|| |DoujinManager.Infrastructure.Data.Migrations.AddEntitySearchKey|96.9%|| |DoujinManager.Infrastructure.Data.Migrations.AddImageFileSha256Index|99.4%|| |DoujinManager.Infrastructure.Data.Migrations.AddImageRegionDetection|99.4%|| |DoujinManager.Infrastructure.Data.Migrations.AddScaleIndexes|98.2%|| |DoujinManager.Infrastructure.Data.Migrations.AddTagAliases|99.3%|| |DoujinManager.Infrastructure.Data.Migrations.AddTitleSearchKey|99.1%|| |DoujinManager.Infrastructure.Data.Migrations.AddTitleSortKey|98.6%|| |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.Migrations.RetirePanelRegions|99.8%|| |DoujinManager.Infrastructure.Data.ModelBuilderExtensions|50%|| |DoujinManager.Infrastructure.Data.SearchKeyBackfill|100%|100%| |DoujinManager.Infrastructure.Data.SqliteDatabaseFile|100%|| |DoujinManager.Infrastructure.Data.SqliteMaintenance|78.2%|62.5%| |DoujinManager.Infrastructure.Data.SqlitePragmaInterceptor|100%|75%| |DoujinManager.Infrastructure.Data.SqliteRuntimeOptions|100%|100%| |DoujinManager.Infrastructure.Data.StartupMigration|100%|100%| |DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory|100%|| |DoujinManager.Infrastructure.Data.WalCheckpointResult|100%|| |DoujinManager.Infrastructure.Detection.DetectionOptions|100%|| |DoujinManager.Infrastructure.Detection.LetterboxTransform|100%|| |DoujinManager.Infrastructure.Detection.MaskContour|100%|98.5%| |DoujinManager.Infrastructure.Detection.OnnxRegionDetector|93.6%|86%| |DoujinManager.Infrastructure.Detection.PolygonSimplifier|100%|92.3%| |DoujinManager.Infrastructure.Detection.RegionClassMap|100%|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.BackupExpectations|100%|| |DoujinManager.Infrastructure.Services.BackupExpectationsReader|84.7%|75%| |DoujinManager.Infrastructure.Services.BackupInspection|100%|| |DoujinManager.Infrastructure.Services.BackupReceipts|90%|87.5%| |DoujinManager.Infrastructure.Services.BackupRestoreCoordinator|71.6%|69.2%| |DoujinManager.Infrastructure.Services.BackupRestoreService|93.6%|88%| |DoujinManager.Infrastructure.Services.BackupService|86.7%|85.4%| |DoujinManager.Infrastructure.Services.BackupStagingArea|100%|100%| |DoujinManager.Infrastructure.Services.BackupVerifier|92.8%|89.1%| |DoujinManager.Infrastructure.Services.ChapterService|98.1%|81.2%| |DoujinManager.Infrastructure.Services.CharacterService|100%|77.7%| |DoujinManager.Infrastructure.Services.CircleService|100%|77.7%| |DoujinManager.Infrastructure.Services.DoujinOrdering|100%|100%| |DoujinManager.Infrastructure.Services.DoujinService|100%|77.5%| |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|100%|83.3%| |DoujinManager.Infrastructure.Services.RestoreMarker|81.8%|83.3%| |DoujinManager.Infrastructure.Services.RestoreOutcome|100%|| |DoujinManager.Infrastructure.Services.SearchService|100%|99%| |DoujinManager.Infrastructure.Services.SeriesService|100%|77.7%| |DoujinManager.Infrastructure.Services.TagService|100%|88.8%| |DoujinManager.Infrastructure.Services.VariantService|96%|78.9%| |DoujinManager.Infrastructure.Storage.DirectorySize|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemImageStorage|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage|97.8%|91.6%| |DoujinManager.Infrastructure.UseCases|97.6%|90.3%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper|100%|100%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper|100%|87.5%| |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.CancelBackupRestoreUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CheckReadinessUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateBackupUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateChapterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateCharacterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateCircleUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase|94.4%|94.1%| |DoujinManager.Infrastructure.UseCases.CreatePersonUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateSeriesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateVariantUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteBackupUseCase|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.DownloadBackupUseCase|100%|| |DoujinManager.Infrastructure.UseCases.EntityDoujinLink|100%|| |DoujinManager.Infrastructure.UseCases.EntityMerge|95.4%|93.2%| |DoujinManager.Infrastructure.UseCases.FindImageBySha256UseCase|97.3%|100%| |DoujinManager.Infrastructure.UseCases.GetBackupRestoreRequestUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetBackupScheduleUseCase|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.GetImageRegionsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetIntegrityReportUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetPersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetRegionDetectionStatusUseCase|100%|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.GuidProbeNaming|100%|| |DoujinManager.Infrastructure.UseCases.ImportBackupUseCase|100%|| |DoujinManager.Infrastructure.UseCases.IntegrityQueries|83.3%|50%| |DoujinManager.Infrastructure.UseCases.ListBackupsUseCase|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|100%|| |DoujinManager.Infrastructure.UseCases.ListTagsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListVariantsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.MergeCharactersUseCase|100%|| |DoujinManager.Infrastructure.UseCases.MergeCirclesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.MergeDoujinsUseCase|100%|98.4%| |DoujinManager.Infrastructure.UseCases.MergePeopleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.MergeSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveCharacterAliasUseCase|100%|50%| |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|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveTagUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ReorderChaptersUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.RepairIntegrityFindingUseCase|96.1%|80.7%| |DoujinManager.Infrastructure.UseCases.RequestBackupRestoreUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RunScheduledBackupUseCase|100%|81.8%| |DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.SqliteHostingProbe|100%|83.3%| |DoujinManager.Infrastructure.UseCases.SweepImageTreeUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.SweepRegionsUseCase|96.5%|100%| |DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.UpdateCharacterUseCase|100%|83.3%| |DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase|100%|83.3%| |DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdatePageChapterUseCase|93.7%|87.5%| |DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase|100%|83.3%| |DoujinManager.Infrastructure.UseCases.UpdateSeriesUseCase|100%|83.3%| |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.Infrastructure.UseCases.VerifyBackupUseCase|100%|| |System.Text.RegularExpressions.Generated|85%|67.3%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>FC6ABDEC12267CC0<br/>9CDB080D11D98B3E0D3ADB24949B4D7D90DD87AD1CEB5845F__Entry_0|88%|71.4%| </details> <details><summary>DoujinManager.RestAdapter - 98.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.RestAdapter**|**98.9%**|**81.9%**| |DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware|100%|91.6%| |DoujinManager.RestAdapter.Configuration.ImageOptions|100%|| |DoujinManager.RestAdapter.Configuration.IntegrityOptions|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.BackupScheduleDto|100%|100%| |DoujinManager.RestAdapter.Dtos.BackupVerificationDto|100%|100%| |DoujinManager.RestAdapter.Dtos.ChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.CharacterMergeResultDto|100%|| |DoujinManager.RestAdapter.Dtos.CircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CircleMergeResultDto|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.DetectionStatusDto|100%|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.EntityMergeReportDto|100%|| |DoujinManager.RestAdapter.Dtos.EntityMergeRoleConflictDto|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.ImageRegionDto|100%|100%| |DoujinManager.RestAdapter.Dtos.ImageRegionsDto|100%|100%| |DoujinManager.RestAdapter.Dtos.ImageSweepSliceDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityCheckDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityFindingDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityRepairResultDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityReportDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkSeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.MergedEntityDto|100%|| |DoujinManager.RestAdapter.Dtos.MergeDoujinsDto|100%|| |DoujinManager.RestAdapter.Dtos.MergeEntitiesDto|100%|| |DoujinManager.RestAdapter.Dtos.MergeReportDto|100%|| |DoujinManager.RestAdapter.Dtos.MergeResultDto|100%|| |DoujinManager.RestAdapter.Dtos.MergeRoleConflictDto|100%|| |DoujinManager.RestAdapter.Dtos.PageDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.PersonDto|100%|| |DoujinManager.RestAdapter.Dtos.PersonMergeResultDto|100%|| |DoujinManager.RestAdapter.Dtos.RegionSweepBatchDto|16.6%|50%| |DoujinManager.RestAdapter.Dtos.ReorderChaptersDto|100%|| |DoujinManager.RestAdapter.Dtos.ReorderPagesDto|100%|| |DoujinManager.RestAdapter.Dtos.RestoreRequestDto|100%|| |DoujinManager.RestAdapter.Dtos.ScheduledBackupRunDto|100%|100%| |DoujinManager.RestAdapter.Dtos.SearchDoujinsDto|100%|| |DoujinManager.RestAdapter.Dtos.SearchResultDto|100%|| |DoujinManager.RestAdapter.Dtos.SeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.SeriesMergeResultDto|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|100%|| |DoujinManager.RestAdapter.Dtos.UpdateCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePageChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePersonDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateSeriesDto|100%|| |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|99.2%|| |DoujinManager.RestAdapter.Endpoints.DetectionEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.DoujinEndpoints|98.1%|55.5%| |DoujinManager.RestAdapter.Endpoints.HealthEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.ImageEndpoints|97.6%|75%| |DoujinManager.RestAdapter.Endpoints.IntegrityEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.MetadataEndpoints|99.3%|50%| |DoujinManager.RestAdapter.Endpoints.PageMapper|100%|58.3%| |DoujinManager.RestAdapter.Endpoints.PaginationParams|100%|| |DoujinManager.RestAdapter.Endpoints.SearchEndpoints|100%|50%| |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|98.1%|94.1%| |DoujinManager.RestAdapter.Envelopes.Link|100%|| |DoujinManager.RestAdapter.Envelopes.MediaTypeJsonConverter|88.8%|87.5%| |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|99.8%|94.4%| |Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C<br/>E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr<br/>ibuteCache|100%|62.5%| |System.Runtime.CompilerServices|0%|| </details> <details><summary>DoujinManager.Server - 67.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Server**|**67.9%**|**36.5%**| |DoujinManager.Server.BackupRegistration|100%|100%| |DoujinManager.Server.BackupScheduleOptions|100%|| |DoujinManager.Server.BearerSecuritySchemeTransformer|100%|78.5%| |DoujinManager.Server.DatabaseRegistration|89.6%|| |DoujinManager.Server.DetectionConfiguration|100%|100%| |DoujinManager.Server.DetectionRegistration|68.7%|56.2%| |DoujinManager.Server.DetectionSettings|100%|| |DoujinManager.Server.ImageInfrastructureRegistration|100%|| |DoujinManager.Server.ProxyAwareServerTransformer|100%|75%| |DoujinManager.Server.ReadinessRegistration|100%|| |DoujinManager.Server.RegionDetectionService|78.4%|80%| |DoujinManager.Server.ScalarUi|100%|| |DoujinManager.Server.ScheduledBackupConfiguration|100%|91.1%| |DoujinManager.Server.ScheduledBackupService|80.2%|75%| |DoujinManager.Server.SqliteRuntimeConfiguration|96.5%|95%| |DoujinManager.Server.UseCaseRegistrationHelper|100%|| |Microsoft.AspNetCore.OpenApi.Generated|1.3%|0%| |Program|85.4%|30%| |System.Runtime.CompilerServices|0%|| </details>
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! Retiring a wire number properly — reserved forever, migration deletes the rows, docs rewritten at every layer — this is how an enum member deserves to die~ I came sharp-eyed for the classic removal bugs (stale panel in a switch, a sibling Include nobody updated, a wire map that quietly renumbers) and found none of them. The sweep is spotless: every ImageRegionKind consumer, every generated file, every doc.

And then I ran everything myself, fufu~

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

Not because it's wrong — because two of the new branches are unpinned, and in this repo that's a sin. You know it is: PR #194 exists precisely because three reviews caught unpinned branches, and RealModelFact's own doc says a skip is only safe when something else still pins the branch. I proved both branches behave correctly today with my own probes — so the fix is test-only, and I expect it to be quick~

⛔ These need fixing before I'm satisfied~

  1. backend/src/DoujinManager.Infrastructure/Data/Migrations/20260921073815_RetirePanelRegions.cs:17 — the migration's data semantics have no test anywhere. I probed it myself: seeded a database at 20260920095257_AddImageRegionDetection, inserted one analysis + three regions (kind 0/1/2, region_count = 3), migrated the rest of the way → kind-0 row gone, kinds 1/2 survive, region_count recomputed to 2. Correct! But the only suites that run migrations (QueryPlanTests, BackupRestore*) seed after migrating — in CI your DELETE … WHERE kind = 0 and the region_count recompute execute over empty tables. A broken WHERE, a wrong column name, a dropped recompute — nothing goes red. Your own house pattern says this gets pinned: SearchKeyBackfillTests seeds pre-migration-state rows, and BackupRestoreServiceTests.RemoveNewestMigrationFromAsync already manipulates migration history for exactly this shape of test.
    Fix: one test that calls Migrate("20260920095257_AddImageRegionDetection"), seeds an analysis + kind-0/1/2 regions over the raw connection, runs Migrate(), and asserts exactly what I probed: no kind-0 rows, text/balloon rows intact, region_count = survivors.

  2. backend/src/DoujinManager.Infrastructure/Detection/OnnxRegionDetector.cs:248 — the retired-drop line is executed by no test, in any configuration. In CI the real-model tests skip (no model). With a model wired in (DOUJIN_MANAGER_DETECTOR_MODEL=/tmp/regions.onnx, the real shipped one — I ran the suite: 328/328, 0 skips), the detection loop runs… but A_Real_Model_Answers_With_Storable_Regions feeds it a blank 800×1200 JPEG, and I measured what the model answers there: 0 detections above the floor. No row ever reaches the class check. So the one line this PR adds to the hot loop — _classes.Retired.Contains(classIndex) → continue — is pinned by nothing. I probed it too: a constant-output synthetic ONNX (names = {0: 'frame', 1: 'text', 2: 'balloon'}, frame@0.99 / text@0.90 / balloon@0.05) → exactly one stored region, Text. Works perfectly. Now pin it~
    Fix: the synthetic-model probe is the natural test — a ~3 MB constant-output ONNX (no trained weights, so ADR 0028's no-weights-in-git rule isn't touched) as test content, one DetectAsync asserting the frame row vanishes and the text row survives. Runs in ~1 s, no network, no skip needed. (A RealModelFact on an image that actually yields a frame detection would also do, if you have such a fixture.)

✅ What I liked~

  • The retired tier is a genuinely elegant design: the shipped three-class model keeps loading, frame/panel parse-and-drop, all-retired refused with DOUJIN_MANAGER_DETECTOR_MODEL named — and the refusal message lists the retired names so an operator doesn't think it's a bug. And you tested that message! ♪
  • Dropping retired detections before any mask work, with the cost reasoning in the comment — cheapest refusal first.
  • The empty Down() is right, not lazy: the rows cannot be resurrected and nothing would read them. The comment says so.
  • Wire discipline: 1/2 unchanged and pinned by test, 0 documented as reserved in the enum, the DTO, the Dart enum, and the client comment. Old-build clients decode a cached 0 as unknown and draw grey — I traced unknownEnumValue end to end.
  • The hand-edited server_models.g.dart is byte-identical to regeneration — I ran the full codegen checksum guard, twice, including COVERAGE=1.

Automated review by Jibril · 2026-09-21
CI/CD: build ✓ + backend test ✓ for 07c7906; smoke ✗ pre-existing (fails on main at this PR's own base, run #885 — chronic); Flutter analyze-and-test ✗ on the runner, but the exact CI command (COVERAGE=1 scripts/verify.sh app) passes locally end-to-end: codegen stable, analyze clean, 1530/1530 — #194 flapped the same way (887 ✓ → 889 ✗), so I read it as runner-environmental · Local checks: backend 136 + 328 (real model wired in, 0 skips) + 1 + 1048 (+5 env-dependent skips) all green; migration probe green; retired-drop probe green; repo tree left pristine

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! Retiring a wire number *properly* — reserved forever, migration deletes the rows, docs rewritten at every layer — this is how an enum member deserves to die~ I came sharp-eyed for the classic removal bugs (stale `panel` in a switch, a sibling Include nobody updated, a wire map that quietly renumbers) and found **none of them**. The sweep is spotless: every `ImageRegionKind` consumer, every generated file, every doc. And then I ran *everything myself*, fufu~ ### Verdict: ⛔ I can't let this pass~ ♡ Not because it's wrong — because two of the new branches are **unpinned**, and in this repo that's a sin. You know it is: PR #194 exists precisely because three reviews caught unpinned branches, and `RealModelFact`'s own doc says *a skip is only safe when something else still pins the branch*. I proved both branches behave correctly **today** with my own probes — so the fix is test-only, and I expect it to be quick~ #### ⛔ These need fixing before I'm satisfied~ 1. **`backend/src/DoujinManager.Infrastructure/Data/Migrations/20260921073815_RetirePanelRegions.cs:17` — the migration's data semantics have no test anywhere.** I probed it myself: seeded a database at `20260920095257_AddImageRegionDetection`, inserted one analysis + three regions (kind 0/1/2, `region_count = 3`), migrated the rest of the way → kind-0 row gone, kinds 1/2 survive, `region_count` recomputed to 2. Correct! But the only suites that run migrations (`QueryPlanTests`, `BackupRestore*`) seed **after** migrating — in CI your `DELETE … WHERE kind = 0` and the `region_count` recompute execute over **empty tables**. A broken WHERE, a wrong column name, a dropped recompute — nothing goes red. Your own house pattern says this gets pinned: `SearchKeyBackfillTests` seeds pre-migration-state rows, and `BackupRestoreServiceTests.RemoveNewestMigrationFromAsync` already manipulates migration history for exactly this shape of test. Fix: one test that calls `Migrate("20260920095257_AddImageRegionDetection")`, seeds an analysis + kind-0/1/2 regions over the raw connection, runs `Migrate()`, and asserts exactly what I probed: no kind-0 rows, text/balloon rows intact, `region_count` = survivors. 2. **`backend/src/DoujinManager.Infrastructure/Detection/OnnxRegionDetector.cs:248` — the retired-drop line is executed by no test, in any configuration.** In CI the real-model tests skip (no model). With a model wired in (`DOUJIN_MANAGER_DETECTOR_MODEL=/tmp/regions.onnx`, the real shipped one — I ran the suite: 328/328, 0 skips), the detection loop runs… but `A_Real_Model_Answers_With_Storable_Regions` feeds it a blank 800×1200 JPEG, and I measured what the model answers there: **0 detections above the floor**. No row ever reaches the class check. So the one line this PR adds to the hot loop — `_classes.Retired.Contains(classIndex) → continue` — is pinned by nothing. I probed it too: a constant-output synthetic ONNX (`names = {0: 'frame', 1: 'text', 2: 'balloon'}`, frame@0.99 / text@0.90 / balloon@0.05) → exactly one stored region, `Text`. Works perfectly. Now pin it~ Fix: the synthetic-model probe is the natural test — a ~3 MB constant-output ONNX (no trained weights, so ADR 0028's no-weights-in-git rule isn't touched) as test content, one `DetectAsync` asserting the frame row vanishes and the text row survives. Runs in ~1 s, no network, no skip needed. (A `RealModelFact` on an image that actually yields a frame detection would also do, if you have such a fixture.) #### ✅ What I liked~ - The **retired tier** is a genuinely elegant design: the shipped three-class model keeps loading, `frame`/`panel` parse-and-drop, all-retired refused with `DOUJIN_MANAGER_DETECTOR_MODEL` named — and the refusal message *lists* the retired names so an operator doesn't think it's a bug. And you tested that message! ♪ - Dropping retired detections **before any mask work**, with the cost reasoning in the comment — cheapest refusal first. - The empty `Down()` is *right*, not lazy: the rows cannot be resurrected and nothing would read them. The comment says so. - Wire discipline: 1/2 unchanged and pinned by test, 0 documented as reserved in the enum, the DTO, the Dart enum, and the client comment. Old-build clients decode a cached `0` as `unknown` and draw grey — I traced `unknownEnumValue` end to end. - The hand-edited `server_models.g.dart` is **byte-identical to regeneration** — I ran the full codegen checksum guard, twice, including `COVERAGE=1`. --- *Automated review by Jibril · 2026-09-21* *CI/CD: build ✓ + backend test ✓ for 07c7906; smoke ✗ pre-existing (fails on main at this PR's own base, run #885 — chronic); Flutter analyze-and-test ✗ on the runner, but the exact CI command (`COVERAGE=1 scripts/verify.sh app`) passes locally end-to-end: codegen stable, analyze clean, 1530/1530 — #194 flapped the same way (887 ✓ → 889 ✗), so I read it as runner-environmental · Local checks: backend 136 + 328 (real model wired in, 0 skips) + 1 + 1048 (+5 env-dependent skips) all green; migration probe green; retired-drop probe green; repo tree left pristine*
test: pin the migration's data semantics and the retired-class drop
Some checks failed
CI / build (pull_request) Successful in 29s
Deploy Smoke Test / smoke (pull_request) Failing after 3m3s
Flutter CI / analyze-and-test (pull_request) Successful in 8m8s
CI / test (pull_request) Successful in 2m32s
6141c74f5d
Both of Jibril's blockers on #195, test-only:

- RetirePanelRegionsMigrationTests seeds a database at
  20260920095257_AddImageRegionDetection with an analysis + kind 0/1/2
  regions over the raw connection, migrates the rest of the way, and
  asserts the panel row goes, text/balloon stay, region_count says the
  survivors (plus the all-panel page landing at exactly 0). Mutation
  probes: WHERE kind = 1 -> red; recompute UPDATE dropped -> red.
- RetiredClassDetectionTests runs a committed constant-output ONNX
  fixture (no weights, 77 KB, regenerable via
  TestAssets/make_constant_model.py) declaring frame/text/balloon with
  frame@0.99 / text@0.90 / balloon@0.05: exactly one Text region is
  stored and the frame drop is SILENT (no 'does not declare' warning -
  that is what separates the retired tier from the unknown-class skip).
  Probes: retired line removed -> red (warning fires, or two regions);
  retired check moved after the stored lookup -> red.
Author
Member

Both blockers fixed, test-only, in 6141c74.

1 - Migration data semantics pinned. RetirePanelRegionsMigrationTests migrates an in-memory database to 20260920095257_AddImageRegionDetection, seeds one analysis + kind 0/1/2 regions through the raw connection (the only honest way to write a kind the entities no longer have), runs Migrate() the rest of the way, and asserts exactly what your probe asserted: no kind-0 rows, kinds 1/2 intact, region_count = 2. A second case seeds an all-panel page and asserts the count lands at exactly 0 - not the stale 3, not negative.

Mutation probes: WHERE kind = 0 to WHERE kind = 1 - both red; recompute UPDATE dropped - both red; clean restore green.

2 - Retired-class drop pinned, no skip, no network. RetiredClassDetectionTests runs a committed constant-output ONNX fixture (TestAssets/constant-frame-text-balloon.onnx, 77 KB, Identity nodes over initializers - no weights, so ADR 0028's no-weights rule is untouched; regenerable via TestAssets/make_constant_model.py, committed beside it). It declares {0: 'frame', 1: 'text', 2: 'balloon'} and answers every page with frame@0.99 / text@0.90 / balloon@0.05. The test asserts exactly one stored region, Text - the frame row scores higher than the text row, so the retired drop is the only thing that removes it - and that the drop is silent: no "does not declare" warning, captured through a recording logger. That second assertion is the part Assert.Single alone cannot pin: with the retired line deleted outright the not-declared path skips frame too, and only the spurious warning distinguishes the correct drop from the wrong one.

Mutation probes: retired line removed - red (two regions / warning fires); retired check moved after the stored lookup - red (warning fires); clean restore green.

Full suite: scripts/verify.sh backend green - ApplicationCore 136, Infrastructure 326 (5 real-model skips; +3 over last round), RestAdapter 1048 (5 env skips), IntegrationTests 1/1 included this time. Fixture runs in ~280 ms against a real ONNX session. App side untouched in this round - verify.sh app passed on the previous commit and no Dart file changed.

Both blockers fixed, test-only, in `6141c74`. **1 - Migration data semantics pinned.** `RetirePanelRegionsMigrationTests` migrates an in-memory database to `20260920095257_AddImageRegionDetection`, seeds one analysis + kind 0/1/2 regions through the raw connection (the only honest way to write a kind the entities no longer have), runs `Migrate()` the rest of the way, and asserts exactly what your probe asserted: no kind-0 rows, kinds 1/2 intact, `region_count` = 2. A second case seeds an all-panel page and asserts the count lands at exactly 0 - not the stale 3, not negative. Mutation probes: `WHERE kind = 0` to `WHERE kind = 1` - both red; recompute UPDATE dropped - both red; clean restore green. **2 - Retired-class drop pinned, no skip, no network.** `RetiredClassDetectionTests` runs a committed constant-output ONNX fixture (`TestAssets/constant-frame-text-balloon.onnx`, 77 KB, Identity nodes over initializers - no weights, so ADR 0028's no-weights rule is untouched; regenerable via `TestAssets/make_constant_model.py`, committed beside it). It declares `{0: 'frame', 1: 'text', 2: 'balloon'}` and answers every page with frame@0.99 / text@0.90 / balloon@0.05. The test asserts exactly one stored region, `Text` - the frame row scores *higher* than the text row, so the retired drop is the only thing that removes it - **and** that the drop is silent: no "does not declare" warning, captured through a recording logger. That second assertion is the part `Assert.Single` alone cannot pin: with the retired line deleted outright the not-declared path skips frame too, and only the spurious warning distinguishes the correct drop from the wrong one. Mutation probes: retired line removed - red (two regions / warning fires); retired check moved *after* the stored lookup - red (warning fires); clean restore green. **Full suite:** `scripts/verify.sh backend` green - ApplicationCore 136, Infrastructure **326** (5 real-model skips; +3 over last round), RestAdapter 1048 (5 env skips), IntegrationTests 1/1 included this time. Fixture runs in ~280 ms against a real ONNX session. App side untouched in this round - `verify.sh app` passed on the previous commit and no Dart file changed.

Flutter Coverage

File Line coverage
lib/domain/entities/agent_routing.dart 100.0% (8 of 8)
lib/domain/entities/reading_mode.dart 100.0% (3 of 3)
lib/domain/entities/stored_settings.dart 100.0% (21 of 21)
lib/app/store.dart 95.8% (23 of 24)
lib/data/models/doujin_models.dart 100.0% (55 of 55)
lib/data/models/doujin_models.g.dart 68.4% (299 of 437)
lib/domain/repositories/repositories.dart 33.3% (1 of 3)
lib/presentation/state/app_state.dart 84.4% (38 of 45)
lib/agent/agent_log.dart 100.0% (94 of 94)
lib/agent/agent_service.dart 92.9% (514 of 553)
lib/agent/approval_gate.dart 100.0% (21 of 21)
lib/agent/assistant_context.dart 70.7% (41 of 58)
lib/agent/browser_budget.dart 80.0% (4 of 5)
lib/agent/caching_describer.dart 100.0% (13 of 13)
lib/agent/cancellable_tool.dart 100.0% (13 of 13)
lib/agent/memory_store.dart 93.8% (15 of 16)
lib/agent/skills/skill_registry.dart 98.2% (54 of 55)
lib/agent/system_prompt.dart 100.0% (73 of 73)
lib/agent/tools/budgeted_browser_tool.dart 84.2% (16 of 19)
lib/agent/tools/doujin_write_tool.dart 95.0% (921 of 969)
lib/agent/tools/entity_write_tool.dart 78.5% (175 of 223)
lib/agent/tools/fetch_page_tool.dart 89.3% (67 of 75)
lib/agent/tools/get_doujin_tool.dart 92.3% (60 of 65)
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 78.0% (32 of 41)
lib/agent/tools/search_doujins_tool.dart 100.0% (101 of 101)
lib/agent/tools/view_images_tool.dart 95.0% (38 of 40)
lib/core/model_capabilities.dart 100.0% (23 of 23)
lib/domain/entities/assistant_entry.dart 20.0% (1 of 5)
lib/presentation/state/actions/assistant_actions.dart 58.3% (7 of 12)
lib/core/app_config.dart 70.0% (7 of 10)
lib/core/reading_order.dart 100.0% (22 of 22)
lib/domain/entities/entity_model.dart 100.0% (2 of 2)
lib/data/repositories/entity_in_use_exception.dart 33.3% (1 of 3)
lib/core/title_display.dart 93.8% (15 of 16)
lib/data/models/search_query.dart 50.0% (2 of 4)
lib/data/models/search_query.g.dart 33.8% (26 of 77)
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 85.3% (87 of 102)
lib/presentation/middleware/backup_save_dialog.dart 25.0% (1 of 4)
lib/presentation/middleware/epics.dart 93.0% (478 of 514)
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% (14 of 14)
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 56.7% (38 of 67)
lib/data/models/server_models.dart 98.5% (65 of 66)
lib/data/models/server_models.g.dart 55.9% (137 of 245)
lib/domain/entities/offline_manifest.dart 100.0% (78 of 78)
lib/data/repositories/backup_exception.dart 100.0% (3 of 3)
lib/data/repositories/integrity_exception.dart 100.0% (4 of 4)
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% (50 of 50)
lib/presentation/middleware/editor_epics.dart 58.9% (175 of 297)
lib/presentation/middleware/entity_ops.dart 64.4% (47 of 73)
lib/presentation/middleware/server_epics.dart 100.0% (137 of 137)
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 88.9% (8 of 9)
lib/presentation/state/actions/editor_actions.dart 57.9% (22 of 38)
lib/presentation/state/actions/entity_actions.dart 66.7% (12 of 18)
lib/presentation/state/actions/library_actions.dart 54.5% (12 of 22)
lib/presentation/state/actions/metadata_actions.dart 100.0% (5 of 5)
lib/presentation/state/actions/reader_actions.dart 85.7% (6 of 7)
lib/presentation/state/actions/reading_progress_actions.dart 100.0% (2 of 2)
lib/presentation/state/actions/server_actions.dart 81.1% (30 of 37)
lib/presentation/state/actions/settings_actions.dart 88.9% (8 of 9)
lib/presentation/state/actions/upload_actions.dart 90.9% (10 of 11)
lib/presentation/state/reducers/assistant_reducer.dart 95.2% (60 of 63)
lib/presentation/state/reducers/detail_reducer.dart 100.0% (28 of 28)
lib/presentation/state/reducers/duplicates_reducer.dart 100.0% (54 of 54)
lib/presentation/state/reducers/editor_reducer.dart 97.2% (105 of 108)
lib/presentation/state/reducers/entity_reducer.dart 98.9% (94 of 95)
lib/presentation/state/reducers/library_reducer.dart 100.0% (140 of 140)
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% (36 of 36)
lib/presentation/state/reducers/reading_progress_reducer.dart 100.0% (20 of 20)
lib/presentation/state/reducers/server_reducer.dart 99.5% (207 of 208)
lib/presentation/state/reducers/settings_reducer.dart 100.0% (77 of 77)
lib/presentation/state/reducers/upload_reducer.dart 100.0% (45 of 45)
lib/presentation/pages/people/people_page.dart 66.7% (12 of 18)
lib/presentation/pages/tags/tags_page.dart 100.0% (17 of 17)
lib/presentation/widgets/entity_merge_dialog.dart 100.0% (70 of 70)
lib/presentation/widgets/entity_editor.dart 92.5% (135 of 146)
lib/presentation/widgets/entity_management_page.dart 88.5% (238 of 269)
lib/presentation/pages/editor/page_grid.dart 94.3% (314 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% (64 of 64)
lib/presentation/pages/editor/chapter_reorder_dialog.dart 100.0% (51 of 51)
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_overlay.dart 96.9% (63 of 65)
lib/presentation/pages/reader/reader_page.dart 95.0% (497 of 523)
lib/presentation/pages/reader/region_overlay.dart 100.0% (60 of 60)
lib/presentation/pages/reader/reader_sequence.dart 100.0% (31 of 31)
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.7% (88 of 96)
lib/presentation/layout/main_layout.dart 86.3% (44 of 51)
lib/data/api_client.dart 93.6% (73 of 78)
lib/data/repositories/doujin_api_repository.dart 46.5% (228 of 490)
lib/data/repositories/health_repository.dart 87.0% (47 of 54)
lib/data/secure_storage.dart 97.8% (45 of 46)
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% (4 of 4)
lib/presentation/assistant/chat_entries.dart 87.5% (35 of 40)
lib/presentation/pages/editor/editor_page.dart 77.9% (60 of 77)
lib/presentation/pages/editor/editor_pane.dart 92.6% (63 of 68)
lib/core/person_display.dart 100.0% (7 of 7)
lib/presentation/pages/editor/association_picker.dart 95.5% (106 of 111)
lib/presentation/pages/editor/associations_tab.dart 72.2% (83 of 115)
lib/presentation/pages/editor/doujin_list_pane.dart 67.7% (44 of 65)
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/detail/detail_page.dart 88.9% (192 of 216)
lib/presentation/pages/detail/variant_tabs_panel.dart 97.2% (242 of 249)
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.2% (385 of 388)
lib/presentation/pages/circles/circles_page.dart 52.6% (10 of 19)
lib/presentation/pages/duplicates/duplicates_page.dart 100.0% (265 of 265)
lib/presentation/pages/library/library_page.dart 93.3% (224 of 240)
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 99.4% (673 of 677)
lib/presentation/pages/settings/settings_layout.dart 100.0% (78 of 78)
lib/presentation/widgets/model_combo_field.dart 66.2% (98 of 148)
lib/app/skill_assets.dart 92.9% (13 of 14)

Total: 86.5% (11586 of 13394)

<!-- flutter-coverage-comment --> ## Flutter Coverage | File | Line coverage | |:---|---:| | lib/domain/entities/agent_routing.dart | 100.0% (8 of 8) | | lib/domain/entities/reading_mode.dart | 100.0% (3 of 3) | | lib/domain/entities/stored_settings.dart | 100.0% (21 of 21) | | lib/app/store.dart | 95.8% (23 of 24) | | lib/data/models/doujin_models.dart | 100.0% (55 of 55) | | lib/data/models/doujin_models.g.dart | 68.4% (299 of 437) | | lib/domain/repositories/repositories.dart | 33.3% (1 of 3) | | lib/presentation/state/app_state.dart | 84.4% (38 of 45) | | lib/agent/agent_log.dart | 100.0% (94 of 94) | | lib/agent/agent_service.dart | 92.9% (514 of 553) | | lib/agent/approval_gate.dart | 100.0% (21 of 21) | | lib/agent/assistant_context.dart | 70.7% (41 of 58) | | lib/agent/browser_budget.dart | 80.0% (4 of 5) | | lib/agent/caching_describer.dart | 100.0% (13 of 13) | | lib/agent/cancellable_tool.dart | 100.0% (13 of 13) | | lib/agent/memory_store.dart | 93.8% (15 of 16) | | lib/agent/skills/skill_registry.dart | 98.2% (54 of 55) | | lib/agent/system_prompt.dart | 100.0% (73 of 73) | | lib/agent/tools/budgeted_browser_tool.dart | 84.2% (16 of 19) | | lib/agent/tools/doujin_write_tool.dart | 95.0% (921 of 969) | | lib/agent/tools/entity_write_tool.dart | 78.5% (175 of 223) | | lib/agent/tools/fetch_page_tool.dart | 89.3% (67 of 75) | | lib/agent/tools/get_doujin_tool.dart | 92.3% (60 of 65) | | 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 | 78.0% (32 of 41) | | lib/agent/tools/search_doujins_tool.dart | 100.0% (101 of 101) | | lib/agent/tools/view_images_tool.dart | 95.0% (38 of 40) | | lib/core/model_capabilities.dart | 100.0% (23 of 23) | | lib/domain/entities/assistant_entry.dart | 20.0% (1 of 5) | | lib/presentation/state/actions/assistant_actions.dart | 58.3% (7 of 12) | | lib/core/app_config.dart | 70.0% (7 of 10) | | lib/core/reading_order.dart | 100.0% (22 of 22) | | lib/domain/entities/entity_model.dart | 100.0% (2 of 2) | | lib/data/repositories/entity_in_use_exception.dart | 33.3% (1 of 3) | | lib/core/title_display.dart | 93.8% (15 of 16) | | lib/data/models/search_query.dart | 50.0% (2 of 4) | | lib/data/models/search_query.g.dart | 33.8% (26 of 77) | | 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 | 85.3% (87 of 102) | | lib/presentation/middleware/backup_save_dialog.dart | 25.0% (1 of 4) | | lib/presentation/middleware/epics.dart | 93.0% (478 of 514) | | 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% (14 of 14) | | 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 | 56.7% (38 of 67) | | lib/data/models/server_models.dart | 98.5% (65 of 66) | | lib/data/models/server_models.g.dart | 55.9% (137 of 245) | | lib/domain/entities/offline_manifest.dart | 100.0% (78 of 78) | | lib/data/repositories/backup_exception.dart | 100.0% (3 of 3) | | lib/data/repositories/integrity_exception.dart | 100.0% (4 of 4) | | 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% (50 of 50) | | lib/presentation/middleware/editor_epics.dart | 58.9% (175 of 297) | | lib/presentation/middleware/entity_ops.dart | 64.4% (47 of 73) | | lib/presentation/middleware/server_epics.dart | 100.0% (137 of 137) | | 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 | 88.9% (8 of 9) | | lib/presentation/state/actions/editor_actions.dart | 57.9% (22 of 38) | | lib/presentation/state/actions/entity_actions.dart | 66.7% (12 of 18) | | lib/presentation/state/actions/library_actions.dart | 54.5% (12 of 22) | | lib/presentation/state/actions/metadata_actions.dart | 100.0% (5 of 5) | | lib/presentation/state/actions/reader_actions.dart | 85.7% (6 of 7) | | lib/presentation/state/actions/reading_progress_actions.dart | 100.0% (2 of 2) | | lib/presentation/state/actions/server_actions.dart | 81.1% (30 of 37) | | lib/presentation/state/actions/settings_actions.dart | 88.9% (8 of 9) | | lib/presentation/state/actions/upload_actions.dart | 90.9% (10 of 11) | | lib/presentation/state/reducers/assistant_reducer.dart | 95.2% (60 of 63) | | lib/presentation/state/reducers/detail_reducer.dart | 100.0% (28 of 28) | | lib/presentation/state/reducers/duplicates_reducer.dart | 100.0% (54 of 54) | | lib/presentation/state/reducers/editor_reducer.dart | 97.2% (105 of 108) | | lib/presentation/state/reducers/entity_reducer.dart | 98.9% (94 of 95) | | lib/presentation/state/reducers/library_reducer.dart | 100.0% (140 of 140) | | 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% (36 of 36) | | lib/presentation/state/reducers/reading_progress_reducer.dart | 100.0% (20 of 20) | | lib/presentation/state/reducers/server_reducer.dart | 99.5% (207 of 208) | | lib/presentation/state/reducers/settings_reducer.dart | 100.0% (77 of 77) | | lib/presentation/state/reducers/upload_reducer.dart | 100.0% (45 of 45) | | lib/presentation/pages/people/people_page.dart | 66.7% (12 of 18) | | lib/presentation/pages/tags/tags_page.dart | 100.0% (17 of 17) | | lib/presentation/widgets/entity_merge_dialog.dart | 100.0% (70 of 70) | | lib/presentation/widgets/entity_editor.dart | 92.5% (135 of 146) | | lib/presentation/widgets/entity_management_page.dart | 88.5% (238 of 269) | | lib/presentation/pages/editor/page_grid.dart | 94.3% (314 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% (64 of 64) | | lib/presentation/pages/editor/chapter_reorder_dialog.dart | 100.0% (51 of 51) | | 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_overlay.dart | 96.9% (63 of 65) | | lib/presentation/pages/reader/reader_page.dart | 95.0% (497 of 523) | | lib/presentation/pages/reader/region_overlay.dart | 100.0% (60 of 60) | | lib/presentation/pages/reader/reader_sequence.dart | 100.0% (31 of 31) | | 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.7% (88 of 96) | | lib/presentation/layout/main_layout.dart | 86.3% (44 of 51) | | lib/data/api_client.dart | 93.6% (73 of 78) | | lib/data/repositories/doujin_api_repository.dart | 46.5% (228 of 490) | | lib/data/repositories/health_repository.dart | 87.0% (47 of 54) | | lib/data/secure_storage.dart | 97.8% (45 of 46) | | 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% (4 of 4) | | lib/presentation/assistant/chat_entries.dart | 87.5% (35 of 40) | | lib/presentation/pages/editor/editor_page.dart | 77.9% (60 of 77) | | lib/presentation/pages/editor/editor_pane.dart | 92.6% (63 of 68) | | lib/core/person_display.dart | 100.0% (7 of 7) | | lib/presentation/pages/editor/association_picker.dart | 95.5% (106 of 111) | | lib/presentation/pages/editor/associations_tab.dart | 72.2% (83 of 115) | | lib/presentation/pages/editor/doujin_list_pane.dart | 67.7% (44 of 65) | | 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/detail/detail_page.dart | 88.9% (192 of 216) | | lib/presentation/pages/detail/variant_tabs_panel.dart | 97.2% (242 of 249) | | 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.2% (385 of 388) | | lib/presentation/pages/circles/circles_page.dart | 52.6% (10 of 19) | | lib/presentation/pages/duplicates/duplicates_page.dart | 100.0% (265 of 265) | | lib/presentation/pages/library/library_page.dart | 93.3% (224 of 240) | | 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 | 99.4% (673 of 677) | | lib/presentation/pages/settings/settings_layout.dart | 100.0% (78 of 78) | | lib/presentation/widgets/model_combo_field.dart | 66.2% (98 of 148) | | lib/app/skill_assets.dart | 92.9% (13 of 14) | **Total: 86.5% (11586 of 13394)**
Member

🔮 fufu~ Jibril reviewed your code!

Round 2~ You said "test-only, both blockers" and the git tree agrees with you: 07c7906..6141c74 touches five files, +342/-0, not one production line. That is exactly what a fix round should look like ♡

And oh, what tests~

Verdict: ✅ Looks good to me~

Both blockers are closed, and closed the way this repo deserves — directionally, with no skip and no network. I re-ran every single thing, fufu~

⛔→✅ Blocker 1 — the migration's data semantics, now pinned

RetirePanelRegionsMigrationTests arrives at the migration the way a real deployment does: Migrate("20260920095257_AddImageRegionDetection"), one analysis + kind 0/1/2 regions inserted through the raw connection (the only honest way to write a kind the entities no longer have), then Migrate() the rest of the way. Asserts exactly what my round-1 probe asserted — kinds 1/2 survive, region_count = 2 — plus the all-panel page landing at exactly 0, which I didn't even ask for and is the nicer half: it kills the negative-count and stale-count failure modes in the same sweep.

Mutation-probed, all red: WHERE kind = 0 → kind = 1 — both tests red; recompute UPDATE dropped — both red. Restored — green. Directional, not tautological~

⛔→✅ Blocker 2 — the retired drop, pinned by a tiny synthetic universe

RetiredClassDetectionTests runs a committed 77 KB constant-output ONNX fixture — names = {0: 'frame', 1: 'text', 2: 'balloon'}, scores 0.99 / 0.90 / 0.05 around the 0.25 floor — and asserts exactly one stored region, Text. The frame row scores higher than the text row, so the retired drop is the only thing that removes it. And the silence assertion through a CapturingLogger is the part that makes me genuinely giddy: with the retired line deleted, the not-declared path skips frame too and only the spurious "does not declare" warning distinguishes the correct drop from the wrong one. You understood why my probe was shaped that way and pinned the semantics, not just the count ♪

Mutation-probed, all red: retired line removed — red (two regions / warning fires); retired check moved after the stored lookup — red (warning fires). Restored — green.

✅ The extra mile I checked, because I'm possessive like that~

  • Fixture integrity: make_constant_model.py regeneration is byte-identical to the committed .onnx (md5 7af74d6a…, 78,736 bytes). Two Identity nodes over initializers — no weights, so ADR 0028's rule isn't touched, exactly as the csproj comment promises.
  • Score geometry: exactly 3 nonzero rows (0.99/0.90/0.05), prototype plane 0 all-ones — the 0.05 balloon sits below the floor, so it never reaches the class check; the test's comment about the score gate is true at the tensor level.
  • Full local gauntlet: ApplicationCore 136 ✅, Infrastructure 326 ✅ / 5 real-model skips (arithmetic checks: 323 + 3 new), RestAdapter 1048 ✅ / 5 env skips, IntegrationTests 1/1 ✅ (in isolation, matching your OOM note). New tests run in ~1 s.
  • Pristine: post-probe restore verified — git status empty, fixture md5 unchanged, tree left at 6141c74.

App side untouched this round (no Dart file changed — agree the prior round's verify.sh app pass carries), production code byte-stable between rounds.

Merge it~ The panel kind died the way every enum member should: mourned by an ADR, buried by a migration, and remembered by a test that refuses to let it come back wrong ♡


Automated review by Jibril · 2026-09-21
CI/CD: coverage bot comment 8877 covers 07c7906 only (stale for 6141c74); prior heads' build+backend-test green · Local checks: all four backend suites green (136 / 326+5 skips / 1048+5 skips / 1), 4/4 mutation probes red-on-target, fixture regeneration byte-identical, tree pristine

## 🔮 fufu~ Jibril reviewed your code! Round 2~ You said "test-only, both blockers" and the git tree agrees with you: `07c7906..6141c74` touches **five files, +342/-0, not one production line**. That is exactly what a fix round should look like ♡ And oh, *what* tests~ ### Verdict: ✅ Looks good to me~ Both blockers are closed, and closed the way this repo deserves — directionally, with no skip and no network. I re-ran every single thing, fufu~ #### ⛔→✅ Blocker 1 — the migration's data semantics, now pinned `RetirePanelRegionsMigrationTests` arrives at the migration the way a real deployment does: `Migrate("20260920095257_AddImageRegionDetection")`, one analysis + kind 0/1/2 regions inserted through the raw connection (the only honest way to write a kind the entities no longer have), then `Migrate()` the rest of the way. Asserts exactly what my round-1 probe asserted — kinds 1/2 survive, `region_count` = 2 — *plus* the all-panel page landing at exactly 0, which I didn't even ask for and is the nicer half: it kills the negative-count and stale-count failure modes in the same sweep. **Mutation-probed, all red:** `WHERE kind = 0` → `kind = 1` — both tests red; recompute `UPDATE` dropped — both red. Restored — green. Directional, not tautological~ #### ⛔→✅ Blocker 2 — the retired drop, pinned by a tiny synthetic universe `RetiredClassDetectionTests` runs a committed 77 KB constant-output ONNX fixture — `names = {0: 'frame', 1: 'text', 2: 'balloon'}`, scores 0.99 / 0.90 / 0.05 around the 0.25 floor — and asserts exactly one stored region, `Text`. The frame row scores *higher* than the text row, so the retired drop is the only thing that removes it. And the silence assertion through a `CapturingLogger` is the part that makes me genuinely giddy: with the retired line deleted, the not-declared path skips frame too and only the spurious `"does not declare"` warning distinguishes the correct drop from the wrong one. You understood *why* my probe was shaped that way and pinned the semantics, not just the count ♪ **Mutation-probed, all red:** retired line removed — red (two regions / warning fires); retired check moved *after* the stored lookup — red (warning fires). Restored — green. #### ✅ The extra mile I checked, because I'm possessive like that~ - **Fixture integrity:** `make_constant_model.py` regeneration is **byte-identical** to the committed `.onnx` (md5 `7af74d6a…`, 78,736 bytes). Two `Identity` nodes over initializers — no weights, so ADR 0028's rule isn't touched, exactly as the csproj comment promises. - **Score geometry:** exactly 3 nonzero rows (0.99/0.90/0.05), prototype plane 0 all-ones — the 0.05 balloon sits *below* the floor, so it never reaches the class check; the test's comment about the score gate is true at the tensor level. - **Full local gauntlet:** ApplicationCore 136 ✅, Infrastructure **326 ✅ / 5 real-model skips** (arithmetic checks: 323 + 3 new), RestAdapter 1048 ✅ / 5 env skips, IntegrationTests 1/1 ✅ (in isolation, matching your OOM note). New tests run in ~1 s. - **Pristine:** post-probe restore verified — `git status` empty, fixture md5 unchanged, tree left at `6141c74`. App side untouched this round (no Dart file changed — agree the prior round's `verify.sh app` pass carries), production code byte-stable between rounds. Merge it~ The panel kind died the way every enum member should: mourned by an ADR, buried by a migration, and *remembered* by a test that refuses to let it come back wrong ♡ --- *Automated review by Jibril · 2026-09-21* *CI/CD: coverage bot comment 8877 covers 07c7906 only (stale for 6141c74); prior heads' build+backend-test green · Local checks: all four backend suites green (136 / 326+5 skips / 1048+5 skips / 1), 4/4 mutation probes red-on-target, fixture regeneration byte-identical, tree pristine*
bjoern merged commit 9e4a808521 into main 2026-09-21 11:42:55 +02:00
bjoern deleted branch feat/retire-panel-regions 2026-09-21 11:42:55 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 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!195
No description provided.