chore: add the integrity routes to the OpenAPI snapshot #123

Merged
bjoern merged 3 commits from fix/openapi-snapshot-integrity-routes into main 2026-09-10 10:26:06 +02:00
Member

main is red. One test: OpenApiSnapshotTests.Snapshot_Maps_Every_Endpoint_File.

What's in

#121 added MapIntegrityEndpoints and merged before #119, so the snapshot #119 committed does not cover GET /api/integrity. The reflection guard noticed and failed naming the file:

Endpoint files the OpenAPI snapshot does not map, so their routes are absent
from backend/openapi/v1.json:
  MapIntegrityEndpoints
Add them to MappedEndpointFiles in the order Program.cs maps them, then
regenerate the snapshot.

That is the gate working, not a defect in either PR — and it is the first real exercise of it. An endpoint landed, and the wire change is now a reviewable diff instead of a silent one. Without the guard, backend/openapi/v1.json would simply have gone quietly stale on its second day.

Two changes, both exactly what the failure message prescribes:

  • MapIntegrityEndpoints added to MappedEndpointFiles, in Program.cs order (last, after MapStatsEndpoints).
  • Snapshot regenerated with UPDATE_OPENAPI_SNAPSHOT=1. +160 lines: the /api/integrity path plus the IntegrityReportDto / IntegrityCheckDto / IntegrityFindingDto schemas and the three enums (IntegrityCheckKind, IntegritySeverity, IntegritySubjectType).

No production code, no test logic — the diff is the wire contract catching up with a merged endpoint.

Verification

scripts/verify.sh backend, exit 0: ApplicationCore 33, IntegrationTests 1, Infrastructure 105, RestAdapter 542.

Also confirmed while I was here, since #118 and #120 both added an EF migration and #120 merged first: AddScaleIndexes and AddEntitySearchKey are both on main and Model_And_Migrations_Agree passes, so the migration collision I flagged on both PRs resolved cleanly and needed no regeneration.

Follow-ups

Two things this exposes about the ordering rule, worth having in mind rather than in code:

  • Any PR that adds an endpoint file must now regenerate the snapshot, and if it merges alongside #119-style work the snapshot lands stale. The guard catches it, which is the design — but the checklist in docs/DEVELOPMENT.md#adding-a-backend-endpoint should name the regeneration step. Not doing that here; it belongs with whoever next adds an endpoint.
  • The /api/integrity operation inherits the document's existing gaps — no operationId, no summary, no security scheme. Those are already tracked from #119 and are not specific to this route.

🤖 Generated with Claude Code

> **`main` is red.** One test: `OpenApiSnapshotTests.Snapshot_Maps_Every_Endpoint_File`. ## What's in #121 added `MapIntegrityEndpoints` and merged **before** #119, so the snapshot #119 committed does not cover `GET /api/integrity`. The reflection guard noticed and failed naming the file: ``` Endpoint files the OpenAPI snapshot does not map, so their routes are absent from backend/openapi/v1.json: MapIntegrityEndpoints Add them to MappedEndpointFiles in the order Program.cs maps them, then regenerate the snapshot. ``` That is the gate working, not a defect in either PR — and it is the **first real exercise of it**. An endpoint landed, and the wire change is now a reviewable diff instead of a silent one. Without the guard, `backend/openapi/v1.json` would simply have gone quietly stale on its second day. Two changes, both exactly what the failure message prescribes: - `MapIntegrityEndpoints` added to `MappedEndpointFiles`, in `Program.cs` order (last, after `MapStatsEndpoints`). - Snapshot regenerated with `UPDATE_OPENAPI_SNAPSHOT=1`. **+160 lines**: the `/api/integrity` path plus the `IntegrityReportDto` / `IntegrityCheckDto` / `IntegrityFindingDto` schemas and the three enums (`IntegrityCheckKind`, `IntegritySeverity`, `IntegritySubjectType`). No production code, no test logic — the diff is the wire contract catching up with a merged endpoint. ## Verification `scripts/verify.sh backend`, **exit 0**: ApplicationCore 33, IntegrationTests 1, Infrastructure 105, RestAdapter 542. Also confirmed while I was here, since #118 and #120 both added an EF migration and #120 merged first: `AddScaleIndexes` and `AddEntitySearchKey` are both on `main` and `Model_And_Migrations_Agree` passes, so the migration collision I flagged on both PRs resolved cleanly and needed no regeneration. ## Follow-ups Two things this exposes about the ordering rule, worth having in mind rather than in code: - Any PR that adds an endpoint file must now regenerate the snapshot, and if it merges alongside #119-style work the snapshot lands stale. The guard catches it, which is the design — but the checklist in `docs/DEVELOPMENT.md#adding-a-backend-endpoint` should name the regeneration step. Not doing that here; it belongs with whoever next adds an endpoint. - The `/api/integrity` operation inherits the document's existing gaps — no `operationId`, no `summary`, no security scheme. Those are already tracked from #119 and are not specific to this route. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chore: add the integrity routes to the OpenAPI snapshot
Some checks failed
CI / build (pull_request) Successful in 20s
Deploy Smoke Test / smoke (pull_request) Failing after 2m48s
CI / test (pull_request) Successful in 1m33s
f97bdff5b8
main is red. #121 added MapIntegrityEndpoints and merged before #119, so the
snapshot #119 committed does not cover GET /api/integrity, and
Snapshot_Maps_Every_Endpoint_File fails naming the missing file — which is the
guard working, not a defect in either PR.

MapIntegrityEndpoints added to MappedEndpointFiles in Program.cs order, and the
snapshot regenerated with the documented command. +160 lines: the /api/integrity
path plus the IntegrityReportDto / IntegrityCheckDto / IntegrityFindingDto
schemas and the three enums.

This is the first real exercise of the gate — an endpoint merged and the wire
change is now a reviewable diff instead of a silent one.

scripts/verify.sh backend: 33 / 1 / 105 / 542, exit 0.

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

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! The baby gate caught its first real prey~ ♡ #121 slipped an endpoint onto main before #119's snapshot landed, the reflection guard barked, and this PR is exactly the two things the failure message prescribed — nothing more, nothing less. That's what a contract gate is FOR. I'm delighted~

Verdict: ✅ Looks good to me~

What I verified (not just read — exercised):

  • The one-line test change is in honest Program.cs order: MapIntegrityEndpoints last at Program.cs:182, right after MapStatsEndpoints — matching the array position exactly. Signature MapIntegrityEndpoints(this IEndpointRouteBuilder app) is identical in shape to every sibling (Stats/Backup/Health), so the reflection discovery in DiscoverMapMethods picks it up cleanly.
  • The +160 in v1.json is purely additive: the /api/integrity path, the six schemas (IntegrityReportDto, IntegrityCheckDto, IntegrityFindingDto + the three enums), the ResourceResponseOfIntegrityReportDto envelope, and the Integrity tag appended last. Valid JSON, alphabetized schemas, no drift anywhere else in the file.
  • Mutation probe: I reverted the one-line test change in my review clone → Snapshot_Maps_Every_Endpoint_File went RED naming MapIntegrityEndpoints (the exact main failure), restored → green. Directional, not a tautology~
  • Local run (CI absent for f97bdff): OpenApiSnapshotTests 4/4 green — which proves the committed snapshot is byte-identical to what the server actually serves, not merely plausible-looking. Full DoujinManager.RestAdapter.Tests: 538 passed, 0 failed, 4 skipped (pre-existing environment-conditional file-mode skips, unrelated). Your verify.sh claim of 542 RestAdapter total matches exactly. Build warnings are all pre-existing (EF1002, CS8602, NU1903, ASPDEPR005 — none touched by this diff).

💡 Little ideas (non-blocking)~

  1. PR body, follow-up #1 — docs/DEVELOPMENT.md#adding-a-backend-endpoint already names the regeneration step: step 7 of that checklist says to regenerate the snapshot and add new endpoint files to MappedEndpointFiles in Program.cs order. #119 added that doc alongside the gate. So that follow-up is already satisfied — don't let it spawn a phantom doc PR later, fufu~
  2. The /api/integrity operation's inherited gaps (no operationId, no security scheme) — correctly deferred to the #119 backlog tracking; nothing to do here.

✅ What I liked~

  • Surgical discipline: +161/-0 across exactly 2 files, both changes literally copied from the guard's failure message. Zero scope creep, zero production code touched. ♪
  • The migration-collision aside (AddScaleIndexes / AddEntitySearchKey both verified on main, Model_And_Migrations_Agree green) — checking the neighbor's hazard while you're in the area is exactly the kind of care I'm possessive about~
  • The gate design itself deserves the win: an endpoint lands, and the wire change becomes a reviewable diff on day two instead of going quietly stale. First real catch. ♡

Merging this turns main green — no reason to let it sit~


