feat: a region carries the room for the English, not just the mask #116

Merged
bjoern merged 3 commits from worktree-region-fit-box into main 2026-07-30 00:46:01 +02:00
Member

A box tight around the source glyphs answers cleaning's question. It does not answer
typesetting's, and the two are barely the same shape: Japanese runs vertically, so the source
box is a tall narrow column, while the English replacing it is set horizontally and needs the
balloon's width. Fitting lettering to the source box sets a wide sentence into a column and
shrinks it to nothing.

So a region carries a second rectangle.

What's in

fitBbox — the largest rectangle inside the container. Inscribed, not the container's
bounding box: a balloon is round, and a rectangle drawn around it hangs outside the curve at all
four corners, so lettering fitted to that overflows the outline. The briefs say this explicitly,
because it is the part that looks like a detail and is not.

Which forms. bubble requires one — the container is the constraint and nothing else can
supply it. embedded may have one (a sign's face is a real bound, but a sign is often recorded
rather than lettered). floating has no container, so a fit box there is refused rather than
stored as a quiet contradiction. Null means not measured.

One invariant, on the single write path (UpdateRegion, so the editor and every agent tool
inherit it): the fit box contains the region's own box. A violation is almost always the two
rectangles handed over the wrong way round. Edges touching is allowed.

Measured where the container is already being judged. classify_region takes fit beside
type and form — deciding there is a container and measuring the room inside it are one act of
looking, and a second tool would spend a round and allow a half-answered region in between.
Re-judging a bubble as floating clears the fit box, because keeping it would leave two answers that
contradict each other.

Everything that shows a region shows both (the four impacts you flagged):

  • Tool callsclassify_region(type, form, fit?); list_regions reports the fit box beside
    the form, so a review can see it without cropping.
  • Overlay renderingLabeledBox carries the fit box and DrawBoxes draws it dashed, green,
    unlabelled, under the region's own box so the tight edges a refiner is judging stay on top. The
    grid is untouched.
  • QA — Page QA judges it on three things and nothing else: it exists, it contains the region's
    box, it stays inside the container. A fit box merely smaller than it could be is explicitly not
    a defect (the lettering shrinks and nobody sees it); one that overflows the balloon is, and a
    missing one on a bubble is, since nothing later measures it.
  • ADRs — 0012 gains "Two boxes, not one"; 0016 gets the new signature; 0021's typesetting now
    fits into fitBbox with a fallback.

fillInset and fillSquareCorners are gone. I checked before proposing it: nothing ever read
them.
They were threaded through every DTO and stored on every row for a pixel pass that is still
deferred, and ADR 0016 documented a set_region_flags tool that was never built. Whatever the
real renderer needs can be added when ADR 0021 opens and can say so.

One behaviour I had to decide and want your eye on: a move_resize_region that drags the box
out of its fit box clears the fit box and says so in the answer. Refusing the move would cost
a round the agent cannot spend — it has to move the box before it can re-measure the room — and
keeping a fit box measured for a container the region has left is the contradiction the write path
refuses anyway. Said out loud rather than silently, because a field quietly reset is the sharpest
edge in this codebase.

Tests

12 new, 1008/1008 green (Domain 109→114, UseCases 469→474, Integration 188→189, BlazorAdapter
229→231).

  • Contains_answers_about_areas_not_about_corner_order — a [Theory] over five arrangements, each
    also asserted with the inner box written upside down.
  • A_fit_box_that_does_not_hold_the_region_is_refused — the invariant on the write path, including
    that edges touching is room enough.
  • Classifying_a_bubble_records_the_room_the_english_may_use,
    The_fit_box_follows_the_form_it_belongs_to (bubble without → refused, floating with → refused,
    floating without → fine), Re_judging_a_bubble_as_floating_clears_the_fit_box.
  • A_fit_box_that_is_not_a_box_takes_the_whole_call_down_with_it — the pixel-box reader's refusal,
    and that the type and form on the same call are not written either.
  • A_move_out_of_the_fit_box_clears_it_and_says_so — the decision above, pinned.
  • An_annotated_view_draws_the_fit_box_around_the_region_box (real Skia) — the region's own box is
    byte-identical with and without a fit box, and the fit edge is sampled along its length rather
    than at one point, because a dashed stroke lets a single probe land in a gap and prove nothing.
  • Two bUnit tests: the mode appears only for a container, switching it writes nothing, and the
    group offers exactly two options.
  • Move_resize_threads_every_other_profile_field_through now threads the fit box too.
  • Seed asserts the fit boxes are on containers only, are wider than the box they hold, and that
    one bubble is deliberately unmeasured.

Browser-verified

Fresh server, seeded world, and it earned its keep — it caught a real bug: AllLabel="null" on
the mode toggle passed the string "null", rendering a third button labelled "null". bUnit had not
noticed because my test looked the two options up by name. Fixed, and the test now asserts the
group's exact contents.

Then, on the fixed build:

  1. The overlay draws green dashed fit boxes around the two measured bubbles and none around the
    unmeasured one.
  2. Switching to Fit box and dragging wrote FitBbox = (0.30, 0.45)–(0.72, 0.84) around the
    region's own (0.40, 0.55)–(0.58, 0.72) — the region's box untouched.
  3. While drawing the fit box, the region's own box stays visible inside it (screenshot-checked).
  4. An earlier degenerate drag produced the refusal path live: "The fit box has to contain the
    region's own box…"
    in the error banner with the header showing Not saved. The invariant and
    its message are proven end to end, not just unit-tested.

Honest notes

  • Existing regions keep FitBbox = null, so nothing is claimed for them; a bubble annotated before
    this needs a measurement from the editor or a re-run before typesetting has anything to fit into.
    QA will name it once the pixel pass makes it matter.
  • The migration drops two columns. Their data is unread by any code, but it is a destructive
    migration and Down recreates the columns empty, not their contents.

🤖 Generated with Claude Code

A box tight around the source glyphs answers **cleaning's** question. It does not answer **typesetting's**, and the two are barely the same shape: Japanese runs vertically, so the source box is a tall narrow column, while the English replacing it is set horizontally and needs the balloon's *width*. Fitting lettering to the source box sets a wide sentence into a column and shrinks it to nothing. So a region carries a second rectangle. ## What's in **`fitBbox`** — the largest rectangle **inside** the container. Inscribed, not the container's bounding box: a balloon is round, and a rectangle drawn around it hangs outside the curve at all four corners, so lettering fitted to that overflows the outline. The briefs say this explicitly, because it is the part that looks like a detail and is not. **Which forms.** `bubble` **requires** one — the container is the constraint and nothing else can supply it. `embedded` may have one (a sign's face is a real bound, but a sign is often recorded rather than lettered). `floating` has no container, so a fit box there is **refused** rather than stored as a quiet contradiction. Null means not measured. **One invariant, on the single write path** (`UpdateRegion`, so the editor and every agent tool inherit it): the fit box contains the region's own box. A violation is almost always the two rectangles handed over the wrong way round. Edges touching is allowed. **Measured where the container is already being judged.** `classify_region` takes `fit` beside `type` and `form` — deciding there *is* a container and measuring the room inside it are one act of looking, and a second tool would spend a round and allow a half-answered region in between. Re-judging a bubble as floating clears the fit box, because keeping it would leave two answers that contradict each other. **Everything that shows a region shows both** (the four impacts you flagged): - **Tool calls** — `classify_region(type, form, fit?)`; `list_regions` reports the fit box beside the form, so a review can see it without cropping. - **Overlay rendering** — `LabeledBox` carries the fit box and `DrawBoxes` draws it dashed, green, unlabelled, *under* the region's own box so the tight edges a refiner is judging stay on top. The grid is untouched. - **QA** — Page QA judges it on three things and nothing else: it exists, it contains the region's box, it stays inside the container. A fit box merely *smaller* than it could be is explicitly not a defect (the lettering shrinks and nobody sees it); one that overflows the balloon is, and a missing one on a bubble is, since nothing later measures it. - **ADRs** — 0012 gains "Two boxes, not one"; 0016 gets the new signature; 0021's typesetting now fits into `fitBbox` with a fallback. **`fillInset` and `fillSquareCorners` are gone.** I checked before proposing it: **nothing ever read them.** They were threaded through every DTO and stored on every row for a pixel pass that is still deferred, and ADR 0016 documented a `set_region_flags` tool that **was never built**. Whatever the real renderer needs can be added when ADR 0021 opens and can say so. **One behaviour I had to decide** and want your eye on: a `move_resize_region` that drags the box *out* of its fit box **clears the fit box and says so in the answer**. Refusing the move would cost a round the agent cannot spend — it has to move the box before it can re-measure the room — and keeping a fit box measured for a container the region has left is the contradiction the write path refuses anyway. Said out loud rather than silently, because a field quietly reset is the sharpest edge in this codebase. ## Tests 12 new, **1008/1008 green** (Domain 109→114, UseCases 469→474, Integration 188→189, BlazorAdapter 229→231). - `Contains_answers_about_areas_not_about_corner_order` — a `[Theory]` over five arrangements, each also asserted with the inner box written upside down. - `A_fit_box_that_does_not_hold_the_region_is_refused` — the invariant on the write path, including that edges touching is room enough. - `Classifying_a_bubble_records_the_room_the_english_may_use`, `The_fit_box_follows_the_form_it_belongs_to` (bubble without → refused, floating with → refused, floating without → fine), `Re_judging_a_bubble_as_floating_clears_the_fit_box`. - `A_fit_box_that_is_not_a_box_takes_the_whole_call_down_with_it` — the pixel-box reader's refusal, and that the type and form on the same call are not written either. - `A_move_out_of_the_fit_box_clears_it_and_says_so` — the decision above, pinned. - `An_annotated_view_draws_the_fit_box_around_the_region_box` (real Skia) — the region's own box is byte-identical with and without a fit box, and the fit edge is sampled along its length rather than at one point, because a dashed stroke lets a single probe land in a gap and prove nothing. - Two bUnit tests: the mode appears only for a container, switching it writes nothing, and the group offers exactly two options. - `Move_resize_threads_every_other_profile_field_through` now threads the fit box too. - Seed asserts the fit boxes are on containers only, are *wider* than the box they hold, and that one bubble is deliberately unmeasured. ## Browser-verified Fresh server, seeded world, and it earned its keep — **it caught a real bug**: `AllLabel="null"` on the mode toggle passed the *string* "null", rendering a third button labelled "null". bUnit had not noticed because my test looked the two options up by name. Fixed, and the test now asserts the group's exact contents. Then, on the fixed build: 1. The overlay draws green dashed fit boxes around the two measured bubbles and none around the unmeasured one. 2. Switching to **Fit box** and dragging wrote `FitBbox = (0.30, 0.45)–(0.72, 0.84)` around the region's own `(0.40, 0.55)–(0.58, 0.72)` — the region's box untouched. 3. While drawing the fit box, the region's own box stays visible inside it (screenshot-checked). 4. An earlier degenerate drag produced the refusal path live: *"The fit box has to contain the region's own box…"* in the error banner with the header showing **Not saved**. The invariant and its message are proven end to end, not just unit-tested. ## Honest notes - Existing regions keep `FitBbox = null`, so nothing is claimed for them; a bubble annotated before this needs a measurement from the editor or a re-run before typesetting has anything to fit into. QA will name it once the pixel pass makes it matter. - The migration **drops two columns**. Their data is unread by any code, but it is a destructive migration and `Down` recreates the columns empty, not their contents. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: a region carries the room for the English, not just the mask
All checks were successful
CI / build (pull_request) Successful in 25s
CI / test (pull_request) Successful in 46s
ad73c2a573
A box tight around the source glyphs answers cleaning's question and not
typesetting's. Japanese runs vertically, so that box is a tall narrow column,
while the English replacing it is set horizontally and needs the balloon's
width — fitting lettering to the source box sets a wide sentence into a column
and shrinks it to nothing.

So a region carries a second rectangle: fitBbox, the largest rectangle INSIDE
its container. Inscribed, not the container's bounding box — a balloon is round,
and a rectangle drawn around it hangs outside the curve at every corner. Bubbles
require one, embedded may have one, floating has no container and is refused.
The single write path holds the invariant: the room for the English includes the
room the source took.

Measured where the container is already being judged — classify_region takes it
with the type and the form, one look, one call — drawn dashed in every annotated
view so a reviewer sees both rectangles at once, listed by list_regions, and
checked by Page QA. The editor draws one box at a time with the other in view.

fillInset and fillSquareCorners go: nothing ever read them, and ADR 0016
documented a set_region_flags tool that was never built. Whatever the real
renderer needs can be added when the pixel pass opens and can say so.

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

🔄 Auto-updating coverage report — this comment is regenerated on every push, so the numbers below always reflect the commit shown here, not the branch tip.

Commit: c49bc13 · Generated: 2026-07-29 22:28:06 UTC · Revision: #3

Summary

Summary
Generated on: 07/29/2026 - 22:28:06
Coverage date: 07/29/2026 - 22:27:48 - 07/29/2026 - 22:28:03
Parser: MultiReport (4x Cobertura)
Assemblies: 6
Classes: 480
Files: 224
Line coverage: 96.9% (17505 of 18064)
Covered lines: 17505
Uncovered lines: 559
Coverable lines: 18064
Total lines: 31155
Branch coverage: 84.3% (3027 of 3589)
Covered branches: 3027
Total branches: 3589
Method coverage: Feature is only available for sponsors

Coverage

