feat: ask a page what is on it #191
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/regions-endpoint"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What's in
GET /api/images/{id}/regions— story 1.4 ofdocs/TRANSLATION_PLAN.md, the last backend piece of
phase 1. The reader overlay (story 1.5) is the only thing left.
The state is the point, not the list. ADR 0027 asks for three states to be
distinguishable, and two of them — nothing has looked at this page and
something looked and found nothing — are both an empty array. A
stateword(
notAnalysed/analysed/failed) carries the difference, and the endpointdescription says so on the wire, because reading the list and ignoring the state
is both the easiest thing to do with this resource and the one thing that makes
it lie.
A fourth state the story did not name. The page the detector could not read
is a third empty list. Rendering it as "nothing on this page" is the same false
claim as rendering an unexamined page that way, so it answers
failedwith thereason.
Gated on the loaded detector's id and version, the way
GET /api/detectioncounts. A library mid-re-sweep does not serve shapes the server itself intends
to replace, and the two halves of that gate are pinned separately: a row from an
older version of the same model, and a row from a different model at the same
version string — "v2" is what everything calls its second release, so a gate on
the version alone would pass one of those two.
A server with no detector answers
notAnalysedfor every image rather than500ing: the query is registered ahead of the model check, next to the status
query and for the same reason.
Outlines go out as a flat array of alternating x and y, normalised — the wire
and the stored column are the same shape, and a client strokes them at whatever
size it is drawing the page at.
Also here: a Detection row in the README API table, covering this endpoint
and
GET /api/detection. The table had no detection row at all — #185 and #188added the endpoints without it.
Verification
scripts/verify.sh backendgreen — 1,503 passed, 5 skipped (the real-modeltests, which need
DOUJIN_MANAGER_DETECTOR_MODEL).scripts/verify.sh appdid not run here, and not for a reason this branchcaused: the shared
vendor/openrouter_dartsubmodule is checked out at436b956rather than thedc30fbe(0.30.0)mainpins, soflutter analyzefails on
ModelId— a type the pinned commit has and that one does not — and apub getagainst it rewritesapp/pubspec.lockto 0.27.0. That rewrite hasbeen reverted rather than committed. This branch changes no file under
app/.ImageRegionsIntegrationTestson a host with a detector,ImageRegionsWithoutADetectorTestson one without: all three states, the three different routes intonotAnalysed(never examined, examined by an older version, examined by a different model), the 404, the envelope links, the bearer tokenDetectionUseCaseTestson in-memory SQLite — the argument guard, the outline decode, and a corrupt outline failing loudly rather than being skippedImageRegionsDtoTests, no host: the argument guards, the box fields, and a five-point outline whose flattening cannot be wrong plausiblybackend/openapi/v1.jsonregenerated and committedMutation-probed: 19 mutations, 17 killed. The two survivors are one finding
and it is in docs/BACKLOG.md: removing the explicit
ORDER BYfrom the region read is invisible, because SQLite returns exactlythat order anyway off the
(image_file_id, kind)index — measured, not assumed.What the tests do pin is that the order is kind-then-id rather than some other
explicit order.
The probe also caught a test of mine that was passing by luck: the ordering
fixture used random region ids, so the assertion held about half the time.
The ids are now fixed and chosen so that id order, insertion order and kind
order all disagree.
Not verified: nothing draws these yet. Whether the outlines are any good on
a real page is story 1.5's question, and no test here can answer it.
Follow-ups
One new entry in docs/BACKLOG.md: the explicit
ORDER BYonthe region read, recorded as a branch that cannot be pinned on this schema and
why it stays anyway.
Also corrected there: the phases-2-and-3 entry still said phase 1 "is not built
yet either", which this branch and #183/#185/#188 have made false. It now says
what is actually left of phase 1 — story 1.5, the reader overlay.
🤖 Generated with Claude Code
GET /api/images/{id}/regions, story 1.4 of the translation plan. The state is the answer, not the list. "Nothing has looked at this page" and "something looked and found nothing" are both an empty array, so a state word carries the difference and the endpoint says so on the wire. A fourth state the story did not name came out of the data: the page the detector could not read is a third empty list, and calling that "nothing here" is the same lie for a different reason. Gated on the loaded detector's id and version, the way GET /api/detection counts, so a library mid-re-sweep does not serve shapes the server itself intends to replace. Both halves of that gate are pinned separately: an older version of the same model, and a different model at the same version string. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>Summary
Summary
Coverage
DoujinManager.ApplicationCore - 96.9%
DoujinManager.Infrastructure - 96.8%
on
pshot
Series
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__CoverImageHelper
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__PrimaryTitleHelper
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__RolledBackImageCleanup
E20E2ABA2099901FFAB4904C0513DF077BA6D6CA374__UploadChapterValidator
9CDB080D11D98B3E0D3ADB24949B4D7D90DD87AD1CEB5845F__Entry_0
DoujinManager.RestAdapter - 98.9%
E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr
ibuteCache
DoujinManager.Server - 67.9%
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! A state machine smuggled into a list endpoint — and the PR body opens by declaring "the state is the point, not the list." My wings twitched with delight~ ♡ Three empty arrays that mean three different things, a discriminator word carried on the wire, and tests that assert the states against each other rather than one in isolation. This is the kind of resource design I want to sink my teeth into~
Verdict: ✅ Looks good to me~
I chased every structural claim this PR makes, and every single one held:
GET /api/detectioncounts" — literally true.GetRegionDetectionStatusUseCasecounts onDetectorId && DetectorVersion; the new query gates on the same pair, so a mid-re-sweep library can't disagree with its own status endpoint. And the two halves of the gate are pinned separately: older version of the same model, and a different model wearing the same "v2" string. Fufu~ the fixture even explains why "v2" is the dangerous case~ImageRegionConfiguration:38-41has region → analysis,DeleteBehavior.Cascade, andReplaceAnalysisAsyncruns before bothStoreAsyncandStoreFailureAsync. So the unconditional region read is sound: any region row for an image necessarily belongs to its single current analysis, and the comment explaining why there's no skip-branch is correct, not hand-waving. I checked~ ♡BuildImageRegionsLinksmirrors theimage/thumbnaillink shapes used at HypermediaHelpers :203/:234/:246 down to the media types;[FromServices]carries the same documented ADR-0021 route-table rationale as the status sibling;EmptyActionswith a comment saying why;.ProducesProblemdeclarations on all three codes; theFrom(null!)guards match the DTO-mapper house style.(image_file_id, kind)index exists (ImageRegionConfiguration:46), so the BACKLOG note's "SQLite would return that order anyway — measured" story is consistent with the schema, and the honest recording of the two mutation survivors instead of quietly deleting theORDER BYis exactly the hygiene I demand~ImageRegionsWithoutADetectorTestspins the no-detector registration path against a host that really has none — the exact 500-shapeDetectionRegistration's comment says it refuses to become. The query registered ahead of the model check, same as the journal. Correct~OpenApiSnapshotTests(which ran green in my local pass), and thekind-as-integer rationale (stored value, wire = store) is consistent with the document-wide shape — 47 sibling properties serialize the same way on base.Local checks (CI absent for
c3eefc5— no bot comment yet):dotnet buildRelease succeeded with zero warnings in every changed file (the survivors are pre-existing, in untouched support files), and the targeted run — all five affected test classes plus the snapshot test — passed 43/43, 0 skipped. Branch audit: every new arm has a pin (null query → ANE, missing image → 404 + detail, no detector → notAnalysed, analysis-null via all three routes, analysed-with-regions, failed + reason, corrupt outline → loudFormatException, wire flattening with five points that can't pair plausibly-wrong, links, bearer token).One honesty note from my side: I attempted my own mutation probe (dropping the
DetectorIdhalf of the gate to see the different-model test go red) and the sandbox's memory pressure killed the build three times — so I report it as attempted, not completed, and my approval rests on the mechanical run above plus the schema-level verification, not on the author's probe claims. The working tree was restored pristine (md5-verified) after each attempt.💡 Little ideas (non-blocking)~
_ => NotAnalysedarm inImageRegionsDto.From:ImageAnalysisStatustoday has exactlySucceededandFailed, so listing both members plus an explicitnull => NotAnalysedwould make the switch exhaustive today — and a future third enum member would become a compile-time event instead of silently rendering asnotAnalysed. Purely a nicety; the current form is also defensible for corrupt-enum-value rows, and the siblingDetectionStatusDtoderives its word the same relaxed way. ♪✅ What I liked~
RegionGeometry's encoding stays nobody else's business; corrupt rows fail loudly with a comment explaining why quiet-skipping would be worse.ORDER BYrecorded with the measurement, the stale phase-1 claim corrected, the README row added for endpoints #185/#188 forgot. Documentation that changes with the code~The reader overlay (story 1.5) is the only thing left, and this endpoint hands it everything it needs — state first, shapes second, and a
detectionlink for the "why is this still notAnalysed" follow-up question. Fufu~ merge it before I change my mind about sharing~ ♡Automated review by Jibril · 2026-09-20
CI/CD: absent for head SHA
c3eefc5· Local checks: build 0 errors/0 new warnings · 43/43 targeted tests green (all new-path classes + OpenAPI snapshot); full-suite run delegated to author'sverify.sh backend(1,503 passed, 5 skipped real-model)