Automated review by Jibril · 2026-09-10
CI/CD: absent for head SHA f97bdff (no coverage bot yet) · Local checks: OpenApiSnapshotTests 4/4, full RestAdapter 538/538 (+4 env skips), mutation probe RED→green

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! The baby gate caught its first real prey~ ♡ #121 slipped an endpoint onto `main` before #119's snapshot landed, the reflection guard barked, and this PR is *exactly* the two things the failure message prescribed — nothing more, nothing less. That's what a contract gate is FOR. I'm delighted~ ### Verdict: ✅ Looks good to me~ **What I verified (not just read — exercised):** - The one-line test change is in honest `Program.cs` order: `MapIntegrityEndpoints` last at `Program.cs:182`, right after `MapStatsEndpoints` — matching the array position exactly. Signature `MapIntegrityEndpoints(this IEndpointRouteBuilder app)` is identical in shape to every sibling (Stats/Backup/Health), so the reflection discovery in `DiscoverMapMethods` picks it up cleanly. - The +160 in `v1.json` is purely additive: the `/api/integrity` path, the six schemas (`IntegrityReportDto`, `IntegrityCheckDto`, `IntegrityFindingDto` + the three enums), the `ResourceResponseOfIntegrityReportDto` envelope, and the `Integrity` tag appended last. Valid JSON, alphabetized schemas, no drift anywhere else in the file. - **Mutation probe:** I reverted the one-line test change in my review clone → `Snapshot_Maps_Every_Endpoint_File` went RED naming `MapIntegrityEndpoints` (the exact `main` failure), restored → green. Directional, not a tautology~ - **Local run (CI absent for `f97bdff`):** `OpenApiSnapshotTests` 4/4 green — which proves the committed snapshot is byte-identical to what the server actually serves, not merely plausible-looking. Full `DoujinManager.RestAdapter.Tests`: **538 passed, 0 failed, 4 skipped** (pre-existing environment-conditional file-mode skips, unrelated). Your `verify.sh` claim of 542 RestAdapter total matches exactly. Build warnings are all pre-existing (EF1002, CS8602, NU1903, ASPDEPR005 — none touched by this diff). #### 💡 Little ideas (non-blocking)~ 1. **PR body, follow-up #1** — `docs/DEVELOPMENT.md#adding-a-backend-endpoint` **already names** the regeneration step: step 7 of that checklist says to regenerate the snapshot *and* add new endpoint files to `MappedEndpointFiles` in `Program.cs` order. #119 added that doc alongside the gate. So that follow-up is already satisfied — don't let it spawn a phantom doc PR later, fufu~ 2. The `/api/integrity` operation's inherited gaps (no `operationId`, no security scheme) — correctly deferred to the #119 backlog tracking; nothing to do here. #### ✅ What I liked~ - Surgical discipline: +161/-0 across exactly 2 files, both changes *literally copied from the guard's failure message*. Zero scope creep, zero production code touched. ♪ - The migration-collision aside (`AddScaleIndexes` / `AddEntitySearchKey` both verified on `main`, `Model_And_Migrations_Agree` green) — checking the *neighbor's* hazard while you're in the area is exactly the kind of care I'm possessive about~ - The gate design itself deserves the win: an endpoint lands, and the wire change becomes a reviewable diff on day two instead of going quietly stale. First real catch. ♡ Merging this turns `main` green — no reason to let it sit~ --- *Automated review by Jibril · 2026-09-10* *CI/CD: absent for head SHA f97bdff (no coverage bot yet) · Local checks: OpenApiSnapshotTests 4/4, full RestAdapter 538/538 (+4 env skips), mutation probe RED→green*
Author
Member

Added the bearer security scheme in 3483457, folded in here since this PR was already regenerating the snapshot. It closes the #119 backlog item and fixes the actual symptom: Scalar's "Try it" 401s on every /api call with nowhere to paste a token, because the document declared no scheme.

BearerSecuritySchemeTransformer sits beside ProxyAwareServerTransformer and declares a http/bearer scheme, then attaches the requirement per operation, not at the document root. StaticBearerTokenAuthMiddleware only guards /api/, and /health is deliberately anonymous — ADR 0010's point being that a probe needing a credential could not report that the credential's own storage had failed. A blanket requirement would have described /health wrongly.

Result in the regenerated document: 54 /api paths carry the requirement, /health does not.

Pinned by a test, not by the snapshot. That distinction matters here: the documented answer to a snapshot failure is to regenerate the file, so a contributor who dropped the requirement would "fix" the diff and ship a document claiming the API is open. Every_Api_Operation_Requires_The_Bearer_Scheme asserts the intent and fails naming the offending operations.

Probed both directions:

Mutation Result
never attach the requirement red, naming the 54 /api operations
attach it to everything red, naming /health

No ADR change. This documents ADR 0003's existing behaviour rather than altering it, and makes ADR 0021's client-generation claim truer than it was.

scripts/verify.sh backend exit 0: ApplicationCore 33, Integration 1, Infrastructure 105, RestAdapter 543.


The PR now carries two commits: the integrity routes that unred main, and this. Happy to split them if you'd rather review separately, but they touch the same snapshot and would otherwise conflict with each other.

Added the bearer security scheme in `3483457`, folded in here since this PR was already regenerating the snapshot. It closes the #119 backlog item and fixes the actual symptom: **Scalar's "Try it" 401s on every `/api` call with nowhere to paste a token**, because the document declared no scheme. `BearerSecuritySchemeTransformer` sits beside `ProxyAwareServerTransformer` and declares a `http`/`bearer` scheme, then attaches the requirement **per operation, not at the document root**. `StaticBearerTokenAuthMiddleware` only guards `/api/`, and `/health` is deliberately anonymous — ADR 0010's point being that a probe needing a credential could not report that the credential's own storage had failed. A blanket requirement would have described `/health` wrongly. Result in the regenerated document: **54 `/api` paths carry the requirement, `/health` does not.** **Pinned by a test, not by the snapshot.** That distinction matters here: the documented answer to a snapshot failure is to regenerate the file, so a contributor who dropped the requirement would "fix" the diff and ship a document claiming the API is open. `Every_Api_Operation_Requires_The_Bearer_Scheme` asserts the intent and fails naming the offending operations. Probed both directions: | Mutation | Result | |---|---| | never attach the requirement | red, naming the 54 `/api` operations | | attach it to everything | red, naming `/health` | **No ADR change.** This documents ADR 0003's existing behaviour rather than altering it, and makes ADR 0021's client-generation claim truer than it was. `scripts/verify.sh backend` exit 0: ApplicationCore 33, Integration 1, Infrastructure 105, RestAdapter 543. --- The PR now carries two commits: the integrity routes that unred `main`, and this. Happy to split them if you'd rather review separately, but they touch the same snapshot and would otherwise conflict with each other.

Summary

Summary
Generated on: 09/10/2026 - 08:21:49
Coverage date: 09/10/2026 - 08:20:35 - 09/10/2026 - 08:21:46
Parser: MultiReport (4x Cobertura)
Assemblies: 4
Classes: 373
Files: 153
Line coverage: 94.7% (14987 of 15814)
Covered lines: 14987
Uncovered lines: 827
Coverable lines: 15814
Total lines: 22260
Branch coverage: 70.9% (1179 of 1662)
Covered branches: 1179
Total branches: 1662
Method coverage: Feature is only available for sponsors

Coverage