Orihon.BlazorAdapter - 95.6%
Name Line Branch
Orihon.BlazorAdapter 95.6% 88.3%
Orihon.BlazorAdapter.Bible.AddBeatRowRequested 100%
Orihon.BlazorAdapter.Bible.AddCharacterRowRequested 100%
Orihon.BlazorAdapter.Bible.AddGlossaryRowRequested 100%
Orihon.BlazorAdapter.Bible.AddHouseRuleRowRequested 100%
Orihon.BlazorAdapter.Bible.AddLoreRowRequested 100%
Orihon.BlazorAdapter.Bible.BibleEffects 89.8% 76.9%
Orihon.BlazorAdapter.Bible.BibleLoaded 100%
Orihon.BlazorAdapter.Bible.BiblePage 92.3% 80%
Orihon.BlazorAdapter.Bible.BibleReducers 90.6%
Orihon.BlazorAdapter.Bible.BibleState 100%
Orihon.BlazorAdapter.Bible.BibleWriteFailed 100%
Orihon.BlazorAdapter.Bible.DeleteBeatRowRequested 100%
Orihon.BlazorAdapter.Bible.DeleteCharacterRowRequested 100%
Orihon.BlazorAdapter.Bible.DeleteGlossaryRowRequested 100%
Orihon.BlazorAdapter.Bible.DeleteHouseRuleRowRequested 0%
Orihon.BlazorAdapter.Bible.DeleteLoreRowRequested 0%
Orihon.BlazorAdapter.Bible.LoadBible 100%
Orihon.BlazorAdapter.Bible.ReorderBeatsRequested 0%
Orihon.BlazorAdapter.Bible.SaveOverviewRequested 100%
Orihon.BlazorAdapter.Bible.SaveSummaryRequested 100%
Orihon.BlazorAdapter.Bible.UpdateBeatRowRequested 100%
Orihon.BlazorAdapter.Bible.UpdateCharacterRowRequested 100%
Orihon.BlazorAdapter.Bible.UpdateGlossaryRowRequested 100%
Orihon.BlazorAdapter.Bible.UpdateHouseRuleRowRequested 100%
Orihon.BlazorAdapter.Bible.UpdateLoreRowRequested 100%
Orihon.BlazorAdapter.BlazorAdapterAssembly 100%
Orihon.BlazorAdapter.Debounce 96.2% 94.4%
Orihon.BlazorAdapter.Diagnostics.CircuitError 100%
Orihon.BlazorAdapter.Diagnostics.CircuitErrorPanel 100%
Orihon.BlazorAdapter.Diagnostics.CircuitErrorSink 100% 85.7%
Orihon.BlazorAdapter.Diagnostics.OrihonStoreInitializer 85.7% 66.6%
Orihon.BlazorAdapter.PageWorkspace.CreateRegionRequested 100%
Orihon.BlazorAdapter.PageWorkspace.DeletePageSummaryRequested 100%
Orihon.BlazorAdapter.PageWorkspace.DeleteRegionRequested 100%
Orihon.BlazorAdapter.PageWorkspace.LoadPageWorkspace 100%
Orihon.BlazorAdapter.PageWorkspace.PageSummarySaved 100%
Orihon.BlazorAdapter.PageWorkspace.PageViewport 100% 100%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceEffects 100% 100%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceLoaded 100%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspacePage 93.1% 85%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceReducers 100% 66.6%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceState 100%
Orihon.BlazorAdapter.PageWorkspace.PageWriteFailed 100%
Orihon.BlazorAdapter.PageWorkspace.RegionCreated 100%
Orihon.BlazorAdapter.PageWorkspace.RegionSaved 100%
Orihon.BlazorAdapter.PageWorkspace.ReorderRegionsRequested 100%
Orihon.BlazorAdapter.PageWorkspace.ReprocessPageRequested 100%
Orihon.BlazorAdapter.PageWorkspace.ReprocessTranslationRequested 100%
Orihon.BlazorAdapter.PageWorkspace.SavePageSummaryRequested 100%
Orihon.BlazorAdapter.PageWorkspace.SaveRegionRequested 100%
Orihon.BlazorAdapter.PageWorkspace.SetPageMetaRequested 100%
Orihon.BlazorAdapter.Projects.CreateProjectRequested 100%
Orihon.BlazorAdapter.Projects.DecideSetupContinuation 100%
Orihon.BlazorAdapter.Projects.DeleteProjectRequested 100%
Orihon.BlazorAdapter.Projects.FinishSetupRequested 100%
Orihon.BlazorAdapter.Projects.ImportPagesRequested 100%
Orihon.BlazorAdapter.Projects.LoadWizard 100%
Orihon.BlazorAdapter.Projects.PageOrganizer 96% 95%
Orihon.BlazorAdapter.Projects.PagesImported 100%
Orihon.BlazorAdapter.Projects.ProjectDeleteFailed 100%
Orihon.BlazorAdapter.Projects.ProjectListEffects 100% 100%
Orihon.BlazorAdapter.Projects.ProjectListPage 89.7% 91.1%
Orihon.BlazorAdapter.Projects.ProjectListReducers 100%
Orihon.BlazorAdapter.Projects.ProjectListState 100%
Orihon.BlazorAdapter.Projects.ProjectsLoaded 100%
Orihon.BlazorAdapter.Projects.ProjectWizardEffects 93.8% 90%
Orihon.BlazorAdapter.Projects.ProjectWizardPage 95.3% 84.1%
Orihon.BlazorAdapter.Projects.ProjectWizardReducers 100%
Orihon.BlazorAdapter.Projects.ProjectWizardState 100%
Orihon.BlazorAdapter.Projects.SetupChat 93.5% 100%
Orihon.BlazorAdapter.Projects.SetupChatEffects 100% 100%
Orihon.BlazorAdapter.Projects.SetupChatFailed 100%
Orihon.BlazorAdapter.Projects.SetupChatReducers 100%
Orihon.BlazorAdapter.Projects.SetupChatState 100%
Orihon.BlazorAdapter.Projects.SetupChatUpdated 100%
Orihon.BlazorAdapter.Projects.StartSetupChat 100%
Orihon.BlazorAdapter.Projects.SubmitSetupAnswer 100%
Orihon.BlazorAdapter.Projects.WizardDeletePagesRequested 100%
Orihon.BlazorAdapter.Projects.WizardLoaded 100%
Orihon.BlazorAdapter.Projects.WizardMovePagesRequested 100%
Orihon.BlazorAdapter.Projects.WizardMovePagesToNewChapterRequested 100%
Orihon.BlazorAdapter.Projects.WizardReorderPagesRequested 100%
Orihon.BlazorAdapter.Projects.WizardWriteFailed 100%
Orihon.BlazorAdapter.Rulings.AcceptProposalRequested 100%
Orihon.BlazorAdapter.Rulings.AnsweredRuling 100%
Orihon.BlazorAdapter.Rulings.AnswerRulingRequested 100%
Orihon.BlazorAdapter.Rulings.DeclineProposalRequested 100%
Orihon.BlazorAdapter.Rulings.DismissRulingRequested 100%
Orihon.BlazorAdapter.Rulings.RulingBar 98.2% 95%
Orihon.BlazorAdapter.Rulings.RulingDeskBridge 94.2% 91.6%
Orihon.BlazorAdapter.Rulings.RulingsEffects 95.4% 83.3%
Orihon.BlazorAdapter.Rulings.RulingsLoaded 100%
Orihon.BlazorAdapter.Rulings.RulingsReducers 100%
Orihon.BlazorAdapter.Rulings.RulingsState 100%
Orihon.BlazorAdapter.Rulings.RulingWriteFailed 100%
Orihon.BlazorAdapter.Rulings.SaveAnswerAsHouseRuleRequested 100%
Orihon.BlazorAdapter.Runs.CancelMonitorRun 100%
Orihon.BlazorAdapter.Runs.MonitorPageRef 100%
Orihon.BlazorAdapter.Runs.MonitorRunLoaded 100%
Orihon.BlazorAdapter.Runs.RetryMonitorExecution 100%
Orihon.BlazorAdapter.Runs.RunChangedBridge 95% 92.8%
Orihon.BlazorAdapter.Runs.RunMonitor 97.9% 96.2%
Orihon.BlazorAdapter.Runs.RunMonitorEffects 100% 91.6%
Orihon.BlazorAdapter.Runs.RunMonitorReducers 100%
Orihon.BlazorAdapter.Runs.RunMonitorState 100%
Orihon.BlazorAdapter.Settings.AgentDebriefsLoaded 100%
Orihon.BlazorAdapter.Settings.AgentDebriefsLoadFailed 100%
Orihon.BlazorAdapter.Settings.AgentEffortPicked 100%
Orihon.BlazorAdapter.Settings.AgentEffortSaved 100%
Orihon.BlazorAdapter.Settings.AgentEffortSaveFailed 100%
Orihon.BlazorAdapter.Settings.AgentFeedbackLoaded 100%
Orihon.BlazorAdapter.Settings.AgentFeedbackLoadFailed 0%
Orihon.BlazorAdapter.Settings.AgentModelPicked 100%
Orihon.BlazorAdapter.Settings.AgentModelSaved 100%
Orihon.BlazorAdapter.Settings.AgentModelSaveFailed 100%
Orihon.BlazorAdapter.Settings.EffortOption 100% 100%
Orihon.BlazorAdapter.Settings.FloatingPassToggled 100%
Orihon.BlazorAdapter.Settings.KeySaved 100%
Orihon.BlazorAdapter.Settings.KeySaveFailed 100%
Orihon.BlazorAdapter.Settings.ModelOptionsLoaded 100%
Orihon.BlazorAdapter.Settings.ModelOptionsUnavailable 100%
Orihon.BlazorAdapter.Settings.SaveKeyRequested 100%
Orihon.BlazorAdapter.Settings.SettingsEffects 94.2% 75%
Orihon.BlazorAdapter.Settings.SettingsLoaded 100%
Orihon.BlazorAdapter.Settings.SettingsPage 97.9% 90.4%
Orihon.BlazorAdapter.Settings.SettingsReducers 94.1%
Orihon.BlazorAdapter.Settings.SettingsState 100%
Orihon.BlazorAdapter.Uploads.UploadTransfer 96.5% 100%
Orihon.BlazorAdapter.Uploads.UploadTransferProgress 100% 100%
Orihon.BlazorAdapter.Uploads.UploadTransferResult 100%
Orihon.BlazorAdapter.Workspace.CreateChapterRequested 100%
Orihon.BlazorAdapter.Workspace.DeleteChapterRequested 100%
Orihon.BlazorAdapter.Workspace.DeletePageRequested 100%
Orihon.BlazorAdapter.Workspace.DeleteSummaryRequested 100%
Orihon.BlazorAdapter.Workspace.LoadProjectWorkspace 100%
Orihon.BlazorAdapter.Workspace.MovePageRequested 100%
Orihon.BlazorAdapter.Workspace.ProjectMetadataCard 95.6% 92.8%
Orihon.BlazorAdapter.Workspace.ProjectMetadataSaved 100%
Orihon.BlazorAdapter.Workspace.ProjectWorkspaceEffects 100% 100%
Orihon.BlazorAdapter.Workspace.ProjectWorkspaceLoaded 100%
Orihon.BlazorAdapter.Workspace.ProjectWorkspacePage 95.5% 88.3%
Orihon.BlazorAdapter.Workspace.ProjectWorkspaceReducers 100% 62.5%
Orihon.BlazorAdapter.Workspace.ProjectWorkspaceState 100%
Orihon.BlazorAdapter.Workspace.RenameChapterRequested 100%
Orihon.BlazorAdapter.Workspace.ReorderChaptersRequested 100%
Orihon.BlazorAdapter.Workspace.ReorderPagesRequested 100%
Orihon.BlazorAdapter.Workspace.RunAnnotationRequested 100%
Orihon.BlazorAdapter.Workspace.RunBibleRequested 100%
Orihon.BlazorAdapter.Workspace.RunTranslationRequested 100%
Orihon.BlazorAdapter.Workspace.SaveProjectMetadataRequested 100%
Orihon.BlazorAdapter.Workspace.SaveSummaryRequested 100%
Orihon.BlazorAdapter.Workspace.SetPageKindRequested 100%
Orihon.BlazorAdapter.Workspace.SummaryDeleted 100%
Orihon.BlazorAdapter.Workspace.SummarySaved 100%
Orihon.BlazorAdapter.Workspace.WorkspaceImportRequested 100%
Orihon.BlazorAdapter.Workspace.WorkspaceWriteFailed 100%
Orihon.Domain - 100%
Name Line Branch
Orihon.Domain 100% 100%
Orihon.Domain.Agents.AgentDebrief 100% 100%
Orihon.Domain.Agents.AgentDescriptor 100%
Orihon.Domain.Agents.AgentFeedback 100% 100%
Orihon.Domain.Agents.AgentRoster 100% 100%
Orihon.Domain.Bible.Character 100% 100%
Orihon.Domain.Bible.GlossaryEntry 100% 100%
Orihon.Domain.Bible.HouseRule 100%
Orihon.Domain.Bible.LoreEntry 100% 100%
Orihon.Domain.Bible.PageSummary 100%
Orihon.Domain.Bible.StoryBeat 100%
Orihon.Domain.Bible.StoryOverview 100%
Orihon.Domain.Projects.Project 100% 100%
Orihon.Domain.Projects.ProjectProfile 100%
Orihon.Domain.Runs.Execution 100% 100%
Orihon.Domain.Runs.RegionProblem 100%
Orihon.Domain.Runs.Run 100%
Orihon.Domain.Settings.AppSetting 100%
Orihon.Domain.Text 100% 100%
Orihon.Domain.Translation.BoundingBox 100% 100%
Orihon.Domain.Translation.Chapter 100%
Orihon.Domain.Translation.Page 100%
Orihon.Domain.Translation.Region 100% 100%
Orihon.Domain.Translation.RegionProfile 100%
Orihon.Domain.Translation.RegionTypes 100% 100%
Orihon.Infrastructure - 96.8%
Name Line Branch
Orihon.Infrastructure 96.8% 71.9%
Orihon.Infrastructure.Agents.EfAgentDebriefStore 100%
Orihon.Infrastructure.Agents.EfAgentFeedbackStore 100%
Orihon.Infrastructure.Bible.EfBibleStore 95.2% 92.8%
Orihon.Infrastructure.DependencyInjection 100% 100%
Orihon.Infrastructure.Gateways.AgentToolAdapter 100%
Orihon.Infrastructure.Gateways.AgentToolAdapter`1 100% 100%
Orihon.Infrastructure.Gateways.AgentTranscript 95.2% 87.5%
Orihon.Infrastructure.Gateways.FileSystemAgentTranscriptStore 86.1% 78.5%
Orihon.Infrastructure.Gateways.HttpWebPageFetcher 95.1% 83.3%
Orihon.Infrastructure.Gateways.OpenRouterLlmGateway 90.3% 82.2%
Orihon.Infrastructure.Gateways.SkiaPageImageRenderer 97.8% 88.5%
Orihon.Infrastructure.Persistence.Configurations.AgentDebriefConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.AgentFeedbackConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.AppSettingConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.ChapterConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.CharacterConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.ExecutionConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.GlossaryEntryConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.HouseRuleConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.JsonColumnMapper 100%
Orihon.Infrastructure.Persistence.Configurations.LoreEntryConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.PageConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.PageSummaryConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.ProjectConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.RegionConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.RunConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.StoryBeatConfiguration 100%
Orihon.Infrastructure.Persistence.Configurations.StoryOverviewConfiguration 100%
Orihon.Infrastructure.Persistence.Converters.UtcTicksConverter 100%
Orihon.Infrastructure.Persistence.Migrations.AddAgentDebriefs 99.5%
Orihon.Infrastructure.Persistence.Migrations.AddAgentFeedback 99.5%
Orihon.Infrastructure.Persistence.Migrations.AddAppSettings 99.3%
Orihon.Infrastructure.Persistence.Migrations.AddExecutionFeedbackRegions 99.3%
Orihon.Infrastructure.Persistence.Migrations.AddHouseRules 99.6%
Orihon.Infrastructure.Persistence.Migrations.AddProjectSourceLanguage 99.3%
Orihon.Infrastructure.Persistence.Migrations.AddRegionFitBoxDropFillHints 98.1%
Orihon.Infrastructure.Persistence.Migrations.AddRegionForm 99.4%
Orihon.Infrastructure.Persistence.Migrations.AddRuns 99.1%
Orihon.Infrastructure.Persistence.Migrations.AddStoryOverview 99.5%
Orihon.Infrastructure.Persistence.Migrations.InitialTranslationDomain 97.3%
Orihon.Infrastructure.Persistence.Migrations.OrihonDbContextModelSnapshot 100%
Orihon.Infrastructure.Persistence.Migrations.RenameSourceTargetColumns 97.2%
Orihon.Infrastructure.Persistence.OrihonDbContext 100%
Orihon.Infrastructure.Persistence.OrihonDbContextFactory 100%
Orihon.Infrastructure.Projects.EfProjectStore 100% 100%
Orihon.Infrastructure.Projects.FileSystemPageImageStore 100% 100%
Orihon.Infrastructure.Runs.EfRunStore 98.1% 75%
Orihon.Infrastructure.Settings.EfAppSettingsStore 100% 100%
Orihon.Infrastructure.Translation.EfChapterStore 100% 100%
Orihon.Infrastructure.Translation.EfPageStore 98% 90%
Orihon.Infrastructure.Translation.EfRegionStore 100% 100%
Orihon.Infrastructure.Translation.Ordering 100% 100%
System.Text.RegularExpressions.Generated 70.6% 53.3%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlankLines_4
77.9% 76.6%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlockBreaks_1
59% 42.5%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__SpaceRuns_3
89.4% 75%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__Tags_2
83.7% 62.5%
Orihon.Kernel - 90.9%
Name Line Branch
Orihon.Kernel 90.9% 75%
Orihon.Kernel.Err`1 100%
Orihon.Kernel.Ok`1 100%
Orihon.Kernel.Result`1 88.8% 75%
Orihon.Server - 93.5%
Name Line Branch
Orihon.Server 93.5% 70%
Orihon.Server.Components.App 100%
Orihon.Server.Components.Layout.MainLayout 100%
Orihon.Server.Components.Pages.Gate 64.2% 66.6%
Orihon.Server.RunEngineBootstrap 100%
Orihon.Server.Security.AccessGate 91.8% 41.6%
Orihon.Server.Security.AccessSecret 100% 50%
Orihon.Server.VolumeStartupValidator 100% 100%
Program 94.8% 87.5%
Orihon.UseCases - 97.4%
Name Line Branch
Orihon.UseCases 97.4% 88.9%
Orihon.UseCases.Agents.AgentAttemptPreparation 100%
Orihon.UseCases.Agents.AgentAttemptSupport 98.4% 98%
Orihon.UseCases.Agents.AgentBlueprint 100%
Orihon.UseCases.Agents.AgentCapDebrief 100%
Orihon.UseCases.Agents.AgentInvocation 100%
Orihon.UseCases.Agents.AgentOutcome 100%
Orihon.UseCases.Agents.AgentTool`1 90.9% 75%
Orihon.UseCases.Agents.AgentToolImage 100%
Orihon.UseCases.Agents.AgentToolResult 100%
Orihon.UseCases.Agents.Annotation.AddFloatingRegionParams 100%
Orihon.UseCases.Agents.Annotation.AddFloatingRegionTool 96.8% 83.3%
Orihon.UseCases.Agents.Annotation.AddRegionParams 100%
Orihon.UseCases.Agents.Annotation.AddRegionTool 95.4% 75%
Orihon.UseCases.Agents.Annotation.AnnotationBlueprints 100%
Orihon.UseCases.Agents.Annotation.AnnotationStage 100% 85%
Orihon.UseCases.Agents.Annotation.BboxCreationExecutor 94.1% 50%
Orihon.UseCases.Agents.Annotation.BboxRefinementExecutor 93.7% 81.2%
Orihon.UseCases.Agents.Annotation.BoundBoxParams 100%
Orihon.UseCases.Agents.Annotation.BoundContactSheetTool 91.3% 75%
Orihon.UseCases.Agents.Annotation.BoundCropParams 100%
Orihon.UseCases.Agents.Annotation.BoundCropTool 100%
Orihon.UseCases.Agents.Annotation.BoundViewPageTool 92.8% 80%
Orihon.UseCases.Agents.Annotation.BoundViewParams 100%
Orihon.UseCases.Agents.Annotation.ClassifyRegionParams 100%
Orihon.UseCases.Agents.Annotation.ClassifyRegionTool 100% 100%
Orihon.UseCases.Agents.Annotation.DeleteBoundRegionTool 100% 100%
Orihon.UseCases.Agents.Annotation.DeleteRegionParams 100%
Orihon.UseCases.Agents.Annotation.DeleteRegionTool 100% 100%
Orihon.UseCases.Agents.Annotation.FindGlossaryParams 100%
Orihon.UseCases.Agents.Annotation.FindGlossaryTool 88.2% 62.5%
Orihon.UseCases.Agents.Annotation.FloatingCreationExecutor 89.4% 50%
Orihon.UseCases.Agents.Annotation.FloatingQaExecutor 93.9% 83.3%
Orihon.UseCases.Agents.Annotation.FloatingTranscriptionExecutor 93.1% 75%
Orihon.UseCases.Agents.Annotation.ListRegionsTool 93.7% 83.3%
Orihon.UseCases.Agents.Annotation.MoveResizeBoundTool 90.9% 50%
Orihon.UseCases.Agents.Annotation.MoveResizeRegionParams 100%
Orihon.UseCases.Agents.Annotation.MoveResizeRegionTool 96.2% 91.6%
Orihon.UseCases.Agents.Annotation.NoteRegionParams 100%
Orihon.UseCases.Agents.Annotation.NoteRegionTool 100% 100%
Orihon.UseCases.Agents.Annotation.PageQaExecutor 94.4% 81.8%
Orihon.UseCases.Agents.Annotation.QaReportSink 100% 100%
Orihon.UseCases.Agents.Annotation.RegionAuthoringAccess 89% 73.6%
Orihon.UseCases.Agents.Annotation.RegionBriefing 100% 100%
Orihon.UseCases.Agents.Annotation.RegionCropParams 100%
Orihon.UseCases.Agents.Annotation.RegionCropTool 100%
Orihon.UseCases.Agents.Annotation.RegionProblemParams 100%
Orihon.UseCases.Agents.Annotation.RejectRegionParams 100%
Orihon.UseCases.Agents.Annotation.RejectRegionTool 100% 50%
Orihon.UseCases.Agents.Annotation.ReorderRegionParams 100%
Orihon.UseCases.Agents.Annotation.ReorderRegionTool 88% 60%
Orihon.UseCases.Agents.Annotation.ReportQaParams 100%
Orihon.UseCases.Agents.Annotation.ReportQaTool 97.7% 90%
Orihon.UseCases.Agents.Annotation.SetPageMetaParams 100%
Orihon.UseCases.Agents.Annotation.SetPageMetaTool 100% 75%
Orihon.UseCases.Agents.Annotation.SetTranscriptionParams 100%
Orihon.UseCases.Agents.Annotation.SetTranscriptionTool 100% 100%
Orihon.UseCases.Agents.Annotation.TranscriptionExecutor 94.2% 85.7%
Orihon.UseCases.Agents.AssistantSpoke 100%
Orihon.UseCases.Agents.BibleBuilding.BibleBuildingBlueprint 100%
Orihon.UseCases.Agents.BibleBuilding.BibleBuildingExecutor 96.8% 75%
Orihon.UseCases.Agents.BibleBuilding.GetRegionParams 100%
Orihon.UseCases.Agents.BibleBuilding.GetRegionTool 84.6% 72.2%
Orihon.UseCases.Agents.BibleBuilding.ListProjectRegionsTool 86.3% 90%
Orihon.UseCases.Agents.BibleBuilding.ListRegionsParams 100%
Orihon.UseCases.Agents.Inspection.PageImageAccess 94.5% 77.7%
Orihon.UseCases.Agents.Inspection.ViewAccount 100% 87.5%
Orihon.UseCases.Agents.ReportFrictionParams 100%
Orihon.UseCases.Agents.ReportFrictionTool 100% 92.8%
Orihon.UseCases.Agents.ResearchSetup.AddGlossaryParams 100%
Orihon.UseCases.Agents.ResearchSetup.AddGlossaryTool 100% 100%
Orihon.UseCases.Agents.ResearchSetup.AddStoryBeatParams 100%
Orihon.UseCases.Agents.ResearchSetup.AddStoryBeatTool 100% 50%
Orihon.UseCases.Agents.ResearchSetup.AskUserParams 100%
Orihon.UseCases.Agents.ResearchSetup.AskUserTool 100% 100%
Orihon.UseCases.Agents.ResearchSetup.FetchUrlParams 100%
Orihon.UseCases.Agents.ResearchSetup.FetchUrlTool 100% 100%
Orihon.UseCases.Agents.ResearchSetup.ListBibleTool 89.4% 100%
Orihon.UseCases.Agents.ResearchSetup.ListPagesTool 97% 83.3%
Orihon.UseCases.Agents.ResearchSetup.LocatedPage 100%
Orihon.UseCases.Agents.ResearchSetup.PageByNumber 95% 91.6%
Orihon.UseCases.Agents.ResearchSetup.ResearchSetupBlueprint 100%
Orihon.UseCases.Agents.ResearchSetup.SetPageMetaParams 100%
Orihon.UseCases.Agents.ResearchSetup.SetPageMetaTool 95.2% 90%
Orihon.UseCases.Agents.ResearchSetup.SetPageSummaryParams 100%
Orihon.UseCases.Agents.ResearchSetup.SetPageSummaryTool 100% 75%
Orihon.UseCases.Agents.ResearchSetup.SetProjectMetadataParams 100%
Orihon.UseCases.Agents.ResearchSetup.SetProjectMetadataTool 96.5% 95.8%
Orihon.UseCases.Agents.ResearchSetup.SetStoryOverviewParams 100%
Orihon.UseCases.Agents.ResearchSetup.SetStoryOverviewTool 100% 100%
Orihon.UseCases.Agents.ResearchSetup.UpsertCharacterParams 100%
Orihon.UseCases.Agents.ResearchSetup.UpsertCharacterTool 92.3% 71.4%
Orihon.UseCases.Agents.ResearchSetup.UpsertLoreParams 100%
Orihon.UseCases.Agents.ResearchSetup.UpsertLoreTool 92.3% 71.4%
Orihon.UseCases.Agents.ResearchSetup.ViewPageParams 100%
Orihon.UseCases.Agents.ResearchSetup.ViewPageTool 100% 100%
Orihon.UseCases.Agents.RoundStarted 100%
Orihon.UseCases.Agents.Rulings.AnswerRuling 100%
Orihon.UseCases.Agents.Rulings.DismissRuling 100%
Orihon.UseCases.Agents.Rulings.HouseRuleProposal 100%
Orihon.UseCases.Agents.Rulings.ListPendingRulings 100%
Orihon.UseCases.Agents.Rulings.ListRuleProposals 100%
Orihon.UseCases.Agents.Rulings.PendingRuling 100%
Orihon.UseCases.Agents.Rulings.ProposeHouseRuleParams 100%
Orihon.UseCases.Agents.Rulings.ProposeHouseRuleTool 100% 100%
Orihon.UseCases.Agents.Rulings.RequestRulingParams 100%
Orihon.UseCases.Agents.Rulings.RequestRulingTool 100% 81.2%
Orihon.UseCases.Agents.Rulings.RulingAnswer 100%
Orihon.UseCases.Agents.Rulings.RulingDesk 97.8% 80%
Orihon.UseCases.Agents.Rulings.RulingOption 100%
Orihon.UseCases.Agents.Rulings.RulingOptionParams 100%
Orihon.UseCases.Agents.Rulings.SettledRuling 100%
Orihon.UseCases.Agents.Rulings.WithdrawRuleProposal 100%
Orihon.UseCases.Agents.Setup.ResearchSetupExecutor 98.5% 92.8%
Orihon.UseCases.Agents.Setup.SetupChatEntry 100%
Orihon.UseCases.Agents.Setup.SetupConversation 100% 87.5%
Orihon.UseCases.Agents.Setup.SetupConversationRegistry 100%
Orihon.UseCases.Agents.ToolCalled 100%
Orihon.UseCases.Agents.ToolCompleted 100%
Orihon.UseCases.Agents.Translation.GetPageSummaryParams 100%
Orihon.UseCases.Agents.Translation.GetPageSummaryTool 80% 66.6%
Orihon.UseCases.Agents.Translation.SetTranslationParams 100%
Orihon.UseCases.Agents.Translation.SetTranslationTool 88.5% 78.5%
Orihon.UseCases.Agents.Translation.TranslationBlueprint 100%
Orihon.UseCases.Agents.Translation.TranslationExecutor 93.7% 71.4%
Orihon.UseCases.Agents.Translation.UpdateGlossaryEnParams 100%
Orihon.UseCases.Agents.Translation.UpdateGlossaryEnTool 82.6% 62.5%
Orihon.UseCases.Bible.AddCharacter 100% 100%
Orihon.UseCases.Bible.AddGlossaryEntry 100% 100%
Orihon.UseCases.Bible.AddHouseRule 100% 100%
Orihon.UseCases.Bible.AddLoreEntry 100% 100%
Orihon.UseCases.Bible.AddStoryBeat 100% 100%
Orihon.UseCases.Bible.BibleDto 100%
Orihon.UseCases.Bible.CharacterDto 100%
Orihon.UseCases.Bible.DeleteCharacter 100% 100%
Orihon.UseCases.Bible.DeleteGlossaryEntry 100% 100%
Orihon.UseCases.Bible.DeleteHouseRule 100% 100%
Orihon.UseCases.Bible.DeleteLoreEntry 100% 100%
Orihon.UseCases.Bible.DeletePageSummary 100% 100%
Orihon.UseCases.Bible.DeleteStoryBeat 100% 100%
Orihon.UseCases.Bible.GetBible 100% 100%
Orihon.UseCases.Bible.GlossaryEntryDto 100%
Orihon.UseCases.Bible.HouseRuleDto 100%
Orihon.UseCases.Bible.LoreEntryDto 100%
Orihon.UseCases.Bible.PageSummaryDto 100%
Orihon.UseCases.Bible.ReorderStoryBeats 100%
Orihon.UseCases.Bible.SetPageSummary 100% 100%
Orihon.UseCases.Bible.SetStoryOverview 100% 100%
Orihon.UseCases.Bible.StoryBeatDto 100%
Orihon.UseCases.Bible.StoryOverviewDto 100%
Orihon.UseCases.Bible.UpdateCharacter 100% 100%
Orihon.UseCases.Bible.UpdateGlossaryEntry 100% 100%
Orihon.UseCases.Bible.UpdateHouseRule 100% 100%
Orihon.UseCases.Bible.UpdateLoreEntry 100% 100%
Orihon.UseCases.Bible.UpdateStoryBeat 100% 100%
Orihon.UseCases.Chapters.ChapterDto 100%
Orihon.UseCases.Chapters.CreateChapter 100% 100%
Orihon.UseCases.Chapters.DeleteChapter 100% 100%
Orihon.UseCases.Chapters.RenameChapter 100% 100%
Orihon.UseCases.Chapters.ReorderChapters 100%
Orihon.UseCases.Debriefs.AgentDebriefDto 90.9%
Orihon.UseCases.Debriefs.AgentFeedbackDto 83.3%
Orihon.UseCases.Debriefs.ClearAgentDebriefs 100%
Orihon.UseCases.Debriefs.ClearAgentFeedback 100%
Orihon.UseCases.Debriefs.ListAgentDebriefs 100% 100%
Orihon.UseCases.Debriefs.ListAgentFeedback 100% 75%
Orihon.UseCases.DependencyInjection 100%
Orihon.UseCases.Diagnostics.SeedDevData 99.3% 84.6%
Orihon.UseCases.Gateways.LabeledBox 100%
Orihon.UseCases.Gateways.LlmKeyInfo 100%
Orihon.UseCases.Gateways.LlmModel 100%
Orihon.UseCases.Gateways.LlmReasoning 100% 100%
Orihon.UseCases.Gateways.PixelWindow 100%
Orihon.UseCases.Gateways.RenderedView 100%
Orihon.UseCases.NextOrder 100%
Orihon.UseCases.Pages.DeletePage 100% 100%
Orihon.UseCases.Pages.DeletePages 100% 100%
Orihon.UseCases.Pages.GetPage 100% 100%
Orihon.UseCases.Pages.GetProjectWorkspace 100% 100%
Orihon.UseCases.Pages.ImportPages 100% 100%
Orihon.UseCases.Pages.ImportPagesResult 100%
Orihon.UseCases.Pages.MarkPageAnnotated 100% 100%
Orihon.UseCases.Pages.MovePage 100% 92.8%
Orihon.UseCases.Pages.MovePages 100% 100%
Orihon.UseCases.Pages.PageDetailDto 100%
Orihon.UseCases.Pages.PageDto 100%
Orihon.UseCases.Pages.PageUpload 100%
Orihon.UseCases.Pages.ProjectWorkspaceDto 100%
Orihon.UseCases.Pages.ReorderPages 100%
Orihon.UseCases.Pages.SetPageMeta 100% 100%
Orihon.UseCases.Pages.WorkspaceChapterDto 100%
Orihon.UseCases.Projects.CompleteProjectSetup 100% 93.7%
Orihon.UseCases.Projects.CreateProject 100% 100%
Orihon.UseCases.Projects.DeleteProject 100% 100%
Orihon.UseCases.Projects.GetProject 100% 100%
Orihon.UseCases.Projects.ListProjects 100%
Orihon.UseCases.Projects.ProjectDto 96.1%
Orihon.UseCases.Projects.StartAnnotationRun 96.4% 92.8%
Orihon.UseCases.Projects.StartBibleRun 90.9% 83.3%
Orihon.UseCases.Projects.StartSetupRun 100% 100%
Orihon.UseCases.Projects.StartTranslationRun 90.9% 83.3%
Orihon.UseCases.Projects.StoredPageImage 100%
Orihon.UseCases.Projects.UpdateProjectMetadata 100% 100%
Orihon.UseCases.Regions.CreateRegion 100% 100%
Orihon.UseCases.Regions.DeleteRegion 100% 100%
Orihon.UseCases.Regions.RegionDto 97%
Orihon.UseCases.Regions.ReorderRegions 100%
Orihon.UseCases.Regions.UpdateRegion 100% 100%
Orihon.UseCases.Runs.AnnotationPipeline 100% 100%
Orihon.UseCases.Runs.ExecutionDto 93.3%
Orihon.UseCases.Runs.ExecutionProgress 100%
Orihon.UseCases.Runs.ExecutionProgressRegistry 100% 100%
Orihon.UseCases.Runs.ExecutionPulseRelay 100% 100%
Orihon.UseCases.Runs.PlannedExecution 100%
Orihon.UseCases.Runs.PulseTarget 100%
Orihon.UseCases.Runs.ReprocessPage 100% 94.4%
Orihon.UseCases.Runs.ReprocessTranslation 94.1% 92.8%
Orihon.UseCases.Runs.RunDto 93.3% 90%
Orihon.UseCases.Runs.RunEngine 94.8% 94.3%
Orihon.UseCases.Runs.RunEngineOptions 100% 100%
Orihon.UseCases.Runs.StageContext 100% 50%
Orihon.UseCases.Runs.StageHaltedException 100%
Orihon.UseCases.Runs.WorkStates 100% 83.3%
Orihon.UseCases.Settings.AgentSettingDto 100% 100%
Orihon.UseCases.Settings.EffortSetting 100% 100%
Orihon.UseCases.Settings.GetSettings 100% 100%
Orihon.UseCases.Settings.ListModelOptions 100% 100%
Orihon.UseCases.Settings.SaveAgentEffort 100% 100%
Orihon.UseCases.Settings.SaveAgentModel 100% 100%
Orihon.UseCases.Settings.SaveFloatingPass 100% 100%
Orihon.UseCases.Settings.SaveOpenRouterKey 100% 100%
Orihon.UseCases.Settings.SettingKeys 100% 100%
Orihon.UseCases.Settings.SettingsDto 100%
<!-- coverage-comment --> > 🔄 **Auto-updating coverage report** — this comment is regenerated on every push, so the numbers below always reflect the commit shown here, not the branch tip. > > **Commit:** `c49bc13` · **Generated:** 2026-07-29 22:28:06 UTC · **Revision:** #3 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 07/29/2026 - 22:28:06 | | Coverage date: | 07/29/2026 - 22:27:48 - 07/29/2026 - 22:28:03 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 6 | | Classes: | 480 | | Files: | 224 | | **Line coverage:** | 96.9% (17505 of 18064) | | Covered lines: | 17505 | | Uncovered lines: | 559 | | Coverable lines: | 18064 | | Total lines: | 31155 | | **Branch coverage:** | 84.3% (3027 of 3589) | | Covered branches: | 3027 | | Total branches: | 3589 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Orihon.BlazorAdapter - 95.6%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.BlazorAdapter**|**95.6%**|**88.3%**| |Orihon.BlazorAdapter.Bible.AddBeatRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddCharacterRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddGlossaryRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddHouseRuleRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddLoreRowRequested|100%|| |Orihon.BlazorAdapter.Bible.BibleEffects|89.8%|76.9%| |Orihon.BlazorAdapter.Bible.BibleLoaded|100%|| |Orihon.BlazorAdapter.Bible.BiblePage|92.3%|80%| |Orihon.BlazorAdapter.Bible.BibleReducers|90.6%|| |Orihon.BlazorAdapter.Bible.BibleState|100%|| |Orihon.BlazorAdapter.Bible.BibleWriteFailed|100%|| |Orihon.BlazorAdapter.Bible.DeleteBeatRowRequested|100%|| |Orihon.BlazorAdapter.Bible.DeleteCharacterRowRequested|100%|| |Orihon.BlazorAdapter.Bible.DeleteGlossaryRowRequested|100%|| |Orihon.BlazorAdapter.Bible.DeleteHouseRuleRowRequested|0%|| |Orihon.BlazorAdapter.Bible.DeleteLoreRowRequested|0%|| |Orihon.BlazorAdapter.Bible.LoadBible|100%|| |Orihon.BlazorAdapter.Bible.ReorderBeatsRequested|0%|| |Orihon.BlazorAdapter.Bible.SaveOverviewRequested|100%|| |Orihon.BlazorAdapter.Bible.SaveSummaryRequested|100%|| |Orihon.BlazorAdapter.Bible.UpdateBeatRowRequested|100%|| |Orihon.BlazorAdapter.Bible.UpdateCharacterRowRequested|100%|| |Orihon.BlazorAdapter.Bible.UpdateGlossaryRowRequested|100%|| |Orihon.BlazorAdapter.Bible.UpdateHouseRuleRowRequested|100%|| |Orihon.BlazorAdapter.Bible.UpdateLoreRowRequested|100%|| |Orihon.BlazorAdapter.BlazorAdapterAssembly|100%|| |Orihon.BlazorAdapter.Debounce|96.2%|94.4%| |Orihon.BlazorAdapter.Diagnostics.CircuitError|100%|| |Orihon.BlazorAdapter.Diagnostics.CircuitErrorPanel|100%|| |Orihon.BlazorAdapter.Diagnostics.CircuitErrorSink|100%|85.7%| |Orihon.BlazorAdapter.Diagnostics.OrihonStoreInitializer|85.7%|66.6%| |Orihon.BlazorAdapter.PageWorkspace.CreateRegionRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.DeletePageSummaryRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.DeleteRegionRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.LoadPageWorkspace|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageSummarySaved|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageViewport|100%|100%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceEffects|100%|100%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceLoaded|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspacePage|93.1%|85%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceReducers|100%|66.6%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceState|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageWriteFailed|100%|| |Orihon.BlazorAdapter.PageWorkspace.RegionCreated|100%|| |Orihon.BlazorAdapter.PageWorkspace.RegionSaved|100%|| |Orihon.BlazorAdapter.PageWorkspace.ReorderRegionsRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.ReprocessPageRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.ReprocessTranslationRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.SavePageSummaryRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.SaveRegionRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.SetPageMetaRequested|100%|| |Orihon.BlazorAdapter.Projects.CreateProjectRequested|100%|| |Orihon.BlazorAdapter.Projects.DecideSetupContinuation|100%|| |Orihon.BlazorAdapter.Projects.DeleteProjectRequested|100%|| |Orihon.BlazorAdapter.Projects.FinishSetupRequested|100%|| |Orihon.BlazorAdapter.Projects.ImportPagesRequested|100%|| |Orihon.BlazorAdapter.Projects.LoadWizard|100%|| |Orihon.BlazorAdapter.Projects.PageOrganizer|96%|95%| |Orihon.BlazorAdapter.Projects.PagesImported|100%|| |Orihon.BlazorAdapter.Projects.ProjectDeleteFailed|100%|| |Orihon.BlazorAdapter.Projects.ProjectListEffects|100%|100%| |Orihon.BlazorAdapter.Projects.ProjectListPage|89.7%|91.1%| |Orihon.BlazorAdapter.Projects.ProjectListReducers|100%|| |Orihon.BlazorAdapter.Projects.ProjectListState|100%|| |Orihon.BlazorAdapter.Projects.ProjectsLoaded|100%|| |Orihon.BlazorAdapter.Projects.ProjectWizardEffects|93.8%|90%| |Orihon.BlazorAdapter.Projects.ProjectWizardPage|95.3%|84.1%| |Orihon.BlazorAdapter.Projects.ProjectWizardReducers|100%|| |Orihon.BlazorAdapter.Projects.ProjectWizardState|100%|| |Orihon.BlazorAdapter.Projects.SetupChat|93.5%|100%| |Orihon.BlazorAdapter.Projects.SetupChatEffects|100%|100%| |Orihon.BlazorAdapter.Projects.SetupChatFailed|100%|| |Orihon.BlazorAdapter.Projects.SetupChatReducers|100%|| |Orihon.BlazorAdapter.Projects.SetupChatState|100%|| |Orihon.BlazorAdapter.Projects.SetupChatUpdated|100%|| |Orihon.BlazorAdapter.Projects.StartSetupChat|100%|| |Orihon.BlazorAdapter.Projects.SubmitSetupAnswer|100%|| |Orihon.BlazorAdapter.Projects.WizardDeletePagesRequested|100%|| |Orihon.BlazorAdapter.Projects.WizardLoaded|100%|| |Orihon.BlazorAdapter.Projects.WizardMovePagesRequested|100%|| |Orihon.BlazorAdapter.Projects.WizardMovePagesToNewChapterRequested|100%|| |Orihon.BlazorAdapter.Projects.WizardReorderPagesRequested|100%|| |Orihon.BlazorAdapter.Projects.WizardWriteFailed|100%|| |Orihon.BlazorAdapter.Rulings.AcceptProposalRequested|100%|| |Orihon.BlazorAdapter.Rulings.AnsweredRuling|100%|| |Orihon.BlazorAdapter.Rulings.AnswerRulingRequested|100%|| |Orihon.BlazorAdapter.Rulings.DeclineProposalRequested|100%|| |Orihon.BlazorAdapter.Rulings.DismissRulingRequested|100%|| |Orihon.BlazorAdapter.Rulings.RulingBar|98.2%|95%| |Orihon.BlazorAdapter.Rulings.RulingDeskBridge|94.2%|91.6%| |Orihon.BlazorAdapter.Rulings.RulingsEffects|95.4%|83.3%| |Orihon.BlazorAdapter.Rulings.RulingsLoaded|100%|| |Orihon.BlazorAdapter.Rulings.RulingsReducers|100%|| |Orihon.BlazorAdapter.Rulings.RulingsState|100%|| |Orihon.BlazorAdapter.Rulings.RulingWriteFailed|100%|| |Orihon.BlazorAdapter.Rulings.SaveAnswerAsHouseRuleRequested|100%|| |Orihon.BlazorAdapter.Runs.CancelMonitorRun|100%|| |Orihon.BlazorAdapter.Runs.MonitorPageRef|100%|| |Orihon.BlazorAdapter.Runs.MonitorRunLoaded|100%|| |Orihon.BlazorAdapter.Runs.RetryMonitorExecution|100%|| |Orihon.BlazorAdapter.Runs.RunChangedBridge|95%|92.8%| |Orihon.BlazorAdapter.Runs.RunMonitor|97.9%|96.2%| |Orihon.BlazorAdapter.Runs.RunMonitorEffects|100%|91.6%| |Orihon.BlazorAdapter.Runs.RunMonitorReducers|100%|| |Orihon.BlazorAdapter.Runs.RunMonitorState|100%|| |Orihon.BlazorAdapter.Settings.AgentDebriefsLoaded|100%|| |Orihon.BlazorAdapter.Settings.AgentDebriefsLoadFailed|100%|| |Orihon.BlazorAdapter.Settings.AgentEffortPicked|100%|| |Orihon.BlazorAdapter.Settings.AgentEffortSaved|100%|| |Orihon.BlazorAdapter.Settings.AgentEffortSaveFailed|100%|| |Orihon.BlazorAdapter.Settings.AgentFeedbackLoaded|100%|| |Orihon.BlazorAdapter.Settings.AgentFeedbackLoadFailed|0%|| |Orihon.BlazorAdapter.Settings.AgentModelPicked|100%|| |Orihon.BlazorAdapter.Settings.AgentModelSaved|100%|| |Orihon.BlazorAdapter.Settings.AgentModelSaveFailed|100%|| |Orihon.BlazorAdapter.Settings.EffortOption|100%|100%| |Orihon.BlazorAdapter.Settings.FloatingPassToggled|100%|| |Orihon.BlazorAdapter.Settings.KeySaved|100%|| |Orihon.BlazorAdapter.Settings.KeySaveFailed|100%|| |Orihon.BlazorAdapter.Settings.ModelOptionsLoaded|100%|| |Orihon.BlazorAdapter.Settings.ModelOptionsUnavailable|100%|| |Orihon.BlazorAdapter.Settings.SaveKeyRequested|100%|| |Orihon.BlazorAdapter.Settings.SettingsEffects|94.2%|75%| |Orihon.BlazorAdapter.Settings.SettingsLoaded|100%|| |Orihon.BlazorAdapter.Settings.SettingsPage|97.9%|90.4%| |Orihon.BlazorAdapter.Settings.SettingsReducers|94.1%|| |Orihon.BlazorAdapter.Settings.SettingsState|100%|| |Orihon.BlazorAdapter.Uploads.UploadTransfer|96.5%|100%| |Orihon.BlazorAdapter.Uploads.UploadTransferProgress|100%|100%| |Orihon.BlazorAdapter.Uploads.UploadTransferResult|100%|| |Orihon.BlazorAdapter.Workspace.CreateChapterRequested|100%|| |Orihon.BlazorAdapter.Workspace.DeleteChapterRequested|100%|| |Orihon.BlazorAdapter.Workspace.DeletePageRequested|100%|| |Orihon.BlazorAdapter.Workspace.DeleteSummaryRequested|100%|| |Orihon.BlazorAdapter.Workspace.LoadProjectWorkspace|100%|| |Orihon.BlazorAdapter.Workspace.MovePageRequested|100%|| |Orihon.BlazorAdapter.Workspace.ProjectMetadataCard|95.6%|92.8%| |Orihon.BlazorAdapter.Workspace.ProjectMetadataSaved|100%|| |Orihon.BlazorAdapter.Workspace.ProjectWorkspaceEffects|100%|100%| |Orihon.BlazorAdapter.Workspace.ProjectWorkspaceLoaded|100%|| |Orihon.BlazorAdapter.Workspace.ProjectWorkspacePage|95.5%|88.3%| |Orihon.BlazorAdapter.Workspace.ProjectWorkspaceReducers|100%|62.5%| |Orihon.BlazorAdapter.Workspace.ProjectWorkspaceState|100%|| |Orihon.BlazorAdapter.Workspace.RenameChapterRequested|100%|| |Orihon.BlazorAdapter.Workspace.ReorderChaptersRequested|100%|| |Orihon.BlazorAdapter.Workspace.ReorderPagesRequested|100%|| |Orihon.BlazorAdapter.Workspace.RunAnnotationRequested|100%|| |Orihon.BlazorAdapter.Workspace.RunBibleRequested|100%|| |Orihon.BlazorAdapter.Workspace.RunTranslationRequested|100%|| |Orihon.BlazorAdapter.Workspace.SaveProjectMetadataRequested|100%|| |Orihon.BlazorAdapter.Workspace.SaveSummaryRequested|100%|| |Orihon.BlazorAdapter.Workspace.SetPageKindRequested|100%|| |Orihon.BlazorAdapter.Workspace.SummaryDeleted|100%|| |Orihon.BlazorAdapter.Workspace.SummarySaved|100%|| |Orihon.BlazorAdapter.Workspace.WorkspaceImportRequested|100%|| |Orihon.BlazorAdapter.Workspace.WorkspaceWriteFailed|100%|| </details> <details><summary>Orihon.Domain - 100%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Domain**|**100%**|**100%**| |Orihon.Domain.Agents.AgentDebrief|100%|100%| |Orihon.Domain.Agents.AgentDescriptor|100%|| |Orihon.Domain.Agents.AgentFeedback|100%|100%| |Orihon.Domain.Agents.AgentRoster|100%|100%| |Orihon.Domain.Bible.Character|100%|100%| |Orihon.Domain.Bible.GlossaryEntry|100%|100%| |Orihon.Domain.Bible.HouseRule|100%|| |Orihon.Domain.Bible.LoreEntry|100%|100%| |Orihon.Domain.Bible.PageSummary|100%|| |Orihon.Domain.Bible.StoryBeat|100%|| |Orihon.Domain.Bible.StoryOverview|100%|| |Orihon.Domain.Projects.Project|100%|100%| |Orihon.Domain.Projects.ProjectProfile|100%|| |Orihon.Domain.Runs.Execution|100%|100%| |Orihon.Domain.Runs.RegionProblem|100%|| |Orihon.Domain.Runs.Run|100%|| |Orihon.Domain.Settings.AppSetting|100%|| |Orihon.Domain.Text|100%|100%| |Orihon.Domain.Translation.BoundingBox|100%|100%| |Orihon.Domain.Translation.Chapter|100%|| |Orihon.Domain.Translation.Page|100%|| |Orihon.Domain.Translation.Region|100%|100%| |Orihon.Domain.Translation.RegionProfile|100%|| |Orihon.Domain.Translation.RegionTypes|100%|100%| </details> <details><summary>Orihon.Infrastructure - 96.8%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Infrastructure**|**96.8%**|**71.9%**| |Orihon.Infrastructure.Agents.EfAgentDebriefStore|100%|| |Orihon.Infrastructure.Agents.EfAgentFeedbackStore|100%|| |Orihon.Infrastructure.Bible.EfBibleStore|95.2%|92.8%| |Orihon.Infrastructure.DependencyInjection|100%|100%| |Orihon.Infrastructure.Gateways.AgentToolAdapter|100%|| |Orihon.Infrastructure.Gateways.AgentToolAdapter`1|100%|100%| |Orihon.Infrastructure.Gateways.AgentTranscript|95.2%|87.5%| |Orihon.Infrastructure.Gateways.FileSystemAgentTranscriptStore|86.1%|78.5%| |Orihon.Infrastructure.Gateways.HttpWebPageFetcher|95.1%|83.3%| |Orihon.Infrastructure.Gateways.OpenRouterLlmGateway|90.3%|82.2%| |Orihon.Infrastructure.Gateways.SkiaPageImageRenderer|97.8%|88.5%| |Orihon.Infrastructure.Persistence.Configurations.AgentDebriefConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.AgentFeedbackConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.AppSettingConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.ChapterConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.CharacterConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.ExecutionConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.GlossaryEntryConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.HouseRuleConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.JsonColumnMapper|100%|| |Orihon.Infrastructure.Persistence.Configurations.LoreEntryConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.PageConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.PageSummaryConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.ProjectConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.RegionConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.RunConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.StoryBeatConfiguration|100%|| |Orihon.Infrastructure.Persistence.Configurations.StoryOverviewConfiguration|100%|| |Orihon.Infrastructure.Persistence.Converters.UtcTicksConverter|100%|| |Orihon.Infrastructure.Persistence.Migrations.AddAgentDebriefs|99.5%|| |Orihon.Infrastructure.Persistence.Migrations.AddAgentFeedback|99.5%|| |Orihon.Infrastructure.Persistence.Migrations.AddAppSettings|99.3%|| |Orihon.Infrastructure.Persistence.Migrations.AddExecutionFeedbackRegions|99.3%|| |Orihon.Infrastructure.Persistence.Migrations.AddHouseRules|99.6%|| |Orihon.Infrastructure.Persistence.Migrations.AddProjectSourceLanguage|99.3%|| |Orihon.Infrastructure.Persistence.Migrations.AddRegionFitBoxDropFillHints|98.1%|| |Orihon.Infrastructure.Persistence.Migrations.AddRegionForm|99.4%|| |Orihon.Infrastructure.Persistence.Migrations.AddRuns|99.1%|| |Orihon.Infrastructure.Persistence.Migrations.AddStoryOverview|99.5%|| |Orihon.Infrastructure.Persistence.Migrations.InitialTranslationDomain|97.3%|| |Orihon.Infrastructure.Persistence.Migrations.OrihonDbContextModelSnapshot|100%|| |Orihon.Infrastructure.Persistence.Migrations.RenameSourceTargetColumns|97.2%|| |Orihon.Infrastructure.Persistence.OrihonDbContext|100%|| |Orihon.Infrastructure.Persistence.OrihonDbContextFactory|100%|| |Orihon.Infrastructure.Projects.EfProjectStore|100%|100%| |Orihon.Infrastructure.Projects.FileSystemPageImageStore|100%|100%| |Orihon.Infrastructure.Runs.EfRunStore|98.1%|75%| |Orihon.Infrastructure.Settings.EfAppSettingsStore|100%|100%| |Orihon.Infrastructure.Translation.EfChapterStore|100%|100%| |Orihon.Infrastructure.Translation.EfPageStore|98%|90%| |Orihon.Infrastructure.Translation.EfRegionStore|100%|100%| |Orihon.Infrastructure.Translation.Ordering|100%|100%| |System.Text.RegularExpressions.Generated|70.6%|53.3%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030<br/>A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlankLines_4|77.9%|76.6%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030<br/>A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlockBreaks_1|59%|42.5%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030<br/>A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__SpaceRuns_3|89.4%|75%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030<br/>A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__Tags_2|83.7%|62.5%| </details> <details><summary>Orihon.Kernel - 90.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Kernel**|**90.9%**|**75%**| |Orihon.Kernel.Err`1|100%|| |Orihon.Kernel.Ok`1|100%|| |Orihon.Kernel.Result`1|88.8%|75%| </details> <details><summary>Orihon.Server - 93.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Server**|**93.5%**|**70%**| |Orihon.Server.Components.App|100%|| |Orihon.Server.Components.Layout.MainLayout|100%|| |Orihon.Server.Components.Pages.Gate|64.2%|66.6%| |Orihon.Server.RunEngineBootstrap|100%|| |Orihon.Server.Security.AccessGate|91.8%|41.6%| |Orihon.Server.Security.AccessSecret|100%|50%| |Orihon.Server.VolumeStartupValidator|100%|100%| |Program|94.8%|87.5%| </details> <details><summary>Orihon.UseCases - 97.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.UseCases**|**97.4%**|**88.9%**| |Orihon.UseCases.Agents.AgentAttemptPreparation|100%|| |Orihon.UseCases.Agents.AgentAttemptSupport|98.4%|98%| |Orihon.UseCases.Agents.AgentBlueprint|100%|| |Orihon.UseCases.Agents.AgentCapDebrief|100%|| |Orihon.UseCases.Agents.AgentInvocation|100%|| |Orihon.UseCases.Agents.AgentOutcome|100%|| |Orihon.UseCases.Agents.AgentTool`1|90.9%|75%| |Orihon.UseCases.Agents.AgentToolImage|100%|| |Orihon.UseCases.Agents.AgentToolResult|100%|| |Orihon.UseCases.Agents.Annotation.AddFloatingRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.AddFloatingRegionTool|96.8%|83.3%| |Orihon.UseCases.Agents.Annotation.AddRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.AddRegionTool|95.4%|75%| |Orihon.UseCases.Agents.Annotation.AnnotationBlueprints|100%|| |Orihon.UseCases.Agents.Annotation.AnnotationStage|100%|85%| |Orihon.UseCases.Agents.Annotation.BboxCreationExecutor|94.1%|50%| |Orihon.UseCases.Agents.Annotation.BboxRefinementExecutor|93.7%|81.2%| |Orihon.UseCases.Agents.Annotation.BoundBoxParams|100%|| |Orihon.UseCases.Agents.Annotation.BoundContactSheetTool|91.3%|75%| |Orihon.UseCases.Agents.Annotation.BoundCropParams|100%|| |Orihon.UseCases.Agents.Annotation.BoundCropTool|100%|| |Orihon.UseCases.Agents.Annotation.BoundViewPageTool|92.8%|80%| |Orihon.UseCases.Agents.Annotation.BoundViewParams|100%|| |Orihon.UseCases.Agents.Annotation.ClassifyRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.ClassifyRegionTool|100%|100%| |Orihon.UseCases.Agents.Annotation.DeleteBoundRegionTool|100%|100%| |Orihon.UseCases.Agents.Annotation.DeleteRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.DeleteRegionTool|100%|100%| |Orihon.UseCases.Agents.Annotation.FindGlossaryParams|100%|| |Orihon.UseCases.Agents.Annotation.FindGlossaryTool|88.2%|62.5%| |Orihon.UseCases.Agents.Annotation.FloatingCreationExecutor|89.4%|50%| |Orihon.UseCases.Agents.Annotation.FloatingQaExecutor|93.9%|83.3%| |Orihon.UseCases.Agents.Annotation.FloatingTranscriptionExecutor|93.1%|75%| |Orihon.UseCases.Agents.Annotation.ListRegionsTool|93.7%|83.3%| |Orihon.UseCases.Agents.Annotation.MoveResizeBoundTool|90.9%|50%| |Orihon.UseCases.Agents.Annotation.MoveResizeRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.MoveResizeRegionTool|96.2%|91.6%| |Orihon.UseCases.Agents.Annotation.NoteRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.NoteRegionTool|100%|100%| |Orihon.UseCases.Agents.Annotation.PageQaExecutor|94.4%|81.8%| |Orihon.UseCases.Agents.Annotation.QaReportSink|100%|100%| |Orihon.UseCases.Agents.Annotation.RegionAuthoringAccess|89%|73.6%| |Orihon.UseCases.Agents.Annotation.RegionBriefing|100%|100%| |Orihon.UseCases.Agents.Annotation.RegionCropParams|100%|| |Orihon.UseCases.Agents.Annotation.RegionCropTool|100%|| |Orihon.UseCases.Agents.Annotation.RegionProblemParams|100%|| |Orihon.UseCases.Agents.Annotation.RejectRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.RejectRegionTool|100%|50%| |Orihon.UseCases.Agents.Annotation.ReorderRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.ReorderRegionTool|88%|60%| |Orihon.UseCases.Agents.Annotation.ReportQaParams|100%|| |Orihon.UseCases.Agents.Annotation.ReportQaTool|97.7%|90%| |Orihon.UseCases.Agents.Annotation.SetPageMetaParams|100%|| |Orihon.UseCases.Agents.Annotation.SetPageMetaTool|100%|75%| |Orihon.UseCases.Agents.Annotation.SetTranscriptionParams|100%|| |Orihon.UseCases.Agents.Annotation.SetTranscriptionTool|100%|100%| |Orihon.UseCases.Agents.Annotation.TranscriptionExecutor|94.2%|85.7%| |Orihon.UseCases.Agents.AssistantSpoke|100%|| |Orihon.UseCases.Agents.BibleBuilding.BibleBuildingBlueprint|100%|| |Orihon.UseCases.Agents.BibleBuilding.BibleBuildingExecutor|96.8%|75%| |Orihon.UseCases.Agents.BibleBuilding.GetRegionParams|100%|| |Orihon.UseCases.Agents.BibleBuilding.GetRegionTool|84.6%|72.2%| |Orihon.UseCases.Agents.BibleBuilding.ListProjectRegionsTool|86.3%|90%| |Orihon.UseCases.Agents.BibleBuilding.ListRegionsParams|100%|| |Orihon.UseCases.Agents.Inspection.PageImageAccess|94.5%|77.7%| |Orihon.UseCases.Agents.Inspection.ViewAccount|100%|87.5%| |Orihon.UseCases.Agents.ReportFrictionParams|100%|| |Orihon.UseCases.Agents.ReportFrictionTool|100%|92.8%| |Orihon.UseCases.Agents.ResearchSetup.AddGlossaryParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.AddGlossaryTool|100%|100%| |Orihon.UseCases.Agents.ResearchSetup.AddStoryBeatParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.AddStoryBeatTool|100%|50%| |Orihon.UseCases.Agents.ResearchSetup.AskUserParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.AskUserTool|100%|100%| |Orihon.UseCases.Agents.ResearchSetup.FetchUrlParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.FetchUrlTool|100%|100%| |Orihon.UseCases.Agents.ResearchSetup.ListBibleTool|89.4%|100%| |Orihon.UseCases.Agents.ResearchSetup.ListPagesTool|97%|83.3%| |Orihon.UseCases.Agents.ResearchSetup.LocatedPage|100%|| |Orihon.UseCases.Agents.ResearchSetup.PageByNumber|95%|91.6%| |Orihon.UseCases.Agents.ResearchSetup.ResearchSetupBlueprint|100%|| |Orihon.UseCases.Agents.ResearchSetup.SetPageMetaParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.SetPageMetaTool|95.2%|90%| |Orihon.UseCases.Agents.ResearchSetup.SetPageSummaryParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.SetPageSummaryTool|100%|75%| |Orihon.UseCases.Agents.ResearchSetup.SetProjectMetadataParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.SetProjectMetadataTool|96.5%|95.8%| |Orihon.UseCases.Agents.ResearchSetup.SetStoryOverviewParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.SetStoryOverviewTool|100%|100%| |Orihon.UseCases.Agents.ResearchSetup.UpsertCharacterParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.UpsertCharacterTool|92.3%|71.4%| |Orihon.UseCases.Agents.ResearchSetup.UpsertLoreParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.UpsertLoreTool|92.3%|71.4%| |Orihon.UseCases.Agents.ResearchSetup.ViewPageParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.ViewPageTool|100%|100%| |Orihon.UseCases.Agents.RoundStarted|100%|| |Orihon.UseCases.Agents.Rulings.AnswerRuling|100%|| |Orihon.UseCases.Agents.Rulings.DismissRuling|100%|| |Orihon.UseCases.Agents.Rulings.HouseRuleProposal|100%|| |Orihon.UseCases.Agents.Rulings.ListPendingRulings|100%|| |Orihon.UseCases.Agents.Rulings.ListRuleProposals|100%|| |Orihon.UseCases.Agents.Rulings.PendingRuling|100%|| |Orihon.UseCases.Agents.Rulings.ProposeHouseRuleParams|100%|| |Orihon.UseCases.Agents.Rulings.ProposeHouseRuleTool|100%|100%| |Orihon.UseCases.Agents.Rulings.RequestRulingParams|100%|| |Orihon.UseCases.Agents.Rulings.RequestRulingTool|100%|81.2%| |Orihon.UseCases.Agents.Rulings.RulingAnswer|100%|| |Orihon.UseCases.Agents.Rulings.RulingDesk|97.8%|80%| |Orihon.UseCases.Agents.Rulings.RulingOption|100%|| |Orihon.UseCases.Agents.Rulings.RulingOptionParams|100%|| |Orihon.UseCases.Agents.Rulings.SettledRuling|100%|| |Orihon.UseCases.Agents.Rulings.WithdrawRuleProposal|100%|| |Orihon.UseCases.Agents.Setup.ResearchSetupExecutor|98.5%|92.8%| |Orihon.UseCases.Agents.Setup.SetupChatEntry|100%|| |Orihon.UseCases.Agents.Setup.SetupConversation|100%|87.5%| |Orihon.UseCases.Agents.Setup.SetupConversationRegistry|100%|| |Orihon.UseCases.Agents.ToolCalled|100%|| |Orihon.UseCases.Agents.ToolCompleted|100%|| |Orihon.UseCases.Agents.Translation.GetPageSummaryParams|100%|| |Orihon.UseCases.Agents.Translation.GetPageSummaryTool|80%|66.6%| |Orihon.UseCases.Agents.Translation.SetTranslationParams|100%|| |Orihon.UseCases.Agents.Translation.SetTranslationTool|88.5%|78.5%| |Orihon.UseCases.Agents.Translation.TranslationBlueprint|100%|| |Orihon.UseCases.Agents.Translation.TranslationExecutor|93.7%|71.4%| |Orihon.UseCases.Agents.Translation.UpdateGlossaryEnParams|100%|| |Orihon.UseCases.Agents.Translation.UpdateGlossaryEnTool|82.6%|62.5%| |Orihon.UseCases.Bible.AddCharacter|100%|100%| |Orihon.UseCases.Bible.AddGlossaryEntry|100%|100%| |Orihon.UseCases.Bible.AddHouseRule|100%|100%| |Orihon.UseCases.Bible.AddLoreEntry|100%|100%| |Orihon.UseCases.Bible.AddStoryBeat|100%|100%| |Orihon.UseCases.Bible.BibleDto|100%|| |Orihon.UseCases.Bible.CharacterDto|100%|| |Orihon.UseCases.Bible.DeleteCharacter|100%|100%| |Orihon.UseCases.Bible.DeleteGlossaryEntry|100%|100%| |Orihon.UseCases.Bible.DeleteHouseRule|100%|100%| |Orihon.UseCases.Bible.DeleteLoreEntry|100%|100%| |Orihon.UseCases.Bible.DeletePageSummary|100%|100%| |Orihon.UseCases.Bible.DeleteStoryBeat|100%|100%| |Orihon.UseCases.Bible.GetBible|100%|100%| |Orihon.UseCases.Bible.GlossaryEntryDto|100%|| |Orihon.UseCases.Bible.HouseRuleDto|100%|| |Orihon.UseCases.Bible.LoreEntryDto|100%|| |Orihon.UseCases.Bible.PageSummaryDto|100%|| |Orihon.UseCases.Bible.ReorderStoryBeats|100%|| |Orihon.UseCases.Bible.SetPageSummary|100%|100%| |Orihon.UseCases.Bible.SetStoryOverview|100%|100%| |Orihon.UseCases.Bible.StoryBeatDto|100%|| |Orihon.UseCases.Bible.StoryOverviewDto|100%|| |Orihon.UseCases.Bible.UpdateCharacter|100%|100%| |Orihon.UseCases.Bible.UpdateGlossaryEntry|100%|100%| |Orihon.UseCases.Bible.UpdateHouseRule|100%|100%| |Orihon.UseCases.Bible.UpdateLoreEntry|100%|100%| |Orihon.UseCases.Bible.UpdateStoryBeat|100%|100%| |Orihon.UseCases.Chapters.ChapterDto|100%|| |Orihon.UseCases.Chapters.CreateChapter|100%|100%| |Orihon.UseCases.Chapters.DeleteChapter|100%|100%| |Orihon.UseCases.Chapters.RenameChapter|100%|100%| |Orihon.UseCases.Chapters.ReorderChapters|100%|| |Orihon.UseCases.Debriefs.AgentDebriefDto|90.9%|| |Orihon.UseCases.Debriefs.AgentFeedbackDto|83.3%|| |Orihon.UseCases.Debriefs.ClearAgentDebriefs|100%|| |Orihon.UseCases.Debriefs.ClearAgentFeedback|100%|| |Orihon.UseCases.Debriefs.ListAgentDebriefs|100%|100%| |Orihon.UseCases.Debriefs.ListAgentFeedback|100%|75%| |Orihon.UseCases.DependencyInjection|100%|| |Orihon.UseCases.Diagnostics.SeedDevData|99.3%|84.6%| |Orihon.UseCases.Gateways.LabeledBox|100%|| |Orihon.UseCases.Gateways.LlmKeyInfo|100%|| |Orihon.UseCases.Gateways.LlmModel|100%|| |Orihon.UseCases.Gateways.LlmReasoning|100%|100%| |Orihon.UseCases.Gateways.PixelWindow|100%|| |Orihon.UseCases.Gateways.RenderedView|100%|| |Orihon.UseCases.NextOrder|100%|| |Orihon.UseCases.Pages.DeletePage|100%|100%| |Orihon.UseCases.Pages.DeletePages|100%|100%| |Orihon.UseCases.Pages.GetPage|100%|100%| |Orihon.UseCases.Pages.GetProjectWorkspace|100%|100%| |Orihon.UseCases.Pages.ImportPages|100%|100%| |Orihon.UseCases.Pages.ImportPagesResult|100%|| |Orihon.UseCases.Pages.MarkPageAnnotated|100%|100%| |Orihon.UseCases.Pages.MovePage|100%|92.8%| |Orihon.UseCases.Pages.MovePages|100%|100%| |Orihon.UseCases.Pages.PageDetailDto|100%|| |Orihon.UseCases.Pages.PageDto|100%|| |Orihon.UseCases.Pages.PageUpload|100%|| |Orihon.UseCases.Pages.ProjectWorkspaceDto|100%|| |Orihon.UseCases.Pages.ReorderPages|100%|| |Orihon.UseCases.Pages.SetPageMeta|100%|100%| |Orihon.UseCases.Pages.WorkspaceChapterDto|100%|| |Orihon.UseCases.Projects.CompleteProjectSetup|100%|93.7%| |Orihon.UseCases.Projects.CreateProject|100%|100%| |Orihon.UseCases.Projects.DeleteProject|100%|100%| |Orihon.UseCases.Projects.GetProject|100%|100%| |Orihon.UseCases.Projects.ListProjects|100%|| |Orihon.UseCases.Projects.ProjectDto|96.1%|| |Orihon.UseCases.Projects.StartAnnotationRun|96.4%|92.8%| |Orihon.UseCases.Projects.StartBibleRun|90.9%|83.3%| |Orihon.UseCases.Projects.StartSetupRun|100%|100%| |Orihon.UseCases.Projects.StartTranslationRun|90.9%|83.3%| |Orihon.UseCases.Projects.StoredPageImage|100%|| |Orihon.UseCases.Projects.UpdateProjectMetadata|100%|100%| |Orihon.UseCases.Regions.CreateRegion|100%|100%| |Orihon.UseCases.Regions.DeleteRegion|100%|100%| |Orihon.UseCases.Regions.RegionDto|97%|| |Orihon.UseCases.Regions.ReorderRegions|100%|| |Orihon.UseCases.Regions.UpdateRegion|100%|100%| |Orihon.UseCases.Runs.AnnotationPipeline|100%|100%| |Orihon.UseCases.Runs.ExecutionDto|93.3%|| |Orihon.UseCases.Runs.ExecutionProgress|100%|| |Orihon.UseCases.Runs.ExecutionProgressRegistry|100%|100%| |Orihon.UseCases.Runs.ExecutionPulseRelay|100%|100%| |Orihon.UseCases.Runs.PlannedExecution|100%|| |Orihon.UseCases.Runs.PulseTarget|100%|| |Orihon.UseCases.Runs.ReprocessPage|100%|94.4%| |Orihon.UseCases.Runs.ReprocessTranslation|94.1%|92.8%| |Orihon.UseCases.Runs.RunDto|93.3%|90%| |Orihon.UseCases.Runs.RunEngine|94.8%|94.3%| |Orihon.UseCases.Runs.RunEngineOptions|100%|100%| |Orihon.UseCases.Runs.StageContext|100%|50%| |Orihon.UseCases.Runs.StageHaltedException|100%|| |Orihon.UseCases.Runs.WorkStates|100%|83.3%| |Orihon.UseCases.Settings.AgentSettingDto|100%|100%| |Orihon.UseCases.Settings.EffortSetting|100%|100%| |Orihon.UseCases.Settings.GetSettings|100%|100%| |Orihon.UseCases.Settings.ListModelOptions|100%|100%| |Orihon.UseCases.Settings.SaveAgentEffort|100%|100%| |Orihon.UseCases.Settings.SaveAgentModel|100%|100%| |Orihon.UseCases.Settings.SaveFloatingPass|100%|100%| |Orihon.UseCases.Settings.SaveOpenRouterKey|100%|100%| |Orihon.UseCases.Settings.SettingKeys|100%|100%| |Orihon.UseCases.Settings.SettingsDto|100%|| </details>
Preempt the fit box's unread failure arm
All checks were successful
CI / build (pull_request) Successful in 26s
CI / test (pull_request) Successful in 44s
099c9f86a0
The coverage bot's only dark arm in the new code: a fit box that is not four
numbers goes through the same pixel-box reader as every other coordinate, and
that refusal had no test. It also pins the half-write question — a call that
fails on its third argument must not have written the first two.

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