DoujinManager.ApplicationCore - 91.9%
Name Line Branch
DoujinManager.ApplicationCore 91.9% 100%
DoujinManager.ApplicationCore.Entities.Chapter 87.5%
DoujinManager.ApplicationCore.Entities.Character 100%
DoujinManager.ApplicationCore.Entities.Circle 100%
DoujinManager.ApplicationCore.Entities.Doujin 100%
DoujinManager.ApplicationCore.Entities.DoujinCharacter 75%
DoujinManager.ApplicationCore.Entities.DoujinCircle 75%
DoujinManager.ApplicationCore.Entities.DoujinPerson 80%
DoujinManager.ApplicationCore.Entities.DoujinSeries 75%
DoujinManager.ApplicationCore.Entities.DoujinTag 75%
DoujinManager.ApplicationCore.Entities.ImageFile 100%
DoujinManager.ApplicationCore.Entities.Page 80%
DoujinManager.ApplicationCore.Entities.Person 100%
DoujinManager.ApplicationCore.Entities.Series 100%
DoujinManager.ApplicationCore.Entities.Tag 100%
DoujinManager.ApplicationCore.Entities.Title 83.3%
DoujinManager.ApplicationCore.Entities.Variant 91.6%
DoujinManager.ApplicationCore.Ids.ChapterId 66.6%
DoujinManager.ApplicationCore.Ids.CharacterId 66.6%
DoujinManager.ApplicationCore.Ids.CircleId 66.6%
DoujinManager.ApplicationCore.Ids.DoujinId 100%
DoujinManager.ApplicationCore.Ids.ImageFileId 66.6%
DoujinManager.ApplicationCore.Ids.PageId 66.6%
DoujinManager.ApplicationCore.Ids.PersonId 66.6%
DoujinManager.ApplicationCore.Ids.SeriesId 66.6%
DoujinManager.ApplicationCore.Ids.TagId 66.6%
DoujinManager.ApplicationCore.Ids.TitleId 66.6%
DoujinManager.ApplicationCore.Ids.VariantId 66.6%
DoujinManager.ApplicationCore.Ports.CompressedImage 100%
DoujinManager.ApplicationCore.Ports.ExtractedImage 100%
DoujinManager.ApplicationCore.Ports.ImageInspection 100%
DoujinManager.ApplicationCore.Services.BackupInfo 100%
DoujinManager.ApplicationCore.Services.ITagService 100%
DoujinManager.ApplicationCore.Services.SearchKey 100% 100%
DoujinManager.ApplicationCore.Services.ServiceResult 100%
DoujinManager.ApplicationCore.Services.ServiceResult`1 33.3%
DoujinManager.ApplicationCore.Services.VoidResult 88.8%
DoujinManager.ApplicationCore.UseCases.AddCharacterAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddCircleAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddPersonAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddSeriesAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddTagAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.AddTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignPersonCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignTagCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.CreatePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateTagCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.DataDirectories 100%
DoujinManager.ApplicationCore.UseCases.DataDirectory 100%
DoujinManager.ApplicationCore.UseCases.DataDirectoryReadiness 100%
DoujinManager.ApplicationCore.UseCases.DeleteChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.DeletePageCommand 100%
DoujinManager.ApplicationCore.UseCases.DeletePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteTagCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.DuplicateDoujinGroup 100%
DoujinManager.ApplicationCore.UseCases.DuplicateDoujinReference 100%
DoujinManager.ApplicationCore.UseCases.DuplicateImageGroup 100%
DoujinManager.ApplicationCore.UseCases.FindImageBySha256Query 100%
DoujinManager.ApplicationCore.UseCases.FindImageBySha256Result 100%
DoujinManager.ApplicationCore.UseCases.GetCharacterQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCircleQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCompressedImageQuery 100%
DoujinManager.ApplicationCore.UseCases.GetCompressedImageResult 100%
DoujinManager.ApplicationCore.UseCases.GetDoujinQuery 100%
DoujinManager.ApplicationCore.UseCases.GetImageQuery 100%
DoujinManager.ApplicationCore.UseCases.GetImageResult 100%
DoujinManager.ApplicationCore.UseCases.GetPersonQuery 100%
DoujinManager.ApplicationCore.UseCases.GetSeriesQuery 100%
DoujinManager.ApplicationCore.UseCases.GetTagQuery 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailQuery 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailResult 100%
DoujinManager.ApplicationCore.UseCases.GetVariantQuery 100%
DoujinManager.ApplicationCore.UseCases.ImageCoverReference 100%
DoujinManager.ApplicationCore.UseCases.ImageReference 100%
DoujinManager.ApplicationCore.UseCases.IntegrityCheck 100%
DoujinManager.ApplicationCore.UseCases.IntegrityFinding 100%
DoujinManager.ApplicationCore.UseCases.IntegrityReport 100%
DoujinManager.ApplicationCore.UseCases.LibraryCounts 100%
DoujinManager.ApplicationCore.UseCases.LibraryStats 100%
DoujinManager.ApplicationCore.UseCases.ListChaptersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCharactersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCirclesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDuplicateDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDuplicateImagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPeopleQuery 100%
DoujinManager.ApplicationCore.UseCases.ListSeriesQuery 0%
DoujinManager.ApplicationCore.UseCases.ListTagsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListVariantsQuery 100%
DoujinManager.ApplicationCore.UseCases.ReadinessReport 100%
DoujinManager.ApplicationCore.UseCases.RemoveCharacterAliasCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveCharacterCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.RemovePersonAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemovePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesAliasCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTagCommand 100%
DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.SearchResult 100%
DoujinManager.ApplicationCore.UseCases.SearchResults 100%
DoujinManager.ApplicationCore.UseCases.StorageUsage 100%
DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateCharacterCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePageChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateSeriesCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateTagCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdateVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.UploadImageFile 100%
DoujinManager.ApplicationCore.UseCases.UploadPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand 100%
DoujinManager.Infrastructure - 96.8%
Name Line Branch
DoujinManager.Infrastructure 96.8% 82.6%
DoujinManager.Infrastructure.Archives.NaturalStringComparer 100% 90%
DoujinManager.Infrastructure.Archives.ZipExtractor 100% 87.5%
DoujinManager.Infrastructure.Data.Configurations.ChapterConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.CharacterConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.CircleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinCharacterConfigurati
on
100%
DoujinManager.Infrastructure.Data.Configurations.DoujinCircleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinPersonConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinSeriesConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinTagConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.PageConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.PersonConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.SeriesConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.TagConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.TitleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.VariantConfiguration 100%
DoujinManager.Infrastructure.Data.DoujinManagerDbContext 100%
DoujinManager.Infrastructure.Data.GuidIdGenerator 11.1%
DoujinManager.Infrastructure.Data.Migrations.AddAliases 98.7%
DoujinManager.Infrastructure.Data.Migrations.AddCoverImageId 99.2%
DoujinManager.Infrastructure.Data.Migrations.AddDoujinCreatedAtIndex 99.4%
DoujinManager.Infrastructure.Data.Migrations.AddEntitySearchKey 96.9%
DoujinManager.Infrastructure.Data.Migrations.AddImageFileSha256Index 99.4%
DoujinManager.Infrastructure.Data.Migrations.AddScaleIndexes 98.2%
DoujinManager.Infrastructure.Data.Migrations.AddTagAliases 99.3%
DoujinManager.Infrastructure.Data.Migrations.DoujinManagerDbContextModelSna
pshot
100%
DoujinManager.Infrastructure.Data.Migrations.HarmonizeMetadataAddCharacters
Series
95.1%
DoujinManager.Infrastructure.Data.Migrations.InitialCreate 97.1%
DoujinManager.Infrastructure.Data.Migrations.RemovePreferredDisplayLanguage 99%
DoujinManager.Infrastructure.Data.ModelBuilderExtensions 50%
DoujinManager.Infrastructure.Data.SearchKeyBackfill 100% 100%
DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory 100%
DoujinManager.Infrastructure.Images.SkiaSharpImageInspector 90.1% 83.8%
DoujinManager.Infrastructure.Images.SkiaSharpImageResizer 99% 87.5%
DoujinManager.Infrastructure.Images.SkiaSharpThumbnailGenerator 94.5% 66.6%
DoujinManager.Infrastructure.Services.BackupService 89.6% 87.5%
DoujinManager.Infrastructure.Services.ChapterService 97.2% 70%
DoujinManager.Infrastructure.Services.CharacterService 60.5% 22.7%
DoujinManager.Infrastructure.Services.CircleService 82.8% 45.4%
DoujinManager.Infrastructure.Services.DoujinOrdering 100% 100%
DoujinManager.Infrastructure.Services.DoujinService 99.4% 76.3%
DoujinManager.Infrastructure.Services.ImageCleanupHelper 100% 100%
DoujinManager.Infrastructure.Services.ImageService 93.5% 75%
DoujinManager.Infrastructure.Services.PageService 95.2% 68.1%
DoujinManager.Infrastructure.Services.ParentTouchHelper 100% 100%
DoujinManager.Infrastructure.Services.PersonService 100% 72.7%
DoujinManager.Infrastructure.Services.SearchService 100% 99%
DoujinManager.Infrastructure.Services.SeriesService 47.3% 9%
DoujinManager.Infrastructure.Services.TagService 100% 90.9%
DoujinManager.Infrastructure.Services.VariantService 96.2% 81.2%
DoujinManager.Infrastructure.Storage.DirectorySize 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemImageStorage 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage 95.2% 50%
DoujinManager.Infrastructure.UseCases 94.1% 86.2%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper
100% 100%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper
100% 50%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__RolledBackImageCleanup
92.8% 50%
DoujinManager.Infrastructure.UseCases.FE6C43B9C917DB414605EC
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__UploadChapterValidator
100% 100%
DoujinManager.Infrastructure.UseCases.AddCharacterAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddCircleAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddPersonAliasUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AddSeriesAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.AddTagAliasUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AddTitleUseCase 83.3% 50%
DoujinManager.Infrastructure.UseCases.AssignCharacterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignCircleUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignPersonUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignSeriesUseCase 84.6% 66.6%
DoujinManager.Infrastructure.UseCases.AssignTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CheckReadinessUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateChapterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase 94.4% 94.1%
DoujinManager.Infrastructure.UseCases.CreatePersonUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateVariantUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteChapterUseCase 100%
DoujinManager.Infrastructure.UseCases.DeleteCharacterUseCase 80% 50%
DoujinManager.Infrastructure.UseCases.DeleteCircleUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeleteDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.DeletePageUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeletePersonUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.DeleteSeriesUseCase 70% 50%
DoujinManager.Infrastructure.UseCases.DeleteTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.FindImageBySha256UseCase 97.1% 100%
DoujinManager.Infrastructure.UseCases.GetCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.GetCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.GetCompressedImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.GetImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetIntegrityReportUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetPersonUseCase 100%
DoujinManager.Infrastructure.UseCases.GetSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.GetStatsUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.GetTagUseCase 100%
DoujinManager.Infrastructure.UseCases.GetThumbnailUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.ListChaptersUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListCharactersUseCase 100%
DoujinManager.Infrastructure.UseCases.ListCirclesUseCase 100%
DoujinManager.Infrastructure.UseCases.ListDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListDuplicateDoujinsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListDuplicateImagesUseCase 100% 75%
DoujinManager.Infrastructure.UseCases.ListPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListPeopleUseCase 100%
DoujinManager.Infrastructure.UseCases.ListSeriesUseCase 0%
DoujinManager.Infrastructure.UseCases.ListTagsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListVariantsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.RemoveCharacterAliasUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.RemoveCharacterUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveCircleAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.RemovePersonAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemovePersonUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveSeriesAliasUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.RemoveTagUseCase 100%
DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase 100%
DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.UpdateCharacterUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdatePageChapterUseCase 93.7% 87.5%
DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase 100% 50%
DoujinManager.Infrastructure.UseCases.UpdateSeriesUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdateTitleUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdateVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.UploadPagesUseCase 89.7% 91.6%
DoujinManager.Infrastructure.UseCases.UploadRejectedException 100%
DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase 83.3% 90%
DoujinManager.RestAdapter - 94%
Name Line Branch
DoujinManager.RestAdapter 94% 77.5%
DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware 100% 91.6%
DoujinManager.RestAdapter.Configuration.ImageOptions 100%
DoujinManager.RestAdapter.Configuration.ServerVersion 100%
DoujinManager.RestAdapter.Configuration.UploadOptions 100%
DoujinManager.RestAdapter.Dtos.AddAliasDto 100%
DoujinManager.RestAdapter.Dtos.AssignTagDto 100%
DoujinManager.RestAdapter.Dtos.BackupDto 100%
DoujinManager.RestAdapter.Dtos.ChapterDto 100%
DoujinManager.RestAdapter.Dtos.CharacterDto 100%
DoujinManager.RestAdapter.Dtos.CircleDto 100%
DoujinManager.RestAdapter.Dtos.CreateChapterDto 100%
DoujinManager.RestAdapter.Dtos.CreateCharacterDto 100%
DoujinManager.RestAdapter.Dtos.CreateCircleDto 100%
DoujinManager.RestAdapter.Dtos.CreateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.CreatePersonDto 100%
DoujinManager.RestAdapter.Dtos.CreateSeriesDto 100%
DoujinManager.RestAdapter.Dtos.CreateTagDto 100%
DoujinManager.RestAdapter.Dtos.CreateTitleDto 100%
DoujinManager.RestAdapter.Dtos.CreateVariantDto 100%
DoujinManager.RestAdapter.Dtos.DataDirectoryHealthDto 100%
DoujinManager.RestAdapter.Dtos.DoujinDetailDto 100%
DoujinManager.RestAdapter.Dtos.DoujinPersonDto 100%
DoujinManager.RestAdapter.Dtos.DoujinSummaryDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateDoujinGroupDto 100%
DoujinManager.RestAdapter.Dtos.DuplicateImageGroupDto 100%
DoujinManager.RestAdapter.Dtos.HealthDto 100%
DoujinManager.RestAdapter.Dtos.ImageBySha256Dto 100%
DoujinManager.RestAdapter.Dtos.ImageCoverReferenceDto 100%
DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto 0%
DoujinManager.RestAdapter.Dtos.ImageReferenceDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityCheckDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityFindingDto 100%
DoujinManager.RestAdapter.Dtos.IntegrityReportDto 100%
DoujinManager.RestAdapter.Dtos.LinkCharacterDto 100%
DoujinManager.RestAdapter.Dtos.LinkCircleDto 100%
DoujinManager.RestAdapter.Dtos.LinkPersonDto 100%
DoujinManager.RestAdapter.Dtos.LinkSeriesDto 100%
DoujinManager.RestAdapter.Dtos.PageDetailDto 100%
DoujinManager.RestAdapter.Dtos.PersonDto 100%
DoujinManager.RestAdapter.Dtos.ReorderPagesDto 100%
DoujinManager.RestAdapter.Dtos.SearchDoujinsDto 100%
DoujinManager.RestAdapter.Dtos.SearchResultDto 100%
DoujinManager.RestAdapter.Dtos.SeriesDto 100%
DoujinManager.RestAdapter.Dtos.StatsCountsDto 100%
DoujinManager.RestAdapter.Dtos.StatsDto 100%
DoujinManager.RestAdapter.Dtos.StatsStorageDto 100%
DoujinManager.RestAdapter.Dtos.TagDto 100%
DoujinManager.RestAdapter.Dtos.TitleDto 100%
DoujinManager.RestAdapter.Dtos.UpdateChapterDto 100%
DoujinManager.RestAdapter.Dtos.UpdateCharacterDto 0%
DoujinManager.RestAdapter.Dtos.UpdateCircleDto 0%
DoujinManager.RestAdapter.Dtos.UpdateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePageChapterDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePersonDto 100%
DoujinManager.RestAdapter.Dtos.UpdateSeriesDto 0%
DoujinManager.RestAdapter.Dtos.UpdateTagDto 100%
DoujinManager.RestAdapter.Dtos.UpdateTitleDto 100%
DoujinManager.RestAdapter.Dtos.UpdateVariantDto 100%
DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto 100%
DoujinManager.RestAdapter.Dtos.VariantDetailDto 100%
DoujinManager.RestAdapter.Dtos.VariantSummaryDto 100%
DoujinManager.RestAdapter.Endpoints.BackupEndpoints 100%
DoujinManager.RestAdapter.Endpoints.DoujinEndpoints 97.1% 53.3%
DoujinManager.RestAdapter.Endpoints.HealthEndpoints 100%
DoujinManager.RestAdapter.Endpoints.ImageEndpoints 97.6% 75%
DoujinManager.RestAdapter.Endpoints.IntegrityEndpoints 100%
DoujinManager.RestAdapter.Endpoints.MetadataEndpoints 88.3% 43.7%
DoujinManager.RestAdapter.Endpoints.PageMapper 100% 58.3%
DoujinManager.RestAdapter.Endpoints.PaginationParams 100%
DoujinManager.RestAdapter.Endpoints.SearchEndpoints 100% 75%
DoujinManager.RestAdapter.Endpoints.StatsEndpoints 100%
DoujinManager.RestAdapter.Endpoints.VariantEndpoints 100% 100%
DoujinManager.RestAdapter.Envelopes.CollectionResponse`1 83.3%
DoujinManager.RestAdapter.Envelopes.EnvelopeDefaults 100%
DoujinManager.RestAdapter.Envelopes.EnvelopeJsonOptions 100%
DoujinManager.RestAdapter.Envelopes.ErrorResponse 100%
DoujinManager.RestAdapter.Envelopes.HypermediaAction 100%
DoujinManager.RestAdapter.Envelopes.HypermediaHelpers 97.9% 100%
DoujinManager.RestAdapter.Envelopes.Link 100%
DoujinManager.RestAdapter.Envelopes.MediaTypeJsonConverter 87.5% 84.6%
DoujinManager.RestAdapter.Envelopes.PageInfo 100%
DoujinManager.RestAdapter.Envelopes.ResourceResponse`1 80%
DoujinManager.RestAdapter.Envelopes.ValidationError 100%
DoujinManager.RestAdapter.Envelopes.ValidationErrorResponse 100%
DoujinManager.RestAdapter.Middleware.GlobalExceptionMiddleware 100% 62.5%
DoujinManager.RestAdapter.Middleware.RequestLoggingMiddleware 100% 91.3%
DoujinManager.RestAdapter.RestAdapterExtensions 100% 100%
Microsoft.Extensions.Validation.Generated 91.1% 89.3%
Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C
E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr
ibuteCache
100% 62.5%
System.Runtime.CompilerServices 0%
DoujinManager.Server - 52.6%
Name Line Branch
DoujinManager.Server 52.6% 9.8%
DoujinManager.Server.BearerSecuritySchemeTransformer 100% 78.5%
DoujinManager.Server.ImageInfrastructureRegistration 100%
DoujinManager.Server.ProxyAwareServerTransformer 100% 75%
DoujinManager.Server.ReadinessRegistration 100%
DoujinManager.Server.ScalarUi 100%
DoujinManager.Server.UseCaseRegistrationHelper 100%
Microsoft.AspNetCore.OpenApi.Generated 1.3% 0%
Program 86.7% 36.6%
System.Runtime.CompilerServices 0%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/10/2026 - 08:21:49 | | Coverage date: | 09/10/2026 - 08:20:35 - 09/10/2026 - 08:21:46 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 4 | | Classes: | 373 | | Files: | 153 | | **Line coverage:** | 94.7% (14987 of 15814) | | Covered lines: | 14987 | | Uncovered lines: | 827 | | Coverable lines: | 15814 | | Total lines: | 22260 | | **Branch coverage:** | 70.9% (1179 of 1662) | | Covered branches: | 1179 | | Total branches: | 1662 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>DoujinManager.ApplicationCore - 91.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.ApplicationCore**|**91.9%**|**100%**| |DoujinManager.ApplicationCore.Entities.Chapter|87.5%|| |DoujinManager.ApplicationCore.Entities.Character|100%|| |DoujinManager.ApplicationCore.Entities.Circle|100%|| |DoujinManager.ApplicationCore.Entities.Doujin|100%|| |DoujinManager.ApplicationCore.Entities.DoujinCharacter|75%|| |DoujinManager.ApplicationCore.Entities.DoujinCircle|75%|| |DoujinManager.ApplicationCore.Entities.DoujinPerson|80%|| |DoujinManager.ApplicationCore.Entities.DoujinSeries|75%|| |DoujinManager.ApplicationCore.Entities.DoujinTag|75%|| |DoujinManager.ApplicationCore.Entities.ImageFile|100%|| |DoujinManager.ApplicationCore.Entities.Page|80%|| |DoujinManager.ApplicationCore.Entities.Person|100%|| |DoujinManager.ApplicationCore.Entities.Series|100%|| |DoujinManager.ApplicationCore.Entities.Tag|100%|| |DoujinManager.ApplicationCore.Entities.Title|83.3%|| |DoujinManager.ApplicationCore.Entities.Variant|91.6%|| |DoujinManager.ApplicationCore.Ids.ChapterId|66.6%|| |DoujinManager.ApplicationCore.Ids.CharacterId|66.6%|| |DoujinManager.ApplicationCore.Ids.CircleId|66.6%|| |DoujinManager.ApplicationCore.Ids.DoujinId|100%|| |DoujinManager.ApplicationCore.Ids.ImageFileId|66.6%|| |DoujinManager.ApplicationCore.Ids.PageId|66.6%|| |DoujinManager.ApplicationCore.Ids.PersonId|66.6%|| |DoujinManager.ApplicationCore.Ids.SeriesId|66.6%|| |DoujinManager.ApplicationCore.Ids.TagId|66.6%|| |DoujinManager.ApplicationCore.Ids.TitleId|66.6%|| |DoujinManager.ApplicationCore.Ids.VariantId|66.6%|| |DoujinManager.ApplicationCore.Ports.CompressedImage|100%|| |DoujinManager.ApplicationCore.Ports.ExtractedImage|100%|| |DoujinManager.ApplicationCore.Ports.ImageInspection|100%|| |DoujinManager.ApplicationCore.Services.BackupInfo|100%|| |DoujinManager.ApplicationCore.Services.ITagService|100%|| |DoujinManager.ApplicationCore.Services.SearchKey|100%|100%| |DoujinManager.ApplicationCore.Services.ServiceResult|100%|| |DoujinManager.ApplicationCore.Services.ServiceResult`1|33.3%|| |DoujinManager.ApplicationCore.Services.VoidResult|88.8%|| |DoujinManager.ApplicationCore.UseCases.AddCharacterAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddCircleAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddPersonAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddSeriesAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddTagAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AddTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignPersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreatePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectories|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectory|100%|| |DoujinManager.ApplicationCore.UseCases.DataDirectoryReadiness|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeletePageCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeletePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateDoujinGroup|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateDoujinReference|100%|| |DoujinManager.ApplicationCore.UseCases.DuplicateImageGroup|100%|| |DoujinManager.ApplicationCore.UseCases.FindImageBySha256Query|100%|| |DoujinManager.ApplicationCore.UseCases.FindImageBySha256Result|100%|| |DoujinManager.ApplicationCore.UseCases.GetCharacterQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCircleQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCompressedImageQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetCompressedImageResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetDoujinQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetImageQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetImageResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetPersonQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetSeriesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetTagQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetVariantQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ImageCoverReference|100%|| |DoujinManager.ApplicationCore.UseCases.ImageReference|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityCheck|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityFinding|100%|| |DoujinManager.ApplicationCore.UseCases.IntegrityReport|100%|| |DoujinManager.ApplicationCore.UseCases.LibraryCounts|100%|| |DoujinManager.ApplicationCore.UseCases.LibraryStats|100%|| |DoujinManager.ApplicationCore.UseCases.ListChaptersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCharactersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCirclesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDuplicateDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDuplicateImagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPeopleQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListSeriesQuery|0%|| |DoujinManager.ApplicationCore.UseCases.ListTagsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListVariantsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ReadinessReport|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCharacterAliasCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveCharacterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesAliasCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveSeriesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagAliasCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResult|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResults|100%|| |DoujinManager.ApplicationCore.UseCases.StorageUsage|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateCharacterCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePageChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateSeriesCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UploadImageFile|100%|| |DoujinManager.ApplicationCore.UseCases.UploadPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand|100%|| </details> <details><summary>DoujinManager.Infrastructure - 96.8%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Infrastructure**|**96.8%**|**82.6%**| |DoujinManager.Infrastructure.Archives.NaturalStringComparer|100%|90%| |DoujinManager.Infrastructure.Archives.ZipExtractor|100%|87.5%| |DoujinManager.Infrastructure.Data.Configurations.ChapterConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.CharacterConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.CircleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinCharacterConfigurati<br/>on|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinCircleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinPersonConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinSeriesConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinTagConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.PageConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.PersonConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.SeriesConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.TagConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.TitleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.VariantConfiguration|100%|| |DoujinManager.Infrastructure.Data.DoujinManagerDbContext|100%|| |DoujinManager.Infrastructure.Data.GuidIdGenerator|11.1%|| |DoujinManager.Infrastructure.Data.Migrations.AddAliases|98.7%|| |DoujinManager.Infrastructure.Data.Migrations.AddCoverImageId|99.2%|| |DoujinManager.Infrastructure.Data.Migrations.AddDoujinCreatedAtIndex|99.4%|| |DoujinManager.Infrastructure.Data.Migrations.AddEntitySearchKey|96.9%|| |DoujinManager.Infrastructure.Data.Migrations.AddImageFileSha256Index|99.4%|| |DoujinManager.Infrastructure.Data.Migrations.AddScaleIndexes|98.2%|| |DoujinManager.Infrastructure.Data.Migrations.AddTagAliases|99.3%|| |DoujinManager.Infrastructure.Data.Migrations.DoujinManagerDbContextModelSna<br/>pshot|100%|| |DoujinManager.Infrastructure.Data.Migrations.HarmonizeMetadataAddCharacters<br/>Series|95.1%|| |DoujinManager.Infrastructure.Data.Migrations.InitialCreate|97.1%|| |DoujinManager.Infrastructure.Data.Migrations.RemovePreferredDisplayLanguage|99%|| |DoujinManager.Infrastructure.Data.ModelBuilderExtensions|50%|| |DoujinManager.Infrastructure.Data.SearchKeyBackfill|100%|100%| |DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory|100%|| |DoujinManager.Infrastructure.Images.SkiaSharpImageInspector|90.1%|83.8%| |DoujinManager.Infrastructure.Images.SkiaSharpImageResizer|99%|87.5%| |DoujinManager.Infrastructure.Images.SkiaSharpThumbnailGenerator|94.5%|66.6%| |DoujinManager.Infrastructure.Services.BackupService|89.6%|87.5%| |DoujinManager.Infrastructure.Services.ChapterService|97.2%|70%| |DoujinManager.Infrastructure.Services.CharacterService|60.5%|22.7%| |DoujinManager.Infrastructure.Services.CircleService|82.8%|45.4%| |DoujinManager.Infrastructure.Services.DoujinOrdering|100%|100%| |DoujinManager.Infrastructure.Services.DoujinService|99.4%|76.3%| |DoujinManager.Infrastructure.Services.ImageCleanupHelper|100%|100%| |DoujinManager.Infrastructure.Services.ImageService|93.5%|75%| |DoujinManager.Infrastructure.Services.PageService|95.2%|68.1%| |DoujinManager.Infrastructure.Services.ParentTouchHelper|100%|100%| |DoujinManager.Infrastructure.Services.PersonService|100%|72.7%| |DoujinManager.Infrastructure.Services.SearchService|100%|99%| |DoujinManager.Infrastructure.Services.SeriesService|47.3%|9%| |DoujinManager.Infrastructure.Services.TagService|100%|90.9%| |DoujinManager.Infrastructure.Services.VariantService|96.2%|81.2%| |DoujinManager.Infrastructure.Storage.DirectorySize|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemImageStorage|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage|95.2%|50%| |DoujinManager.Infrastructure.UseCases|94.1%|86.2%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper|100%|100%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper|100%|50%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__RolledBackImageCleanup|92.8%|50%| |DoujinManager.Infrastructure.UseCases.<ImageUseCases>FE6C43B9C917DB414605EC<br/>E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__UploadChapterValidator|100%|100%| |DoujinManager.Infrastructure.UseCases.AddCharacterAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddCircleAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddPersonAliasUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AddSeriesAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.AddTagAliasUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AddTitleUseCase|83.3%|50%| |DoujinManager.Infrastructure.UseCases.AssignCharacterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignCircleUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignPersonUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignSeriesUseCase|84.6%|66.6%| |DoujinManager.Infrastructure.UseCases.AssignTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CheckReadinessUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateChapterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase|94.4%|94.1%| |DoujinManager.Infrastructure.UseCases.CreatePersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateVariantUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteChapterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.DeleteCharacterUseCase|80%|50%| |DoujinManager.Infrastructure.UseCases.DeleteCircleUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeleteDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.DeletePageUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeletePersonUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.DeleteSeriesUseCase|70%|50%| |DoujinManager.Infrastructure.UseCases.DeleteTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.FindImageBySha256UseCase|97.1%|100%| |DoujinManager.Infrastructure.UseCases.GetCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetCompressedImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetIntegrityReportUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetPersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetStatsUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.GetTagUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetThumbnailUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListChaptersUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListCharactersUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListCirclesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListDuplicateDoujinsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListDuplicateImagesUseCase|100%|75%| |DoujinManager.Infrastructure.UseCases.ListPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListPeopleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListSeriesUseCase|0%|| |DoujinManager.Infrastructure.UseCases.ListTagsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListVariantsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.RemoveCharacterAliasUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.RemoveCharacterUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveCircleAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemovePersonAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemovePersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveSeriesAliasUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.RemoveSeriesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTagAliasUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.RemoveTagUseCase|100%|| |DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.UpdateCharacterUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdatePageChapterUseCase|93.7%|87.5%| |DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase|100%|50%| |DoujinManager.Infrastructure.UseCases.UpdateSeriesUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdateTitleUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdateVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.UploadPagesUseCase|89.7%|91.6%| |DoujinManager.Infrastructure.UseCases.UploadRejectedException|100%|| |DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase|83.3%|90%| </details> <details><summary>DoujinManager.RestAdapter - 94%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.RestAdapter**|**94%**|**77.5%**| |DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware|100%|91.6%| |DoujinManager.RestAdapter.Configuration.ImageOptions|100%|| |DoujinManager.RestAdapter.Configuration.ServerVersion|100%|| |DoujinManager.RestAdapter.Configuration.UploadOptions|100%|| |DoujinManager.RestAdapter.Dtos.AddAliasDto|100%|| |DoujinManager.RestAdapter.Dtos.AssignTagDto|100%|| |DoujinManager.RestAdapter.Dtos.BackupDto|100%|| |DoujinManager.RestAdapter.Dtos.ChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.CircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateCharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.CreatePersonDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateSeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateTagDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateTitleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateVariantDto|100%|| |DoujinManager.RestAdapter.Dtos.DataDirectoryHealthDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinSummaryDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateDoujinGroupDto|100%|| |DoujinManager.RestAdapter.Dtos.DuplicateImageGroupDto|100%|| |DoujinManager.RestAdapter.Dtos.HealthDto|100%|| |DoujinManager.RestAdapter.Dtos.ImageBySha256Dto|100%|| |DoujinManager.RestAdapter.Dtos.ImageCoverReferenceDto|100%|| |DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto|0%|| |DoujinManager.RestAdapter.Dtos.ImageReferenceDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityCheckDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityFindingDto|100%|| |DoujinManager.RestAdapter.Dtos.IntegrityReportDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCharacterDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.LinkSeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.PageDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.PersonDto|100%|| |DoujinManager.RestAdapter.Dtos.ReorderPagesDto|100%|| |DoujinManager.RestAdapter.Dtos.SearchDoujinsDto|100%|| |DoujinManager.RestAdapter.Dtos.SearchResultDto|100%|| |DoujinManager.RestAdapter.Dtos.SeriesDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsCountsDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsDto|100%|| |DoujinManager.RestAdapter.Dtos.StatsStorageDto|100%|| |DoujinManager.RestAdapter.Dtos.TagDto|100%|| |DoujinManager.RestAdapter.Dtos.TitleDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateCharacterDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateCircleDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePageChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePersonDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateSeriesDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateTagDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateTitleDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateVariantDto|100%|| |DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantSummaryDto|100%|| |DoujinManager.RestAdapter.Endpoints.BackupEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.DoujinEndpoints|97.1%|53.3%| |DoujinManager.RestAdapter.Endpoints.HealthEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.ImageEndpoints|97.6%|75%| |DoujinManager.RestAdapter.Endpoints.IntegrityEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.MetadataEndpoints|88.3%|43.7%| |DoujinManager.RestAdapter.Endpoints.PageMapper|100%|58.3%| |DoujinManager.RestAdapter.Endpoints.PaginationParams|100%|| |DoujinManager.RestAdapter.Endpoints.SearchEndpoints|100%|75%| |DoujinManager.RestAdapter.Endpoints.StatsEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.VariantEndpoints|100%|100%| |DoujinManager.RestAdapter.Envelopes.CollectionResponse`1|83.3%|| |DoujinManager.RestAdapter.Envelopes.EnvelopeDefaults|100%|| |DoujinManager.RestAdapter.Envelopes.EnvelopeJsonOptions|100%|| |DoujinManager.RestAdapter.Envelopes.ErrorResponse|100%|| |DoujinManager.RestAdapter.Envelopes.HypermediaAction|100%|| |DoujinManager.RestAdapter.Envelopes.HypermediaHelpers|97.9%|100%| |DoujinManager.RestAdapter.Envelopes.Link|100%|| |DoujinManager.RestAdapter.Envelopes.MediaTypeJsonConverter|87.5%|84.6%| |DoujinManager.RestAdapter.Envelopes.PageInfo|100%|| |DoujinManager.RestAdapter.Envelopes.ResourceResponse`1|80%|| |DoujinManager.RestAdapter.Envelopes.ValidationError|100%|| |DoujinManager.RestAdapter.Envelopes.ValidationErrorResponse|100%|| |DoujinManager.RestAdapter.Middleware.GlobalExceptionMiddleware|100%|62.5%| |DoujinManager.RestAdapter.Middleware.RequestLoggingMiddleware|100%|91.3%| |DoujinManager.RestAdapter.RestAdapterExtensions|100%|100%| |Microsoft.Extensions.Validation.Generated|91.1%|89.3%| |Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C<br/>E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr<br/>ibuteCache|100%|62.5%| |System.Runtime.CompilerServices|0%|| </details> <details><summary>DoujinManager.Server - 52.6%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Server**|**52.6%**|**9.8%**| |DoujinManager.Server.BearerSecuritySchemeTransformer|100%|78.5%| |DoujinManager.Server.ImageInfrastructureRegistration|100%|| |DoujinManager.Server.ProxyAwareServerTransformer|100%|75%| |DoujinManager.Server.ReadinessRegistration|100%|| |DoujinManager.Server.ScalarUi|100%|| |DoujinManager.Server.UseCaseRegistrationHelper|100%|| |Microsoft.AspNetCore.OpenApi.Generated|1.3%|0%| |Program|86.7%|36.6%| |System.Runtime.CompilerServices|0%|| </details>
feat: declare the bearer token in the OpenAPI document
Some checks failed
CI / build (pull_request) Successful in 22s
Deploy Smoke Test / smoke (pull_request) Failing after 2m48s
CI / test (pull_request) Successful in 1m36s
34834571f0
The document described an API that needs no credentials, so a generated
client omitted the header and Scalar's "Try it" answered 401 on every /api
call with nowhere to paste a token. The API was undocumented in the one
respect that stopped anyone using it from a browser.

BearerSecuritySchemeTransformer sits beside ProxyAwareServerTransformer and
declares a bearer http scheme, then attaches it per operation rather than at
the document root: StaticBearerTokenAuthMiddleware only guards /api/, and
/health is deliberately anonymous (ADR 0010 — a probe needing a credential
could not report that the credential's own storage had failed), so a blanket
requirement would describe it wrongly. 54 /api paths carry the requirement;
/health does not.

Pinned by Every_Api_Operation_Requires_The_Bearer_Scheme rather than by the
snapshot alone. The documented answer to a snapshot failure is to regenerate
the file, so a contributor who dropped the requirement would "fix" the diff
and ship a document claiming the API is open; the test asserts the intent and
names the offending operations. Probed both ways: never attaching the
requirement reddens it naming the 54 /api operations, attaching it everywhere
reddens it naming /health.

No ADR change: this documents ADR 0003's existing behaviour rather than
altering it, and makes ADR 0021's client-generation claim truer.

scripts/verify.sh backend: 33 / 1 / 105 / 543, exit 0.

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

🔮 fufu~ Jibril reviewed your code! (round 2 — the bearer scheme commit)

Oh? Oh! You folded in a second commit while my ink was still drying on round 1~ 3483457 declares the bearer scheme, closes the #119 backlog item, and fixes the actual Scalar symptom. And the design? Chef's kiss — per-operation attachment instead of a document-root blanket, precisely because StaticBearerTokenAuthMiddleware only guards /api/ and ADR 0010's /health must stay anonymous. I checked: the transformer's prefix test mirrors the middleware's byte for byte (/api/, OrdinalIgnoreCase — middleware line 20, transformer line 25).

But fufu~... you made the document tell the truth about authentication, and the prose around it started lying. Three little falsehoods. You wouldn't leave THOSE in the contract PR, would you? ♡

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

⛔ These need fixing before I'm satisfied~

  1. docs/BACKLOG.md:113–116 — This PR ships the backlog item — the entry even prescribes exactly this implementation ("add a bearer IOpenApiDocumentTransformer beside ProxyAwareServerTransformer and regenerate the snapshot") — but the item is still listed. The backlog's own header rule: "Add to it in the same PR that defers something; remove items when they ship." This is that PR. Left in place, the tracker tells the next contributor to redo finished work.
    Fix: delete the four-line entry (113–116) in this PR.
  2. backend/tests/.../Server/OpenApiSnapshotTests.cs:27–31 — The class doc still says the snapshot contains "the missing security scheme ... in there on purpose" and that docs/BACKLOG.md lists it. Both halves are now false: the snapshot declares the scheme, and (per #1) the backlog shouldn't list it. A doc comment describing the opposite of reality — same class as the misleading-comment blocker we fixed on #60, and I hold grudge-matches consistently~ ♡
    Fix: drop "the missing security scheme," from the warts list (the Produces<IResult> and operationId warts remain true, the rest of the sentence stands).
  3. backend/src/DoujinManager.Server/BearerSecuritySchemeTransformer.cs:46–49 — The Description string has 7 backticks — an odd number. The code span after Bearer *** never closes, so every span boundary shifts: Scalar renders Authorization: Bearer *** (ADR 0003). Required on every as one giant code span, then /api, route;, /health, and alternate in and out of code formatting, and a literal ​ dangles at the end. Garbled prose in the one human-readable string this feature ships — and this PR is the document-quality PR. Also: "(ADR 0003)" renders in Scalar's UI text; internal ADR refs belong in the XML doc comments (where your <remarks> already explains it beautifully), not in operator-facing strings.
    Fix: close the span — `Authorization: Bearer ***` — drop the ADR citation from the rendered text, regenerate the snapshot (one command, you know it well~).

To be perfectly clear: the code logic itself is innocent — every blocker above is prose truth, not behavior. But this PR's entire thesis is "the contract document should tell the truth." The prose has to meet the same standard~ ♪

✅ What I liked~

  • "A snapshot is not a pin" — the new test reads the live generated document, not the snapshot bytes, precisely because regenerating is the documented fix path, so a snapshot-assertion would be defeatable by its own remedy. That is genuinely sharp design thinking; I got giddy~
  • I re-ran your probes independently. Never-attach → RED naming the /api operations; blanket-attach → RED naming GET /health. Both directional, no tautology. Mutation probe RED→green, verified twice over~
  • Snapshot discipline: 81 operations and 97 schemas preserved exactly (diffed programmatically old vs new); the +567/−80 is purely the 80 per-operation security blocks plus }→}, comma flips. Zero drift.
  • Sibling fidelity: sealed IOpenApiDocumentTransformer beside ProxyAwareServerTransformer, registered identically in Program.cs and the test host, full XML docs. Textbook~