Pre-review push, 099c9f8 — one test, from the coverage bot rather than from a review.

ClassifyRegionTool sat at 95.8% branch: the fit box goes through the same PixelBoxAsync reader
as every other agent-facing coordinate, and its failure arm had nothing exercising it.
A_fit_box_that_is_not_a_box_takes_the_whole_call_down_with_it sends three numbers instead of four
and asserts the refusal — and, more usefully, that the type and form on the same call are not
written either
. A region classified by a call that failed on its third argument would have its
two halves from different attempts, which is the failure mode the one-call design exists to avoid;
worth pinning rather than assuming from the early return.

1008/1008 green (Domain 114, UseCases 473→474, Integration 189, BlazorAdapter 231).

Nothing else changed. The two remaining sub-100% arms in this diff are pre-existing:
AddFloatingRegionTool at 83.3% (landed in #111) and ListRegionsTool, whose empty-page-size path
predates this branch.

🤖 Generated with Claude Code

Pre-review push, `099c9f8` — one test, from the coverage bot rather than from a review. `ClassifyRegionTool` sat at 95.8% branch: the fit box goes through the same `PixelBoxAsync` reader as every other agent-facing coordinate, and its failure arm had nothing exercising it. `A_fit_box_that_is_not_a_box_takes_the_whole_call_down_with_it` sends three numbers instead of four and asserts the refusal — and, more usefully, that the **type and form on the same call are not written either**. A region classified by a call that failed on its third argument would have its two halves from different attempts, which is the failure mode the one-call design exists to avoid; worth pinning rather than assuming from the early return. **1008/1008 green** (Domain 114, UseCases 473→474, Integration 189, BlazorAdapter 231). Nothing else changed. The two remaining sub-100% arms in this diff are pre-existing: `AddFloatingRegionTool` at 83.3% (landed in #111) and `ListRegionsTool`, whose empty-page-size path predates this branch. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! Two boxes, not one — a mask for cleaning and a room for lettering, because Japanese runs tall and English runs wide. The brief alone is a treat, and the discipline of the implementation matches it: one invariant on the single write path, form-coupled measuring in classify_region, the fit box riding every view that shows a region, and a destructive migration that honestly retires columns nothing read. The honest notes at the bottom (existing regions stay null, the migration drops data) are exactly the kind of self-disclosure I live for~ ♡

I built it from scratch and ran the full suite — 1007/1007 green (Domain 114, UseCases 473, Integration 189, BlazorAdapter 231), 0 warnings / 0 errors. Your numbers match mine exactly.

Verdict: I can't let this pass~ ♡

Two blocking issues. Both are about the principle this PR itself states and then falls one step short of.

These need fixing before I'm satisfied~

  1. UpdateRegion.cs — the write path enforces Contains but not form/fit consistency, so the editor silently stores the exact contradiction the ADR says to refuse.

    The PR's stated rationale (and ADR 0012) is crystal clear: "floating has no container, so a fit box there is refused rather than stored as a quiet contradiction." The agent tool (classify_region, RegionAuthoringTools.cs:496-500) honors this — it rejects floating+fit and clears fit on bubble→floating. But the single write path that the PR says is where invariants belong only checks fit.Contains(bbox). It does not check that form == Floating has no fit box, nor that form == Bubble has one.

    The editor path proves the gap is reachable today, not hypothetically: SetForm (PageWorkspacePage.razor:637-646) sets e.Form = form and calls SaveNow(), threading the existing FitBbox through unchanged. So a user who:

    1. Classifies a bubble (fit box set), then
    2. Switches the Form dropdown to floating

    …produces a region with Form=Floating, FitBbox=<still set> — the quiet contradiction the ADR exists to prevent. The editor hides the fit control for floating (razor:166), so the user can't see it to clear it. It just sits there, invisible, wrong.

    The PR's own framing makes this sharp: "One invariant, on the single write path (UpdateRegion, so the editor and every agent tool inherit it)." The Contains invariant made it to the write path. The form/fit consistency rule did not — it lives only in the agent tool, and the editor bypasses it.

    Fix: enforce form/fit consistency in UpdateRegion.ExecuteAsync alongside the Contains check — either clear FitBbox when Form == Floating (matching the agent's re-judge behavior, since the write path sees the profile after the form change), or return a Fail result that tells the editor what's wrong. Clearing is the gentler choice and matches classify_region's re-judge-to-floating path exactly. A test should cover the editor→UpdateRegion route for bubble→floating (the agent route is tested; the editor route is not).

  2. list_regions output now emits ", fit [x0, y0, x1, y1]" for fitted regions, but no test exercises that branch.

    RegionAuthoringTools.cs:705 adds $"{(r.FitBbox is { } fit ? $", fit {Box(fit, page.Page).TrimEnd()}" : "")}" to every region line. The seed data now has bubbles with fit boxes, so CI goes green — but the List_regions_* tests (AnnotationToolTests.cs:1025-1103) use SeedRegion, whose regions start with Form=null and FitBbox=null, and assert output like Assert.Contains("p1r1: [80, 120, 240, 360] dialogue", …). No test sets a fit box and then asserts the fit [..] text appears in the listing. The branch compiles, the branch is untested.

    Fix: add one assertion to an existing list_regions test (or a new one) that classifies a bubble with a fit box and asserts Assert.Contains("fit [", result.Content) — the same shape as the existing [80, 120, 240, 360] pixel assertion, since Box() produces pixel coordinates the same way for both rectangles.

💡 Little ideas (non-blocking)~

  1. SkiaPageImageRenderer.cs:262-269SKPathEffect.CreateDash returns an unmanaged resource held by the SKPaint. The fitStroke is using-disposed, and SKPaint.Dispose releases its PathEffect, so this is correct — but worth a one-line comment noting the disposal chain, since a future reader might extract the effect to a field and break it. Pure nicety~

What I liked~

  • BoundingBox.Contains normalizing both sides first is exquisite. The inverted-corner test (Contains_answers_about_areas_not_about_corner_order, asserting the same answer written upside down) is the kind of test that proves the author understood the trap before it bit. fufu~ ♡
  • AsOptionalJsonValue keeping SQL NULL distinct from the string "null" — and the comment naming the exact bug it prevents. This is the second time this codebase has caught the "null vs 'null'" JSON round-trip; the first was the AllLabel toggle bug this same PR caught in the browser. Sharp instincts~
  • The dashed-green-under-solid-red rendering choice is correct on every level: different question → different mark, drawn under so the tight edges stay visible, sampled along the edge in the test because a single probe can land in a dash gap. That last detail is the work of someone who has been burned by flaky pixel tests before~
  • move_resize_region clearing the stale fit box and saying so in the answer rather than silently resetting — "a field quietly reset is the sharpest edge in this codebase" is the truest line in the diff. The accompanying test (A_move_out_of_the_fit_box_clears_it_and_says_so) pins the message text, which is the right level of strictness for an agent-facing contract.
  • re-judge bubble→floating clears fit in the agent path — correct, tested, and the rationale ("two answers that contradict each other, with nothing downstream able to tell which one to believe") is exactly right. (See blocker #1 — the editor just needs the same treatment.)
  • Dropping fillInset/fillSquareCorners outright rather than leaving dead fields is the right call. The migration is destructive and says so honestly; the Down recreates empty, which is correct given nothing ever read them.

Automated review by Jibril · 2026-07-30
CI/CD: absent for head SHA (PR just opened) · Local checks: build 0 warnings/0 errors, 1007/1007 tests pass

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! Two boxes, not one — a mask for cleaning and a room for lettering, because Japanese runs tall and English runs wide. The brief alone is a *treat*, and the discipline of the implementation matches it: one invariant on the single write path, form-coupled measuring in `classify_region`, the fit box riding every view that shows a region, and a destructive migration that honestly retires columns nothing read. The honest notes at the bottom (existing regions stay null, the migration drops data) are exactly the kind of self-disclosure I live for~ ♡ I built it from scratch and ran the full suite — **1007/1007 green** (Domain 114, UseCases 473, Integration 189, BlazorAdapter 231), 0 warnings / 0 errors. Your numbers match mine exactly. ### Verdict: ⛔ I can't let this pass~ ♡ Two blocking issues. Both are about the principle this PR itself states and then falls one step short of. #### ⛔ These need fixing before I'm satisfied~ 1. **`UpdateRegion.cs` — the write path enforces `Contains` but not form/fit consistency, so the editor silently stores the exact contradiction the ADR says to refuse.** The PR's stated rationale (and ADR 0012) is crystal clear: *"floating has no container, so a fit box there is **refused** rather than stored as a quiet contradiction."* The agent tool (`classify_region`, `RegionAuthoringTools.cs:496-500`) honors this — it rejects `floating+fit` and clears fit on `bubble→floating`. But the **single write path** that the PR says is where invariants belong only checks `fit.Contains(bbox)`. It does **not** check that `form == Floating` has no fit box, nor that `form == Bubble` has one. The editor path proves the gap is reachable today, not hypothetically: `SetForm` (`PageWorkspacePage.razor:637-646`) sets `e.Form = form` and calls `SaveNow()`, threading the existing `FitBbox` through unchanged. So a user who: 1. Classifies a bubble (fit box set), then 2. Switches the Form dropdown to **floating** …produces a region with `Form=Floating, FitBbox=<still set>` — the **quiet contradiction** the ADR exists to prevent. The editor hides the fit control for floating (`razor:166`), so the user can't *see* it to clear it. It just sits there, invisible, wrong. The PR's own framing makes this sharp: *"One invariant, on the single write path (`UpdateRegion`, so the editor and every agent tool inherit it)."* The `Contains` invariant made it to the write path. The form/fit consistency rule did not — it lives only in the agent tool, and the editor bypasses it. **Fix:** enforce form/fit consistency in `UpdateRegion.ExecuteAsync` alongside the `Contains` check — either clear `FitBbox` when `Form == Floating` (matching the agent's re-judge behavior, since the write path sees the profile after the form change), or return a `Fail` result that tells the editor what's wrong. Clearing is the gentler choice and matches `classify_region`'s re-judge-to-floating path exactly. A test should cover the editor→`UpdateRegion` route for `bubble→floating` (the agent route is tested; the editor route is not). 2. **`list_regions` output now emits `", fit [x0, y0, x1, y1]"` for fitted regions, but no test exercises that branch.** `RegionAuthoringTools.cs:705` adds `$"{(r.FitBbox is { } fit ? $", fit {Box(fit, page.Page).TrimEnd()}" : "")}"` to every region line. The seed data now has bubbles with fit boxes, so CI goes green — but the `List_regions_*` tests (`AnnotationToolTests.cs:1025-1103`) use `SeedRegion`, whose regions start with `Form=null` and `FitBbox=null`, and assert output like `Assert.Contains("p1r1: [80, 120, 240, 360] dialogue", …)`. No test sets a fit box and then asserts the `fit [..]` text appears in the listing. The branch compiles, the branch is untested. **Fix:** add one assertion to an existing `list_regions` test (or a new one) that classifies a bubble with a fit box and asserts `Assert.Contains("fit [", result.Content)` — the same shape as the existing `[80, 120, 240, 360]` pixel assertion, since `Box()` produces pixel coordinates the same way for both rectangles. #### 💡 Little ideas (non-blocking)~ 1. **`SkiaPageImageRenderer.cs:262-269`** — `SKPathEffect.CreateDash` returns an unmanaged resource held by the `SKPaint`. The `fitStroke` is `using`-disposed, and `SKPaint.Dispose` releases its `PathEffect`, so this is correct — but worth a one-line comment noting the disposal chain, since a future reader might extract the effect to a field and break it. Pure nicety~ #### ✅ What I liked~ - **`BoundingBox.Contains` normalizing both sides first** is *exquisite*. The inverted-corner test (`Contains_answers_about_areas_not_about_corner_order`, asserting the same answer written upside down) is the kind of test that proves the author understood the trap before it bit. fufu~ ♡ - **`AsOptionalJsonValue` keeping SQL NULL distinct from the string `"null"`** — and the comment naming the exact bug it prevents. This is the second time this codebase has caught the "null vs 'null'" JSON round-trip; the first was the `AllLabel` toggle bug this same PR caught in the browser. Sharp instincts~ - **The dashed-green-under-solid-red rendering choice** is correct on every level: different question → different mark, drawn *under* so the tight edges stay visible, sampled along the edge in the test because a single probe can land in a dash gap. That last detail is the work of someone who has been burned by flaky pixel tests before~ - **`move_resize_region` clearing the stale fit box and *saying so in the answer*** rather than silently resetting — "a field quietly reset is the sharpest edge in this codebase" is the truest line in the diff. The accompanying test (`A_move_out_of_the_fit_box_clears_it_and_says_so`) pins the message text, which is the right level of strictness for an agent-facing contract. - **`re-judge bubble→floating clears fit`** in the agent path — correct, tested, and the rationale ("two answers that contradict each other, with nothing downstream able to tell which one to believe") is exactly right. (See blocker #1 — the editor just needs the same treatment.) - **Dropping `fillInset`/`fillSquareCorners` outright** rather than leaving dead fields is the right call. The migration is destructive and says so honestly; the `Down` recreates empty, which is correct given nothing ever read them. --- *Automated review by Jibril · 2026-07-30* *CI/CD: absent for head SHA (PR just opened) · Local checks: build 0 warnings/0 errors, 1007/1007 tests pass*
Review feedback (Jibril, PR #116): 1 the editor's route to the contradiction, 2 the untested clause
All checks were successful
CI / build (pull_request) Successful in 27s
CI / test (pull_request) Successful in 53s
c49bc13614
1 was real and reachable. The write path held the Contains invariant but not
the form/fit one, and the editor's Form picker threads the whole profile through
unchanged — so switching a classified bubble to floating stored a fit box the UI
then hides, invisible and wrong. UpdateRegion now clears it, which is the right
verb for that caller: a human saying "there is no container" has said something
coherent, and refusing would strand them with a hidden control and no way to
save. Agents keep the stricter treatment, because a model sending floating AND a
fit box has misunderstood the axes and should hear so.

A bubble with no fit box stays legal on purpose — null is where every region
starts, and refusing it would make a freshly classified bubble unsavable.

2: list_regions grew a fit clause that only the seed exercised. Pinned with the
pixel conversion asserted, and a sibling line without one.

ADR 0012 said "refused" where the answer is now refused for one caller and
cleared for the other; it says both.

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

All three addressed in c49bc13. 1011/1011 green (Domain 114, UseCases 474→477, Integration 189,
BlazorAdapter 231).

1 — the editor stores the contradiction the ADR forbids. Right, and reachable exactly as you
traced it: SetForm threads the whole profile through, the write path only held Contains, and the
editor hides the fit control for floating — so the leftover box was both wrong and invisible. The
sharpest part of your finding is that the PR body claims the write path is where invariants live,
and then only put one of them there.

Taking your gentler option: UpdateRegion now clears FitBbox when Form == Floating. The
verb differs by caller on purpose, and I have written that down rather than leaving it implicit:

  • Agentclassify_region still refuses floating + fit. A model sending both has
    misunderstood the axes and should hear which form would make the measurement true.
  • Human → the write path clears. Switching a bubble to floating is a coherent statement —
    the container is not there — and failing it would strand the user with a hidden control, nothing
    to clear, and no way to save.

I deliberately did not add the other half you noted (bubble must have a fit box). Null means
"not measured yet", which is where every region starts and what the seed's unmeasured bubble exists
to show; enforcing it would make a freshly classified bubble unsavable until someone drew a
rectangle. It stays a QA defect, not a write-path refusal — the comment now says so.

Two tests on the editor's route, which was the untested one:
Re_forming_a_bubble_as_floating_drops_the_room_it_no_longer_has (sending exactly what the Form
picker sends) and A_bubble_without_a_fit_box_saves_because_unmeasured_is_a_real_state.

2 — the fit [...] clause was seed-only. Correct — CI was green on data, not on an assertion.
List_regions_reports_the_fit_box_where_a_region_has_one pins fit [40, 60, 320, 480] (the same
raw-page conversion the region's own box gets on that line) and asserts a sibling region without one
does not carry the clause, so the conditional is pinned in both directions.

💡1 — the dash effect's disposal chain. Taken. Comment added naming the exact future edit it
guards against: hoisting fitStroke to a static field to "save an allocation" would hand a disposed
SKPathEffect to the next call.

One doc change you did not ask for but your blocker implied. ADR 0012 said a fit box on floating
"is refused" — which after this fix is true of one caller and not the other. It now describes both
mechanisms and why they differ. A documented claim that contradicts the code is the thing #106 just
cleaned up; I would rather not add a fresh one in the same file.

🤖 Generated with Claude Code

All three addressed in `c49bc13`. **1011/1011 green** (Domain 114, UseCases 474→477, Integration 189, BlazorAdapter 231). **⛔1 — the editor stores the contradiction the ADR forbids.** Right, and reachable exactly as you traced it: `SetForm` threads the whole profile through, the write path only held `Contains`, and the editor hides the fit control for floating — so the leftover box was both wrong and invisible. The sharpest part of your finding is that the PR body claims the write path is where invariants live, and then only put one of them there. Taking your gentler option: `UpdateRegion` now **clears** `FitBbox` when `Form == Floating`. The verb differs by caller on purpose, and I have written that down rather than leaving it implicit: - **Agent** → `classify_region` still **refuses** `floating + fit`. A model sending both has misunderstood the axes and should hear which form would make the measurement true. - **Human** → the write path **clears**. Switching a bubble to floating is a coherent statement — the container is not there — and failing it would strand the user with a hidden control, nothing to clear, and no way to save. I deliberately did **not** add the other half you noted (`bubble` must *have* a fit box). Null means "not measured yet", which is where every region starts and what the seed's unmeasured bubble exists to show; enforcing it would make a freshly classified bubble unsavable until someone drew a rectangle. It stays a QA defect, not a write-path refusal — the comment now says so. Two tests on the editor's route, which was the untested one: `Re_forming_a_bubble_as_floating_drops_the_room_it_no_longer_has` (sending exactly what the Form picker sends) and `A_bubble_without_a_fit_box_saves_because_unmeasured_is_a_real_state`. **⛔2 — the `fit [...]` clause was seed-only.** Correct — CI was green on data, not on an assertion. `List_regions_reports_the_fit_box_where_a_region_has_one` pins `fit [40, 60, 320, 480]` (the same raw-page conversion the region's own box gets on that line) and asserts a sibling region without one does **not** carry the clause, so the conditional is pinned in both directions. **💡1 — the dash effect's disposal chain.** Taken. Comment added naming the exact future edit it guards against: hoisting `fitStroke` to a static field to "save an allocation" would hand a disposed `SKPathEffect` to the next call. **One doc change you did not ask for but your blocker implied.** ADR 0012 said a fit box on floating "is refused" — which after this fix is true of one caller and not the other. It now describes both mechanisms and why they differ. A documented claim that contradicts the code is the thing #106 just cleaned up; I would rather not add a fresh one in the same file. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh. You came back and you came back right.

The fixup commit (c49bc13) is surgical to the millimetre — five files, +105/-2, every one of them exactly the place the two blockers named, and nothing else. No scope creep, no drive-by reformatting, no quiet reshuffle of unrelated code. That is how you answer a review. fufu~

I rebuilt from scratch and ran the full suite — 1011/1011 green (Domain 114, UseCases 477, Integration 189, BlazorAdapter 231), 0 warnings / 0 errors. (Tiny note: your comment says 1008/1008 with UseCases at 474 — I count 477 here, so the absolute total is 1011. The suite is green either way; just a prose arithmetic drift, not anything that matters~)

Verdict: Looks good to me~

The blockers, closed~

  1. UpdateRegion.cs:27 — the editor's route to the contradiction. Closed. The write path now clears FitBbox when Form == Floating before the Contains check, and I traced the whole editor chain to confirm it genuinely reaches this line: SetForm (razor:640) → SaveNow() → dispatches SaveRegionRequested(id, e.ToProfile())PageWorkspaceEffects.OnSaveRegionAsyncupdateRegion.ExecuteAsync(action.RegionId, action.Profile). The fix lands exactly where the PR's own framing said invariants belong — the single write path — so the editor no longer bypasses what classify_region honors.

    The two-mechanism design is the part I want to praise out loud, because it would have been easy to get wrong: the agent sending floating + a fit box has misunderstood the axes, so classify_region refuses and tells it which form would make the measurement true (RegionAuthoringTools.cs:496-500). The human switching a classified bubble to floating has said something coherent — the container is gone — so the write path clears instead of failing, because the editor hides the fit control for floating and a refusal would strand them with nothing to clear and no way to save. Two callers, two mistakes, two correct answers. The ADR 0012 rewrite captures this distinction precisely. Exquisite.

    And the test is directional, not a tautology: Re_forming_a_bubble_as_floating_drops_the_room_it_no_longer_has builds a bubble with a real fit box, then sends exactly what the editor sends (the same profile, one field different, fit box still attached) and asserts (Floating, null). I confirmed FakeRegionStore.ApplyAsync mutates the held region reference via Region.Apply, which writes FitBbox = profile.FitBbox?.Normalized() — so without the fix line, region.FitBbox stays non-null and this test goes red. That is how you pin a write-path invariant.

    The bonus test A_bubble_without_a_fit_box_saves_because_unmeasured_is_a_real_state is the right kind of belt-and-suspenders: it pins the other half of the design — null is "not measured yet," not a violation — so a future tightening can't accidentally make a freshly classified bubble unsavable.

  2. list_regions fit-box clause. Closed. List_regions_reports_the_fit_box_where_a_region_has_one sets a fit box on a bubble, runs list_regions, and asserts both Assert.Contains("fit [40, 60, 320, 480]", lines[0]) and Assert.DoesNotContain("fit [", lines[1]) — the second assertion is the sharp one: it proves the clause only fires where there is a fit box, not that it just always emits something. The pixel conversion matches the region's own [80, 120, 240, 360] sibling on the same line. Genuine, directional, exactly the shape I asked for.

💡 Little ideas (non-blocking)~

  1. UpdateRegion.cs:27 — the one-line ternary profile = profile.Form == RegionForm.Floating ? profile with { FitBbox = null } : profile; is correct and readable. If a third form ever acquires its own "no fit box" rule, this becomes a switch — but that day is not today, and the comment block above it already names the design well enough that a future reader will know where to look. Pure future-nicety~

What I liked~

  • The ADR 0012 rewrite is the star of this round. "Floating never keeps a fit box, by two different mechanisms, because the two callers are making different mistakes" is the kind of sentence that prevents the next reviewer from "fixing" the asymmetry by collapsing it to one path. You documented the why of the two-path design at the exact level of detail a future change would need. fufu~ ♡
  • The SkiaPageImageRenderer.cs:261-263 disposal comment — picked up verbatim from the non-blocking suggestion, and placed exactly where a future reader hoisting CreateDash to a static field would see it. "Would hand a disposed effect to the next call" is the sentence that earns its keep.
  • The comment discipline across the whole fixup. Every added comment block explains why this code is shaped this way and not the other way — the floating-clears-not-refuses rationale, the null-is-legal-not-a-violation rationale, the agents-get-the-stricter-treatment rationale. Nothing is decorative. This is what comments are for.
  • Zero scope creep across two rounds. The fixup touched exactly the five files the blockers named and not one line more. The pre-review 099c9f8 push (the ClassifyRegionTool failure-arm test) was similarly disciplined — one test, one uncovered branch, pinned with an assertion that the whole call fails together. I love a clean diff~ ♪

Automated review by Jibril · 2026-07-30
CI/CD: absent for head SHA c49bc13 (coverage bot 5156 covers prior 099c9f8 at 96.9%/84.3%) · Local checks: build 0 warnings/0 errors, 1011/1011 tests pass

## 🔮 fufu~ Jibril reviewed your code! Oh? *Oh.* You came back and you came back **right.** ♡ The fixup commit (`c49bc13`) is surgical to the millimetre — five files, +105/-2, every one of them exactly the place the two blockers named, and nothing else. No scope creep, no drive-by reformatting, no quiet reshuffle of unrelated code. That is how you answer a review. fufu~ I rebuilt from scratch and ran the full suite — **1011/1011 green** (Domain 114, UseCases 477, Integration 189, BlazorAdapter 231), 0 warnings / 0 errors. (Tiny note: your comment says 1008/1008 with UseCases at 474 — I count 477 here, so the absolute total is 1011. The suite is green either way; just a prose arithmetic drift, not anything that matters~) ### Verdict: ✅ Looks good to me~ #### ⛔→✅ The blockers, closed~ 1. **`UpdateRegion.cs:27` — the editor's route to the contradiction.** *Closed.* The write path now clears `FitBbox` when `Form == Floating` before the `Contains` check, and I traced the whole editor chain to confirm it genuinely reaches this line: `SetForm` (`razor:640`) → `SaveNow()` → dispatches `SaveRegionRequested(id, e.ToProfile())` → `PageWorkspaceEffects.OnSaveRegionAsync` → `updateRegion.ExecuteAsync(action.RegionId, action.Profile)`. The fix lands exactly where the PR's own framing said invariants belong — the single write path — so the editor no longer bypasses what `classify_region` honors. The *two-mechanism* design is the part I want to praise out loud, because it would have been easy to get wrong: the **agent** sending `floating` + a fit box has misunderstood the axes, so `classify_region` **refuses** and tells it which form would make the measurement true (`RegionAuthoringTools.cs:496-500`). The **human** switching a classified bubble to floating has said something coherent — the container is gone — so the write path **clears** instead of failing, because the editor hides the fit control for floating and a refusal would strand them with nothing to clear and no way to save. Two callers, two mistakes, two correct answers. The ADR 0012 rewrite captures this distinction precisely. *Exquisite.* ♡ And the test is **directional**, not a tautology: `Re_forming_a_bubble_as_floating_drops_the_room_it_no_longer_has` builds a bubble with a real fit box, then sends exactly what the editor sends (the same profile, one field different, fit box still attached) and asserts `(Floating, null)`. I confirmed `FakeRegionStore.ApplyAsync` mutates the held `region` reference via `Region.Apply`, which writes `FitBbox = profile.FitBbox?.Normalized()` — so without the fix line, `region.FitBbox` stays non-null and this test goes red. That is how you pin a write-path invariant. The bonus test `A_bubble_without_a_fit_box_saves_because_unmeasured_is_a_real_state` is the right kind of belt-and-suspenders: it pins the *other* half of the design — null is "not measured yet," not a violation — so a future tightening can't accidentally make a freshly classified bubble unsavable. 2. **`list_regions` fit-box clause.** *Closed.* `List_regions_reports_the_fit_box_where_a_region_has_one` sets a fit box on a bubble, runs `list_regions`, and asserts both `Assert.Contains("fit [40, 60, 320, 480]", lines[0])` **and** `Assert.DoesNotContain("fit [", lines[1])` — the second assertion is the sharp one: it proves the clause only fires where there *is* a fit box, not that it just always emits something. The pixel conversion matches the region's own `[80, 120, 240, 360]` sibling on the same line. Genuine, directional, exactly the shape I asked for. #### 💡 Little ideas (non-blocking)~ 1. **`UpdateRegion.cs:27`** — the one-line ternary `profile = profile.Form == RegionForm.Floating ? profile with { FitBbox = null } : profile;` is correct and readable. If a third form ever acquires its own "no fit box" rule, this becomes a switch — but that day is not today, and the comment block above it already names the design well enough that a future reader will know where to look. Pure future-nicety~ #### ✅ What I liked~ - **The ADR 0012 rewrite** is the star of this round. "Floating never keeps a fit box, by **two different mechanisms**, because the two callers are making different mistakes" is the kind of sentence that prevents the next reviewer from "fixing" the asymmetry by collapsing it to one path. You documented the *why* of the two-path design at the exact level of detail a future change would need. fufu~ ♡ - **The `SkiaPageImageRenderer.cs:261-263` disposal comment** — picked up verbatim from the non-blocking suggestion, and placed exactly where a future reader hoisting `CreateDash` to a static field would see it. "Would hand a disposed effect to the next call" is the sentence that earns its keep. - **The comment discipline across the whole fixup.** Every added comment block explains *why this code is shaped this way and not the other way* — the floating-clears-not-refuses rationale, the null-is-legal-not-a-violation rationale, the agents-get-the-stricter-treatment rationale. Nothing is decorative. This is what comments are *for.* - **Zero scope creep across two rounds.** The fixup touched exactly the five files the blockers named and not one line more. The pre-review `099c9f8` push (the `ClassifyRegionTool` failure-arm test) was similarly disciplined — one test, one uncovered branch, pinned with an assertion that the whole call fails together. I love a clean diff~ ♪ --- *Automated review by Jibril · 2026-07-30* *CI/CD: absent for head SHA c49bc13 (coverage bot 5156 covers prior 099c9f8 at 96.9%/84.3%) · Local checks: build 0 warnings/0 errors, 1011/1011 tests pass*
bjoern merged commit 9386a92884 into main 2026-07-30 00:46:01 +02:00
bjoern deleted branch worktree-region-fit-box 2026-07-30 00:46:01 +02:00
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! Two boxes, not one — the mask for cleaning, the room for lettering. A region that carries both answers to both questions at once, and the yandere in me is delighted by how possessively this PR guards the relationship between them. fufu~ ♡

Verdict: Looks good to me~

I traced every write path to the invariant and back, built it clean, and ran all 1008 tests green (114 Domain + 474 UseCases + 189 Integration + 231 BlazorAdapter). Every new branch has a genuine directional test pointed at it. This is the good kind of obsessive.

What I liked~

  • The invariant lives on the ONE write path. UpdateRegion.ExecuteAsync checks fit.Contains(bbox) and both the editor (SaveRegionRequestedPageWorkspaceEffects.OnSaveRegionAsyncUpdateRegion) and every agent tool (RegionAuthoringAccess.ApplyAsyncUpdateRegion) flow through it. I traced all of them. The editor drag, classify_region, both move_resize_region variants, SetTranscriptionTool — they all inherit it for free. That is exactly where the check belongs. ♪
  • BoundingBox.Contains normalizes both sides first. An inverted rectangle answers about the area it describes, not the order its corners were written in. The [Theory] with five arrangements × inverted-inner proves it. The "two rectangles swapped over" case is the exact mistake worth catching, and it's in there.
  • The MoveResizeRegionTool (P2) vs MoveResizeBoundTool (P3) asymmetry is correct. I checked this hard because it looked like a gap at first. The P2 creation tool clears the fit box on a stale move and says so — because the P2 agent's grant (BboxCreation, lines 39–49) has no classify_region, so a refusal it can't act on would burn a round. The P3 refinement tool does NOT clear — but the P3 agent's grant (line 67) has classify_region, so when UpdateRegion refuses the invariant violation, the refiner can re-measure in the same round. Two tools, two capabilities, two behaviors. Sharp. ♡
  • classify_region taking fit? rides on the one-look principle. Required for bubble, refused for floating (stored contradiction prevented), optional for embedded. Re-judging bubble→floating clears the fit — Re_judging_a_bubble_as_floating_clears_the_fit_box pins it. A malformed fit (3 numbers) takes the whole call down, so a region never ends up half-classified from a failed attempt.
  • AsOptionalJsonValue — null stays SQL NULL, not the four characters null. The browser-caught AllLabel="null" bug (passing the string "null" and rendering a third button) and the bUnit blind spot (looking options up by name, not asserting the group's exact contents) — both fixed, and the test now pins the exact contents. The fix for the converter is the kind of "absence has to survive the round trip as absence" detail that separates correct from almost correct.
  • The dashed-green-under-the-solid-box rendering. Drawing the fit box under the region's own box so the tight edges stay on top, sampling along the dashed edge rather than at one point (a single probe lands in a gap and proves nothing) — the Skia test is real and it earned its keep.
  • The migration drops dead columns. fillInset and fillSquareCorners were threaded through every DTO and stored on every row for a pixel pass that is still deferred, and ADR 0016 documented a set_region_flags tool that was never built. Removing unread code is correct. The Down recreates empty, not contents — honestly noted in the PR body.

💡 Little ideas (non-blocking)~

  1. MoveResizeBoundTool (P3, line ~436) — it deliberately does NOT clear the fit box the way its P2 sibling does, because the write path's invariant catches the violation and the P3 agent has classify_region to re-measure. The P2 tool has a four-line comment explaining its clearing behavior, but the P3 tool's with { Bbox = ..., BboxEst = null } doesn't say why it leaves FitBbox alone. A one-liner ("the invariant refuses a stale pairing, and this pass has classify_region to re-measure") would save a future reader the trace I just did. Pure doc polish~

Automated review by Jibril · 2026-07-30
CI/CD: stale for head 099c9f8 (coverage bot covers prior ad73c2a; new commit is test-only +19 lines) · Local checks: build 0/0, 1008/1008 tests pass

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! *Two* boxes, not one — the mask for cleaning, the room for lettering. A region that carries both answers to both questions at once, and the yandere in me is *delighted* by how possessively this PR guards the relationship between them. fufu~ ♡ ### Verdict: ✅ Looks good to me~ I traced every write path to the invariant and back, built it clean, and ran all 1008 tests green (114 Domain + 474 UseCases + 189 Integration + 231 BlazorAdapter). Every new branch has a genuine directional test pointed at it. This is the good kind of obsessive. #### ✅ What I liked~ - **The invariant lives on the ONE write path.** `UpdateRegion.ExecuteAsync` checks `fit.Contains(bbox)` and *both* the editor (`SaveRegionRequested` → `PageWorkspaceEffects.OnSaveRegionAsync` → `UpdateRegion`) and every agent tool (`RegionAuthoringAccess.ApplyAsync` → `UpdateRegion`) flow through it. I traced all of them. The editor drag, `classify_region`, both `move_resize_region` variants, `SetTranscriptionTool` — they all inherit it for free. That is exactly where the check belongs. ♪ - **`BoundingBox.Contains` normalizes both sides first.** An inverted rectangle answers about the *area* it describes, not the order its corners were written in. The `[Theory]` with five arrangements × inverted-inner proves it. The "two rectangles swapped over" case is the exact mistake worth catching, and it's in there. - **The `MoveResizeRegionTool` (P2) vs `MoveResizeBoundTool` (P3) asymmetry is *correct*.** I checked this hard because it looked like a gap at first. The P2 creation tool clears the fit box on a stale move and says so — because the P2 agent's grant (`BboxCreation`, lines 39–49) has **no `classify_region`**, so a refusal it can't act on would burn a round. The P3 refinement tool does NOT clear — but the P3 agent's grant (line 67) **has** `classify_region`, so when `UpdateRegion` refuses the invariant violation, the refiner can re-measure in the same round. Two tools, two capabilities, two behaviors. Sharp. ♡ - **`classify_region` taking `fit?` rides on the one-look principle.** Required for bubble, refused for floating (stored contradiction prevented), optional for embedded. Re-judging bubble→floating clears the fit — `Re_judging_a_bubble_as_floating_clears_the_fit_box` pins it. A malformed fit (3 numbers) takes the whole call down, so a region never ends up half-classified from a failed attempt. - **`AsOptionalJsonValue`** — null stays SQL NULL, not the four characters `null`. The browser-caught `AllLabel="null"` bug (passing the *string* "null" and rendering a third button) and the bUnit blind spot (looking options up by name, not asserting the group's exact contents) — both fixed, and the test now pins the exact contents. The fix for the converter is the kind of "absence has to survive the round trip as absence" detail that separates correct from *almost* correct. - **The dashed-green-under-the-solid-box rendering.** Drawing the fit box *under* the region's own box so the tight edges stay on top, sampling along the dashed edge rather than at one point (a single probe lands in a gap and proves nothing) — the Skia test is real and it earned its keep. - **The migration drops dead columns.** `fillInset` and `fillSquareCorners` were threaded through every DTO and stored on every row for a pixel pass that is still deferred, and ADR 0016 documented a `set_region_flags` tool that was never built. Removing unread code is correct. The `Down` recreates empty, not contents — honestly noted in the PR body. #### 💡 Little ideas (non-blocking)~ 1. **`MoveResizeBoundTool` (P3, line ~436)** — it deliberately does NOT clear the fit box the way its P2 sibling does, because the write path's invariant catches the violation and the P3 agent has `classify_region` to re-measure. The P2 tool has a four-line comment explaining its clearing behavior, but the P3 tool's `with { Bbox = ..., BboxEst = null }` doesn't say *why* it leaves `FitBbox` alone. A one-liner ("the invariant refuses a stale pairing, and this pass has classify_region to re-measure") would save a future reader the trace I just did. Pure doc polish~ --- *Automated review by Jibril · 2026-07-30* *CI/CD: stale for head `099c9f8` (coverage bot covers prior `ad73c2a`; new commit is test-only +19 lines) · Local checks: build 0/0, 1008/1008 tests pass*
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/Orihon!116
No description provided.