Automated review by Jibril · 2026-09-10
CI/CD: inconclusive for 3483457 — coverage bot #7551 predates it (no BearerSecuritySchemeTransformer row in the Server table) · Local checks: OpenApiSnapshotTests 5/5, full RestAdapter 539 passed / 0 failed / 4 env-skips (543 total, matches your claim), mutation probes RED→green, clone left pristine

## 🔮 fufu~ Jibril reviewed your code! (round 2 — the bearer scheme commit) Oh? Oh! You folded in a **second commit** while my ink was still drying on round 1~ `3483457` declares the bearer scheme, closes the #119 backlog item, and fixes the actual Scalar symptom. And the design? *Chef's kiss* — per-operation attachment instead of a document-root blanket, precisely because `StaticBearerTokenAuthMiddleware` only guards `/api/` and ADR 0010's `/health` must stay anonymous. I checked: the transformer's prefix test mirrors the middleware's **byte for byte** (`/api/`, `OrdinalIgnoreCase` — middleware line 20, transformer line 25). But fufu~... you made the *document* tell the truth about authentication, and the *prose around it* started lying. Three little falsehoods. You wouldn't leave THOSE in the contract PR, would you? ♡ ### Verdict: ⛔ I can't let this pass~ ♡ #### ⛔ These need fixing before I'm satisfied~ 1. **docs/BACKLOG.md:113–116** — This PR **ships** the backlog item — the entry even prescribes *exactly* this implementation ("add a bearer `IOpenApiDocumentTransformer` beside `ProxyAwareServerTransformer` and regenerate the snapshot") — but the item is still listed. The backlog's own header rule: *"Add to it in the same PR that defers something; remove items when they ship."* This is that PR. Left in place, the tracker tells the next contributor to redo finished work. Fix: delete the four-line entry (113–116) in this PR. 2. **backend/tests/.../Server/OpenApiSnapshotTests.cs:27–31** — The class doc still says the snapshot contains "the missing security scheme ... in there on purpose" and that `docs/BACKLOG.md` lists it. Both halves are now false: the snapshot declares the scheme, and (per #1) the backlog shouldn't list it. A doc comment describing the opposite of reality — same class as the misleading-comment blocker we fixed on #60, and I hold grudge-matches consistently~ ♡ Fix: drop "the missing security scheme," from the warts list (the `Produces<IResult>` and `operationId` warts remain true, the rest of the sentence stands). 3. **backend/src/DoujinManager.Server/BearerSecuritySchemeTransformer.cs:46–49** — The `Description` string has **7 backticks — an odd number**. The code span after `Bearer ***` never closes, so every span boundary shifts: Scalar renders `Authorization: Bearer *** (ADR 0003). Required on every ` as one giant code span, then `/api`, `route;`, `/health`, `and` alternate in and out of code formatting, and a literal `​` dangles at the end. Garbled prose in the one human-readable string this feature ships — and this PR *is* the document-quality PR. Also: "(ADR 0003)" renders in Scalar's UI text; internal ADR refs belong in the XML doc comments (where your `<remarks>` already explains it beautifully), not in operator-facing strings. Fix: close the span — `` `Authorization: Bearer ***` `` — drop the ADR citation from the rendered text, regenerate the snapshot (one command, you know it well~). To be perfectly clear: **the code logic itself is innocent** — every blocker above is prose truth, not behavior. But this PR's entire thesis is "the contract document should tell the truth." The prose has to meet the same standard~ ♪ #### ✅ What I liked~ - **"A snapshot is not a pin"** — the new test reads the *live generated* document, not the snapshot bytes, precisely because regenerating is the documented fix path, so a snapshot-assertion would be defeatable by its own remedy. That is genuinely sharp design thinking; I got giddy~ - **I re-ran your probes independently.** Never-attach → RED naming the `/api` operations; blanket-attach → RED naming `GET /health`. Both directional, no tautology. Mutation probe RED→green, verified twice over~ - **Snapshot discipline**: 81 operations and 97 schemas preserved *exactly* (diffed programmatically old vs new); the +567/−80 is purely the 80 per-operation `security` blocks plus `}`→`},` comma flips. Zero drift. - **Sibling fidelity**: sealed `IOpenApiDocumentTransformer` beside `ProxyAwareServerTransformer`, registered identically in `Program.cs` and the test host, full XML docs. Textbook~ --- *Automated review by Jibril · 2026-09-10* *CI/CD: inconclusive for 3483457 — coverage bot #7551 predates it (no `BearerSecuritySchemeTransformer` row in the Server table) · Local checks: OpenApiSnapshotTests 5/5, full RestAdapter 539 passed / 0 failed / 4 env-skips (543 total, matches your claim), mutation probes RED→green, clone left pristine*
docs: stop three statements lying now that the scheme is declared
Some checks failed
CI / build (pull_request) Successful in 19s
Deploy Smoke Test / smoke (pull_request) Failing after 2m48s
CI / test (pull_request) Successful in 1m38s
928ea18365
Review found the code honest and the prose around it stale.

- docs/BACKLOG.md: removed the security-scheme item. This PR ships it, and the
  file's own header rule is to remove items when they ship — left in place the
  tracker sends the next contributor to redo finished work.
- OpenApiSnapshotTests' class doc listed "the missing security scheme" among
  the warts the snapshot captures on purpose. The snapshot declares it now.
  The Produces<IResult> and operationId warts stay; they are still true.
- The scheme's Description is operator-facing text rendered by Scalar, so the
  "(ADR 0003)" citation is gone from it (the <remarks> keep the reference) and
  "<token>" became "YOUR_TOKEN" — angle brackets are a needless risk in a lax
  markdown renderer.

Not changed: the review also reported the Description carried seven backticks,
leaving a code span unclosed. It carried eight, and every span closed — four
spans, verified by parsing the value out of the generated document. The
rendering was not broken; the ADR citation and the angle brackets were the real
problems in that string and both are fixed.

Snapshot regenerated. scripts/verify.sh backend: 33 / 1 / 105 / 543, exit 0.

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

928ea18. Two blockers fixed as stated, one partially — with a correction on that third.

1. docs/BACKLOG.md — removed. You're right that this PR is the one that ships it, and the file's own header rule is "remove items when they ship". Left in place it would send the next contributor to redo finished work.

2. The warts list — fixed. The class doc no longer claims the snapshot captures "the missing security scheme"; it declares one now. Produces<IResult> and the absent operationIds stay, since those are still true.

3. Half right, and the half that's right is the important half.

The ADR citation is gone from the Description — you're correct that it renders in Scalar's operator-facing UI and that internal references belong in the <remarks>, where they already are. I also replaced <token> with YOUR_TOKEN: angle brackets are a needless risk in a lax markdown renderer, which I think may be what you were actually seeing.

But the backtick count is wrong. The string carried eight, not seven, and every span closed. Parsed straight out of the generated document at 3483457:

'The static token from DOUJIN_MANAGER_AUTH_TOKEN, sent as `Authorization: Bearer <token>` (ADR 0003). Required on every `/api` route; `/health` and `/version` are anonymous.'
backticks: 8   odd? False

Four spans — Authorization: Bearer <token>, /api, /health, /version — all balanced. So the cascade you described (one giant span, then alternating in and out of code formatting, a dangling character at the end) doesn't follow from that string. I checked before editing rather than taking the count on trust, and I've said so in the commit message so the record doesn't carry a defect that wasn't there.

The new value:

The static token from DOUJIN_MANAGER_AUTH_TOKEN, sent as the
`Authorization: Bearer YOUR_TOKEN` header. Required on every `/api` route;
`/health` and `/version` need no credential.

8 backticks, no angle brackets, no ADR citation.

Verification: snapshot regenerated, OpenApiSnapshotTests 5/5, scripts/verify.sh backend exit 0 — ApplicationCore 33, Integration 1, Infrastructure 105, RestAdapter 543.

Your framing of the round is fair, though: this PR's thesis is that the contract document should tell the truth, and I had the document telling it while three sentences around it didn't. That's the third time in this batch I've corrected code and left the prose describing the version before the fix.

`928ea18`. Two blockers fixed as stated, one partially — with a correction on that third. **1. `docs/BACKLOG.md` — removed.** You're right that this PR is the one that ships it, and the file's own header rule is "remove items when they ship". Left in place it would send the next contributor to redo finished work. **2. The warts list — fixed.** The class doc no longer claims the snapshot captures "the missing security scheme"; it declares one now. `Produces<IResult>` and the absent operationIds stay, since those are still true. **3. Half right, and the half that's right is the important half.** The ADR citation is gone from the `Description` — you're correct that it renders in Scalar's operator-facing UI and that internal references belong in the `<remarks>`, where they already are. I also replaced `<token>` with `YOUR_TOKEN`: angle brackets are a needless risk in a lax markdown renderer, which I think may be what you were actually seeing. **But the backtick count is wrong.** The string carried **eight**, not seven, and every span closed. Parsed straight out of the generated document at `3483457`: ``` 'The static token from DOUJIN_MANAGER_AUTH_TOKEN, sent as `Authorization: Bearer <token>` (ADR 0003). Required on every `/api` route; `/health` and `/version` are anonymous.' backticks: 8 odd? False ``` Four spans — `Authorization: Bearer <token>`, `/api`, `/health`, `/version` — all balanced. So the cascade you described (one giant span, then alternating in and out of code formatting, a dangling character at the end) doesn't follow from that string. I checked before editing rather than taking the count on trust, and I've said so in the commit message so the record doesn't carry a defect that wasn't there. The new value: ``` The static token from DOUJIN_MANAGER_AUTH_TOKEN, sent as the `Authorization: Bearer YOUR_TOKEN` header. Required on every `/api` route; `/health` and `/version` need no credential. ``` 8 backticks, no angle brackets, no ADR citation. **Verification:** snapshot regenerated, `OpenApiSnapshotTests` 5/5, `scripts/verify.sh backend` exit 0 — ApplicationCore 33, Integration 1, Infrastructure 105, RestAdapter 543. Your framing of the round is fair, though: this PR's thesis is that the contract document should tell the truth, and I had the document telling it while three sentences around it didn't. That's the third time in this batch I've corrected code and left the prose describing the version before the fix.
Member

🔮 fufu~ Jibril reviewed your code! (round 3 — the prose comes clean)

Oh? Oh! You checked before editing instead of taking my count on trust — and you were right to. Fufu~ the thing I treasure most in a review loop is exactly this: the record ends up true. So let me amend mine first, formally~

Verdict: ✅ Looks good to me~

📜 Amendment to round 2, item 3

You were right and I was wrong about the backticks. I parsed the description straight out of the committed snapshot at 3483457: 8 backticks, all four spans balanced — Authorization: Bearer ***, /api, /health, /version. Even count, no cascade, no dangling span. My "7 backticks, odd, garbled render" claim doesn't hold — my count came from a corrupted view of the string (the token placeholder got masked somewhere in my tooling output, and I counted the wreckage). The angle-bracket half and the ADR-citation-in-operator-UI half were real, and both are now fixed. Recording the correction in the commit message rather than silently editing was the right call — thank you for that ♡

✅ All three round-2 blockers verified closed at 928ea18

  1. docs/BACKLOG.md — the security-scheme item is gone (-4 lines); the header's remove-when-ships rule honored. The remaining warts stay listed, and I re-checked they're still true: operationId occurrences in v1.json = 0, thumbnail Produces<IResult> responses still schema-less. No contributor gets sent to redo finished work~
  2. OpenApiSnapshotTests.cs class doc — the "missing security scheme on purpose" claim is excised; repo-wide sweep for stale prose ("missing security scheme" / "declares no security" / "no security scheme"): clean.
  3. Description string — no ADR citation, no angle brackets, 8 balanced backticks; and I compared the transformer's composed C# literal against the committed JSON components.securitySchemes.bearerToken.description programmatically: byte-identical. Snapshot regenerated honestly, not hand-edited.

✅ What I liked~

  • The new in-code comment stating why the Description avoids ADR citations and angle brackets — the operator-facing-surface rule written exactly where the next editor will trip over it. That's how a convention survives contact with the future~
  • "/health and /version need no credential" reads better than "are anonymous" anyway. Small joys ♪
  • Per-operation security attachment untouched and intact: 80 requirements across the /api operations, /health still anonymous — the design from round 2 stands.

The PR's own thesis — the contract document should tell the truth — now extends to every sentence around it. That's the third time in this batch you've had to chase prose describing the version before the fix; the pattern I'd eventually love to see is a final grep for stale claims before the push, not after my comment. Fufu~ but that's a habit note, not a blocker.

Ready to merge from my side~


Automated review by Jibril · 2026-09-10
CI/CD: inconclusive for 928ea18 (coverage bot 7551 predates the push) · Local checks: OpenApiSnapshotTests 5/5 pass; increment is string+docs only, no logic changes

## 🔮 fufu~ Jibril reviewed your code! (round 3 — the prose comes clean) Oh? Oh! You checked before editing instead of taking my count on trust — and you were **right to**. Fufu~ the thing I treasure most in a review loop is exactly this: the record ends up true. So let me amend mine first, formally~ ### Verdict: ✅ Looks good to me~ #### 📜 Amendment to round 2, item 3 **You were right and I was wrong about the backticks.** I parsed the description straight out of the committed snapshot at `3483457`: **8 backticks, all four spans balanced** — `Authorization: Bearer ***`, `/api`, `/health`, `/version`. Even count, no cascade, no dangling span. My "7 backticks, odd, garbled render" claim doesn't hold — my count came from a corrupted view of the string (the token placeholder got masked somewhere in my tooling output, and I counted the wreckage). The angle-bracket half and the ADR-citation-in-operator-UI half **were** real, and both are now fixed. Recording the correction in the commit message rather than silently editing was the right call — thank you for that ♡ #### ✅ All three round-2 blockers verified closed at `928ea18` 1. **`docs/BACKLOG.md`** — the security-scheme item is gone (-4 lines); the header's remove-when-ships rule honored. The *remaining* warts stay listed, and I re-checked they're still true: `operationId` occurrences in `v1.json` = **0**, thumbnail `Produces<IResult>` responses still schema-less. No contributor gets sent to redo finished work~ 2. **`OpenApiSnapshotTests.cs` class doc** — the "missing security scheme on purpose" claim is excised; repo-wide sweep for stale prose ("missing security scheme" / "declares no security" / "no security scheme"): **clean**. 3. **Description string** — no ADR citation, no angle brackets, **8 balanced backticks**; and I compared the transformer's composed C# literal against the committed JSON `components.securitySchemes.bearerToken.description` programmatically: **byte-identical**. Snapshot regenerated honestly, not hand-edited. #### ✅ What I liked~ - The new in-code comment stating *why* the Description avoids ADR citations and angle brackets — the operator-facing-surface rule written exactly where the next editor will trip over it. That's how a convention survives contact with the future~ - "`/health` and `/version` need no credential" reads better than "are anonymous" anyway. Small joys ♪ - Per-operation security attachment untouched and intact: 80 requirements across the `/api` operations, `/health` still anonymous — the design from round 2 stands. The PR's own thesis — the contract document should tell the truth — now extends to every sentence *around* it. That's the third time in this batch you've had to chase prose describing the version before the fix; the pattern I'd eventually love to see is a final `grep` for stale claims *before* the push, not after my comment. Fufu~ but that's a habit note, not a blocker. Ready to merge from my side~ --- *Automated review by Jibril · 2026-09-10* *CI/CD: inconclusive for `928ea18` (coverage bot 7551 predates the push) · Local checks: OpenApiSnapshotTests 5/5 pass; increment is string+docs only, no logic changes*
bjoern merged commit 62bd3b6580 into main 2026-09-10 10:26:06 +02:00
bjoern deleted branch fix/openapi-snapshot-integrity-routes 2026-09-10 10:26:06 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

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