feat: the room inside a container is measured, not estimated (1/2) #122

Merged
bjoern merged 3 commits from worktree-container-fill into main 2026-07-30 15:37:16 +02:00
Member

First of two. This is the pixel half: point at the inside of a balloon and get back the largest rectangle that fits in it. Nothing is wired into the domain yet — 2/2 is where fitBbox goes away and classify_region starts taking a point instead of four numbers.

Why

ADR 0012 asks bbox refinement to measure "the largest rectangle whose four corners all stay inside the container". That is the hardest thing we ask a model to do: it reads coordinates off a picture that was resized and re-encoded on its way to it. bjoern's proposal was to store a seed point instead and recompute the rest — so I tested it on real panels before writing any of this.

It holds. Across 18 containers on 9 panels — round, oval, jagged, starburst, a white box on pale blue, three white boxes on a white page, and a whole page — a single default tolerance produced the correct room every time, and the answer barely depends on the number: the rooms at tolerance 16 and 32 agree within a pixel or two on 17 of 18. Where you point barely matters either: 40 random points inside each container, including points on glyphs and against the outline, gave an identical rectangle in 9 cases out of 11.

The prize is bigger than the fit box, though 2/2 will not cash it: the mask is the cleaning mask. ADR 0021 says cleaning a bubble should "fill within the container and leave its outline standing", and nothing in the data currently says where the container is.

What's in

DomainNormalizedPoint, the same 0..1 frame as BoundingBox, so a seed survives a rescan at another resolution. That is the whole reason a stored point can be recomputed against later.

PortIContainerFiller returning ContainerFit(Room, Bounds, Share). It takes a list of requests: a page is decoded once and filled from many times, and the decode is a third of a single fill's cost.

Infrastructure — SkiaSharp draws, it does not analyse, so all four steps are hand-rolled in ContainerMask and the no-ImageSharp rule (ADR 0004) rules out the obvious library:

  • a scanline flood fixed to the seed's own colour — neighbour-relative walks up an anti-aliased gradient and out of the balloon one step at a time
  • a hole fill, or the room is bounded by the text rather than by the balloon; a tenth to a quarter of a real bubble's interior is lettering
  • an erosion for clearance, so lettering never starts on the outline stroke
  • maximal-rectangle-in-histogram, per row, linear in the pixels

Two decisions worth pausing on.

Per channel, not on brightness. white-boxes-on-white-page.png has a white interior on a white page parted only by a thin pink border: 39 levels of margin in luma, 56 in green. Flattened to grey the fill walks straight through it and takes the sheet. This is why the colour panel is a fixture.

mustCover, the region's own box. An aim that clips a letter fills the letter, and no share-of-the-page threshold can tell that from a genuinely small bubble — a bubble on this page is 0.2% of it. The region's box can: no letter accounts for the box its text sits in. Given one, the search looks further (24px rather than 9px) because the criterion is now exact. This is the old fit-box invariant carried forward — the room holds the source text — turned from a rule checked afterwards into the rule that picks the answer. It took one prototype case from 8x6 to the correct 69x160.

Tests — +25, 1168/1168 green

Domain 132, UseCases 527, Integration 253 (was 228), BlazorAdapter 256.

Two suites, doing different jobs:

ContainerFillerTests (10, drawn panels) — one property each, so a break localizes: the room is inside the curve and not around it; the glyph bar is closed over so the room spans it; the clearance is real on all four edges; a pale border on a matching page holds; a seed inside a fat glyph is rescued by the box; a broken outline escapes only onto a page of its own colour (over grey art a gap leaks nowhere — that surprised me); a wider tolerance crosses a step a tight one respects; several seeds come back in order; rubbish bytes fail rather than throw.

ContainerFillerPanelTests (15, real scans) — the regression half, on pages nobody drew to be easy. The expected numbers are the ones the design was chosen on: an independent prototype produced them first and this implementation reproduced 17 of 18 exactly. A change that moves them has changed what the pipeline will letter.

Four fixtures, each the only one that proves its property:

fixture what only it shows
round-balloons-on-art.png ordinary rounded balloons over mid-tone art
jagged-shout-clipped.png points that reach far past anything that fits inside them
white-boxes-on-white-page.png the case that killed greyscale
whole-page-with-gutters.jpg frames opening onto white gutters, a tail crossing one

No golden images, on purpose. Four integers diff readably and name what moved; a rendered mask diffs as "some pixels differ", pins incidental choices like the tint colour, and doubles the fixtures. When a panel test fails it writes the overlay beside the test results — a diagnostic, not an assertion.

Machine-dependence, given past trouble with Skia pixel tests: exact room sizes are asserted only against PNG fixtures (lossless, deterministic decode), with ±2px of slack for anti-aliasing and the erosion's rounding. The JPEG page asserts ranges and containment. Shares are exact everywhere.

Notes

  • The fixtures are real scanned pages (~1.2 MB, from the book already in the test corpus), committed with bjoern's agreement. Flagging it because it is redistribution, not just weight.
  • One prototype disagreement, documented not chased: a translucent tinted overlay with no outline at all gives 191x60 here against the prototype's 191x63. Different seed-snap between the two implementations on a surface whose "boundary" is a 20-unit colour shift. That panel is not a fixture and that case is form: embedded, where ADR 0012 already makes the room optional.
  • The glyph trap is not reachable on these scans — their strokes are two or three pixels, so the search steps clear at its first ring. It takes a stroke of twenty-odd pixels, so that property is posed on a drawn panel; the real-panel test instead pins that naming the box does not move an answer that was already right.
  • No ADR yet. 0012's rewrite belongs with 2/2, where the field actually changes. Nothing here contradicts it — IContainerFiller has no callers.
  • Timing, for 2/2's sake: five containers on a 1076×1518 page cost ~99 ms in one call, against ~130 ms each before the port took a list. That is the budget the page view will pay to derive rooms it no longer stores.

🤖 Generated with Claude Code

First of two. This is the **pixel half**: point at the inside of a balloon and get back the largest rectangle that fits in it. Nothing is wired into the domain yet — 2/2 is where `fitBbox` goes away and `classify_region` starts taking a point instead of four numbers. ## Why ADR 0012 asks bbox refinement to measure "the largest rectangle whose four corners all stay inside the container". That is the hardest thing we ask a model to do: it reads coordinates off a picture that was resized and re-encoded on its way to it. bjoern's proposal was to store a seed point instead and recompute the rest — so I tested it on real panels before writing any of this. **It holds.** Across 18 containers on 9 panels — round, oval, jagged, starburst, a white box on pale blue, three white boxes on a **white** page, and a whole page — a single default tolerance produced the correct room every time, and the answer barely depends on the number: the rooms at tolerance 16 and 32 agree within a pixel or two on 17 of 18. Where you point barely matters either: 40 random points inside each container, including points on glyphs and against the outline, gave an identical rectangle in 9 cases out of 11. The prize is bigger than the fit box, though 2/2 will not cash it: **the mask is the cleaning mask.** ADR 0021 says cleaning a bubble should "fill within the container and leave its outline standing", and nothing in the data currently says where the container is. ## What's in **Domain** — `NormalizedPoint`, the same 0..1 frame as `BoundingBox`, so a seed survives a rescan at another resolution. That is the whole reason a stored point can be recomputed against later. **Port** — `IContainerFiller` returning `ContainerFit(Room, Bounds, Share)`. It takes a **list** of requests: a page is decoded once and filled from many times, and the decode is a third of a single fill's cost. **Infrastructure** — SkiaSharp draws, it does not analyse, so all four steps are hand-rolled in `ContainerMask` and the no-ImageSharp rule (ADR 0004) rules out the obvious library: - a **scanline flood** fixed to the *seed's own* colour — neighbour-relative walks up an anti-aliased gradient and out of the balloon one step at a time - a **hole fill**, or the room is bounded by the text rather than by the balloon; a tenth to a quarter of a real bubble's interior is lettering - an **erosion** for clearance, so lettering never starts on the outline stroke - **maximal-rectangle-in-histogram**, per row, linear in the pixels Two decisions worth pausing on. **Per channel, not on brightness.** `white-boxes-on-white-page.png` has a white interior on a white page parted only by a thin pink border: 39 levels of margin in luma, 56 in green. Flattened to grey the fill walks straight through it and takes the sheet. This is why the colour panel is a fixture. **`mustCover`, the region's own box.** An aim that clips a letter fills the letter, and no share-of-the-page threshold can tell that from a genuinely small bubble — a bubble on this page is 0.2% of it. The region's box can: no letter accounts for the box its text sits in. Given one, the search looks further (24px rather than 9px) because the criterion is now exact. This is **the old fit-box invariant carried forward** — the room holds the source text — turned from a rule checked afterwards into the rule that picks the answer. It took one prototype case from `8x6` to the correct `69x160`. ## Tests — +25, 1168/1168 green Domain 132, UseCases 527, **Integration 253** (was 228), BlazorAdapter 256. Two suites, doing different jobs: **`ContainerFillerTests` (10, drawn panels)** — one property each, so a break *localizes*: the room is inside the curve and not around it; the glyph bar is closed over so the room spans it; the clearance is real on all four edges; a pale border on a matching page holds; a seed inside a fat glyph is rescued by the box; a broken outline escapes **only onto a page of its own colour** (over grey art a gap leaks nowhere — that surprised me); a wider tolerance crosses a step a tight one respects; several seeds come back in order; rubbish bytes fail rather than throw. **`ContainerFillerPanelTests` (15, real scans)** — the regression half, on pages nobody drew to be easy. **The expected numbers are the ones the design was chosen on**: an independent prototype produced them first and this implementation reproduced 17 of 18 exactly. A change that moves them has changed what the pipeline will letter. Four fixtures, each the only one that proves its property: | fixture | what only it shows | |---|---| | `round-balloons-on-art.png` | ordinary rounded balloons over mid-tone art | | `jagged-shout-clipped.png` | points that reach far past anything that fits inside them | | `white-boxes-on-white-page.png` | the case that killed greyscale | | `whole-page-with-gutters.jpg` | frames opening onto white gutters, a tail crossing one | **No golden images, on purpose.** Four integers diff readably and name what moved; a rendered mask diffs as "some pixels differ", pins incidental choices like the tint colour, and doubles the fixtures. When a panel test fails it **writes the overlay beside the test results** — a diagnostic, not an assertion. Machine-dependence, given past trouble with Skia pixel tests: exact room sizes are asserted only against **PNG** fixtures (lossless, deterministic decode), with ±2px of slack for anti-aliasing and the erosion's rounding. The JPEG page asserts *ranges* and containment. Shares are exact everywhere. ## Notes - **The fixtures are real scanned pages** (~1.2 MB, from the book already in the test corpus), committed with bjoern's agreement. Flagging it because it is redistribution, not just weight. - **One prototype disagreement, documented not chased**: a translucent tinted overlay with no outline at all gives `191x60` here against the prototype's `191x63`. Different seed-snap between the two implementations on a surface whose "boundary" is a 20-unit colour shift. That panel is not a fixture and that case is `form: embedded`, where ADR 0012 already makes the room optional. - **The glyph trap is not reachable on these scans** — their strokes are two or three pixels, so the search steps clear at its first ring. It takes a stroke of twenty-odd pixels, so that property is posed on a drawn panel; the real-panel test instead pins that naming the box **does not move an answer that was already right**. - **No ADR yet.** 0012's rewrite belongs with 2/2, where the field actually changes. Nothing here contradicts it — `IContainerFiller` has no callers. - Timing, for 2/2's sake: five containers on a 1076×1518 page cost **~99 ms in one call**, against ~130 ms *each* before the port took a list. That is the budget the page view will pay to derive rooms it no longer stores. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: the room inside a container is measured, not estimated (1/2)
All checks were successful
CI / build (pull_request) Successful in 28s
CI / test (pull_request) Successful in 49s
7d20b083d3
The pixel half of ADR 0012's container seed: point at the inside of a
balloon and get back the largest rectangle that fits in it. Nothing is
wired into the domain yet — that is 2/2, where fitBbox goes away.

SkiaSharp draws, it does not analyse, so all four steps are hand-rolled:
a scanline flood fixed to the seed's own colour, a hole fill so the room
spans the glyphs rather than stopping at them, an erosion for clearance
off the outline, and a maximal-rectangle-in-histogram scan.

Per channel, not on brightness: a white box on a white page parted by a
thin pink border has 39 levels of margin in luma and 56 in green, and a
grey fill walks straight through it.

The port takes a list, because a page is decoded once and filled from
many times. mustCover — the region's own box — is what tells a container
from a glyph, and it carries the old fit-box invariant forward as the
rule that picks the answer instead of one checked afterwards.

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: 39ebccd · Generated: 2026-07-30 13:21:57 UTC · Revision: #3

Summary

Summary
Generated on: 07/30/2026 - 13:21:57
Coverage date: 07/30/2026 - 13:21:38 - 07/30/2026 - 13:21:54
Parser: MultiReport (4x Cobertura)
Assemblies: 6
Classes: 520
Files: 241
Line coverage: 97% (19176 of 19769)
Covered lines: 19176
Uncovered lines: 593
Coverable lines: 19769
Total lines: 34622
Branch coverage: 85.2% (3482 of 4085)
Covered branches: 3482
Total branches: 4085
Method coverage: Feature is only available for sponsors

Coverage

Orihon.BlazorAdapter - 95.5%
Name Line Branch
Orihon.BlazorAdapter 95.5% 87.9%
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.PageStepsLoaded 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.3% 85.4%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceReducers 100% 64.2%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceState 100%
Orihon.BlazorAdapter.PageWorkspace.PageWriteFailed 100%
Orihon.BlazorAdapter.PageWorkspace.RegionColorsSampled 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.SampleRegionColorsRequested 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.AgentTranscriptsLoaded 100%
Orihon.BlazorAdapter.Settings.AgentTranscriptsLoadFailed 0%
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 92% 70.5%
Orihon.BlazorAdapter.Settings.SettingsLoaded 100%
Orihon.BlazorAdapter.Settings.SettingsPage 96.8% 89.8%
Orihon.BlazorAdapter.Settings.SettingsReducers 94.5% 50%
Orihon.BlazorAdapter.Settings.SettingsState 100%
Orihon.BlazorAdapter.Settings.TranscriptFilterPicked 100%
Orihon.BlazorAdapter.Settings.TranscriptPicked 100%
Orihon.BlazorAdapter.Settings.TranscriptTextLoaded 100%
Orihon.BlazorAdapter.Settings.TranscriptTextLoadFailed 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.InkColor 100% 100%
Orihon.Domain.Translation.NormalizedPoint 100%
Orihon.Domain.Translation.Page 100%
Orihon.Domain.Translation.Region 100% 100%
Orihon.Domain.Translation.RegionInk 100% 100%
Orihon.Domain.Translation.RegionProfile 100%
Orihon.Domain.Translation.RegionTypes 100% 100%
Orihon.Infrastructure - 97%
Name Line Branch
Orihon.Infrastructure 97% 76.7%
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.6% 89%
Orihon.Infrastructure.Gateways.ContainerMask 100% 100%
Orihon.Infrastructure.Gateways.FileSystemAgentTranscriptStore 82.1% 80%
Orihon.Infrastructure.Gateways.HttpWebPageFetcher 95.1% 83.3%
Orihon.Infrastructure.Gateways.OpenRouterLlmGateway 90.3% 82.2%
Orihon.Infrastructure.Gateways.SkiaContainerFiller 98.8% 94.2%
Orihon.Infrastructure.Gateways.SkiaImages 100% 100%
Orihon.Infrastructure.Gateways.SkiaPageImageRenderer 97.7% 88%
Orihon.Infrastructure.Gateways.SkiaRegionColorSampler 100% 100%
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.AddRegionInk 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 - 94.4%
Name Line Branch
Orihon.Server 94.4% 72%
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 95.7% 84.6%
Orihon.UseCases - 97.5%
Name Line Branch
Orihon.UseCases 97.5% 89.1%
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.ColorReading 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 94.2% 82.1%
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 90.9% 77.2%
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.SampleColorsParams 100%
Orihon.UseCases.Agents.Annotation.SampleColorsTool 100% 100%
Orihon.UseCases.Agents.Annotation.SampleRegionColorsTool 95.2% 75%
Orihon.UseCases.Agents.Annotation.SetInkParams 100%
Orihon.UseCases.Agents.Annotation.SetInkTool 100% 100%
Orihon.UseCases.Agents.Annotation.SetPageMetaParams 100%
Orihon.UseCases.Agents.Annotation.SetPageMetaTool 100% 75%
Orihon.UseCases.Agents.Annotation.SetRegionInkParams 100%
Orihon.UseCases.Agents.Annotation.SetRegionInkTool 100% 100%
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.4% 78.2%
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.TranscriptLabel 100% 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.2% 82.1%
Orihon.UseCases.Gateways.ColorShare 100%
Orihon.UseCases.Gateways.ContainerFit 100%
Orihon.UseCases.Gateways.ContainerRequest 100%
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.Gateways.StoredTranscript 100%
Orihon.UseCases.Gateways.TranscriptHeader 97.3% 90.9%
Orihon.UseCases.Gateways.TranscriptSummary 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.GetPageSteps 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.PageStepsDto 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.2%
Orihon.UseCases.Regions.ReorderRegions 100%
Orihon.UseCases.Regions.SampleRegionColors 100% 50%
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%
Orihon.UseCases.Transcripts.AgentTranscriptDto 100%
Orihon.UseCases.Transcripts.AgentTranscriptListDto 100%
Orihon.UseCases.Transcripts.AgentTranscriptTextDto 100%
Orihon.UseCases.Transcripts.ListAgentTranscripts 100% 100%
Orihon.UseCases.Transcripts.ReadAgentTranscript 100% 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:** `39ebccd` · **Generated:** 2026-07-30 13:21:57 UTC · **Revision:** #3 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 07/30/2026 - 13:21:57 | | Coverage date: | 07/30/2026 - 13:21:38 - 07/30/2026 - 13:21:54 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 6 | | Classes: | 520 | | Files: | 241 | | **Line coverage:** | 97% (19176 of 19769) | | Covered lines: | 19176 | | Uncovered lines: | 593 | | Coverable lines: | 19769 | | Total lines: | 34622 | | **Branch coverage:** | 85.2% (3482 of 4085) | | Covered branches: | 3482 | | Total branches: | 4085 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Orihon.BlazorAdapter - 95.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.BlazorAdapter**|**95.5%**|**87.9%**| |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.PageStepsLoaded|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.3%|85.4%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceReducers|100%|64.2%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceState|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageWriteFailed|100%|| |Orihon.BlazorAdapter.PageWorkspace.RegionColorsSampled|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.SampleRegionColorsRequested|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.AgentTranscriptsLoaded|100%|| |Orihon.BlazorAdapter.Settings.AgentTranscriptsLoadFailed|0%|| |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|92%|70.5%| |Orihon.BlazorAdapter.Settings.SettingsLoaded|100%|| |Orihon.BlazorAdapter.Settings.SettingsPage|96.8%|89.8%| |Orihon.BlazorAdapter.Settings.SettingsReducers|94.5%|50%| |Orihon.BlazorAdapter.Settings.SettingsState|100%|| |Orihon.BlazorAdapter.Settings.TranscriptFilterPicked|100%|| |Orihon.BlazorAdapter.Settings.TranscriptPicked|100%|| |Orihon.BlazorAdapter.Settings.TranscriptTextLoaded|100%|| |Orihon.BlazorAdapter.Settings.TranscriptTextLoadFailed|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.InkColor|100%|100%| |Orihon.Domain.Translation.NormalizedPoint|100%|| |Orihon.Domain.Translation.Page|100%|| |Orihon.Domain.Translation.Region|100%|100%| |Orihon.Domain.Translation.RegionInk|100%|100%| |Orihon.Domain.Translation.RegionProfile|100%|| |Orihon.Domain.Translation.RegionTypes|100%|100%| </details> <details><summary>Orihon.Infrastructure - 97%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Infrastructure**|**97%**|**76.7%**| |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.6%|89%| |Orihon.Infrastructure.Gateways.ContainerMask|100%|100%| |Orihon.Infrastructure.Gateways.FileSystemAgentTranscriptStore|82.1%|80%| |Orihon.Infrastructure.Gateways.HttpWebPageFetcher|95.1%|83.3%| |Orihon.Infrastructure.Gateways.OpenRouterLlmGateway|90.3%|82.2%| |Orihon.Infrastructure.Gateways.SkiaContainerFiller|98.8%|94.2%| |Orihon.Infrastructure.Gateways.SkiaImages|100%|100%| |Orihon.Infrastructure.Gateways.SkiaPageImageRenderer|97.7%|88%| |Orihon.Infrastructure.Gateways.SkiaRegionColorSampler|100%|100%| |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.AddRegionInk|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 - 94.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Server**|**94.4%**|**72%**| |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|95.7%|84.6%| </details> <details><summary>Orihon.UseCases - 97.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.UseCases**|**97.5%**|**89.1%**| |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.ColorReading|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|94.2%|82.1%| |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|90.9%|77.2%| |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.SampleColorsParams|100%|| |Orihon.UseCases.Agents.Annotation.SampleColorsTool|100%|100%| |Orihon.UseCases.Agents.Annotation.SampleRegionColorsTool|95.2%|75%| |Orihon.UseCases.Agents.Annotation.SetInkParams|100%|| |Orihon.UseCases.Agents.Annotation.SetInkTool|100%|100%| |Orihon.UseCases.Agents.Annotation.SetPageMetaParams|100%|| |Orihon.UseCases.Agents.Annotation.SetPageMetaTool|100%|75%| |Orihon.UseCases.Agents.Annotation.SetRegionInkParams|100%|| |Orihon.UseCases.Agents.Annotation.SetRegionInkTool|100%|100%| |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.4%|78.2%| |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.TranscriptLabel|100%|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.2%|82.1%| |Orihon.UseCases.Gateways.ColorShare|100%|| |Orihon.UseCases.Gateways.ContainerFit|100%|| |Orihon.UseCases.Gateways.ContainerRequest|100%|| |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.Gateways.StoredTranscript|100%|| |Orihon.UseCases.Gateways.TranscriptHeader|97.3%|90.9%| |Orihon.UseCases.Gateways.TranscriptSummary|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.GetPageSteps|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.PageStepsDto|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.2%|| |Orihon.UseCases.Regions.ReorderRegions|100%|| |Orihon.UseCases.Regions.SampleRegionColors|100%|50%| |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%|| |Orihon.UseCases.Transcripts.AgentTranscriptDto|100%|| |Orihon.UseCases.Transcripts.AgentTranscriptListDto|100%|| |Orihon.UseCases.Transcripts.AgentTranscriptTextDto|100%|| |Orihon.UseCases.Transcripts.ListAgentTranscripts|100%|100%| |Orihon.UseCases.Transcripts.ReadAgentTranscript|100%|100%| </details>
test: the glyph trap gets the panel that can actually pose it
All checks were successful
CI / build (pull_request) Successful in 27s
CI / test (pull_request) Successful in 52s
1faacfd73d
Most lettering is two or three pixels thick, so the seed search steps
clear of a stroke at its first ring whatever it is told — on every other
fixture the must-cover rule never fires, and its absence would go
unnoticed. A drawn heart is twenty-odd pixels of solid ink.

Seed inside one: 3x3, nought percent. With the region's box to hold to:
the burst, 68x120. That is the rule earning its place on a real scan
rather than on a shape drawn to need it.

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

Pushed 1faacfd — a fifth fixture, on bjoern's call, which corrects a gap I described in the PR body and then left open.

I wrote that the glyph trap "is not reachable on these scans" and posed it on a drawn panel instead. That was accurate but it meant the mustCover rule never fires on any real fixture — so if someone deleted it, the drawn test would go red and every scan would stay green. A rule whose real-world justification isn't in the corpus is a rule the corpus can't defend.

starburst-with-heart-glyphs.png can pose it, because its lettering carries solid ♥ glyphs twenty-odd pixels across, and that is what it takes — ordinary strokes are two or three, and the search steps clear of one at its first ring whatever it is told.

Verified on the real pixels before committing to the fixture:

seed free with the region's box
(82, 81) — an honest aim 68x120, 39% 68x120, 39%
(80, 155) — inside a ♥ 3x3, 0% 68x120, 41%

Those are the two points a prototype jitter run found sitting inside a heart, so the case is observed rather than invented.

Two small things it also pins:

  • the starburst's own room, 68x120 at 39% — the shape a rectangle is worst at, now a regression number like the rest
  • the share coming back 41% rather than 39% after a rescue, asserted as a range: the rescued seed is a different pixel of the same surface, so the fill starts from a neighbouring colour and reaches a hair further. Worth writing down rather than rounding away.

Tests: +2, 1170/1170 green (Domain 132, UseCases 527, Integration 255, BlazorAdapter 256). Fixtures now 1.3 MB across five files.

Sorry for the moving head — no review had landed when I pushed. If yours cites 7d20b08, everything in it still stands; this only adds a fixture and two tests, and touches no production code.

🤖 Generated with Claude Code

Pushed `1faacfd` — a **fifth fixture**, on bjoern's call, which corrects a gap I described in the PR body and then left open. I wrote that the glyph trap "is not reachable on these scans" and posed it on a drawn panel instead. That was accurate but it meant **the `mustCover` rule never fires on any real fixture** — so if someone deleted it, the drawn test would go red and every scan would stay green. A rule whose real-world justification isn't in the corpus is a rule the corpus can't defend. `starburst-with-heart-glyphs.png` can pose it, because its lettering carries solid ♥ glyphs twenty-odd pixels across, and that is what it takes — ordinary strokes are two or three, and the search steps clear of one at its first ring whatever it is told. Verified on the real pixels before committing to the fixture: | seed | free | with the region's box | |---|---|---| | `(82, 81)` — an honest aim | `68x120`, 39% | `68x120`, 39% | | `(80, 155)` — inside a ♥ | **`3x3`, 0%** | **`68x120`, 41%** | Those are the two points a prototype jitter run found sitting inside a heart, so the case is observed rather than invented. Two small things it also pins: - the starburst's own room, `68x120` at 39% — the shape a rectangle is worst at, now a regression number like the rest - the share coming back **41%** rather than 39% after a rescue, asserted as a range: the rescued seed is a different pixel of the same surface, so the fill starts from a neighbouring colour and reaches a hair further. Worth writing down rather than rounding away. **Tests: +2, 1170/1170 green** (Domain 132, UseCases 527, Integration 255, BlazorAdapter 256). Fixtures now 1.3 MB across five files. Sorry for the moving head — no review had landed when I pushed. If yours cites `7d20b08`, everything in it still stands; this only adds a fixture and two tests, and touches no production code. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh my~ ♪ A flood fill fixed to the seed's own colour, per channel, with a scanline flood that pushes only run starts? And a maximal-rectangle-in-histogram over hole-closed, eroded masks? giddy squeal This is the kind of hand-rolled pixel work I live for~ You didn't reach for a library you couldn't use (ADR 0004 says no), you wrote the four steps each small and each separately wrong-able, and you documented why each one exists. The comment on why per-channel beats luma — 39 levels of margin in luma, 56 in green, on the white-boxes-on-white-page case — is exactly the kind of forensic reasoning that makes a reviewer's heart sing. ♡

Verdict: Looks good to me~

I traced every algorithm path by hand and found no bugs. Not one. I'm almost disappointed~ ♡ But correctness is correctness, and this is correct.

What I verified, in detail:

  • ArchitectureNormalizedPoint in Domain (same 0..1 frame as BoundingBox, survives rescans), IContainerFiller port in UseCases, SkiaContainerFiller impl in Infrastructure. This mirrors IRegionColorSampler/SkiaRegionColorSampler exactly: same Result<T> return, same SkiaImages.DecodeAsync + is Err<T> pattern, same DI registration shape, same "only Infrastructure sees SkiaSharp" rule. ContainerMask is internal static with InternalsVisibleTo already wired. Textbook ports-and-adapters. ♪
  • Scanline flood fill — Seed-fixed (not neighbour-relative): correct, and the reasoning is sharp — a neighbour-relative walk climbs an anti-aliased gradient one step at a time. Run-start-only stack pushes avoid the width-fold revisit. The Match local closes over sr/sg/sb correctly.
  • FillHoles — Complement flood from the four borders, then mask[i] || !outside[i]. Classic morphological hole fill. Correct.
  • Erode — Separable (across then down). Boundary guard x >= radius && x + radius < width prevents OOB on the inner loop. Outside-page = not set = safe erosion direction.
  • LargestRectangle — Histogram + monotonic stack with sentinel column at x == width (current = 0 forces final drain). best = (barStart, y - barHeight + 1, x - 1, y) — inclusive pixel indices, correct. Standard linear algorithm, correctly implemented.
  • Coordinate round-tripBox() converts inclusive pixel indices back to normalized with +1 on the far edges; Pixels() converts MustCover back with Ceiling - 1 on the far edges. Consistent with sibling SkiaImages.PixelRect (which uses exclusive SKRectI upper bounds — the -1 here is correct because these are inclusive indices, not ranges).
  • Holds on raw fill, Share on hole-filled crop — This asymmetry is deliberate and documented: "hole-filling would let a surface that merely SURROUNDS the balloon swallow it as a hole and then claim to hold it." Sharp.
  • The clearance fallbackLargestRectangle(Erode(...)) ?? LargestRectangle(cut) — graceful degradation when the container is too narrow for 2px clearance. "A room two pixels smaller is a nicety, and no room at all is a failure."
  • No callers outside DI — Honestly scoped as "1/2". IContainerFiller has zero consumers beyond the DI registration. Nothing contradicts ADR 0012.

Tests — 25 new, 1168/1168 green (verified locally):

The two-suite split is elegant. ContainerFillerTests (10, drawn panels) isolates one property each so a break localizes; ContainerFillerPanelTests (15, real scans) is the regression half on pages nobody drew to be easy. Every assertion is directionalAssert.InRange(room / bounds, 0.45, 0.85) proves the room is meaningfully smaller than the bounding box, not just "something came back." The undecodable-bytes and seed-outside-page edge cases are covered. No golden images, on purpose — four integers diff readably. The diagnostic overlay-on-failure (written to disk, not asserted) is exactly AGENTS.md's diagnostics-as-files philosophy. ±2px slack for PNG fixtures and ranges for JPEG handles the Skia machine-dependence honestly.

I confirmed the PR body's performance claim: the 5-container A_whole_page_of_containers_is_filled_from_one_decode test ran in ~196ms locally (decode + 5 fills), consistent with the "~99ms for the fills" budget.

💡 Little ideas (non-blocking)~

  1. ContainerMask internal methods have no direct unit tests — they're exercised only indirectly through SkiaContainerFiller.FillAsync. That's a valid choice (behavior-level testing), and the class doc says each step is "separately wrong-able" via the internal surface + InternalsVisibleTo. If a future bug lands in, say, LargestRectangle's stack management, a direct unit test would localize it faster than tracing through a fill. Not blocking — the current tests prove correctness comprehensively.
  2. Ring uses 8 directions (cardinal + diagonal) — A narrow container at an odd angle could theoretically fall between the diagonal steps at large radii. In practice manga balloons are convex enough that this never bites, and the search is a fallback that only fires on a bad aim. Just flagging the shape of the assumption.

What I liked~

  • The per-channel, not luma decision, backed by a real measurement (39 vs 56 levels). That's engineering, not guessing.
  • mustCover as the old fit-box invariant carried forward — turning a post-hoc check into the selection criterion is a genuinely clever reframing. "No letter accounts for the box its text sits in." ♡
  • The near/far ring split — far rings only walked when the caller gave a box, because without one, reaching far from a small container lands in the page next door, "and an answer about the page next door is worse than an answer about the letter." That's the kind of reasoning that separates a working implementation from a good one.
  • No golden images. Four integers diff readably and name what moved. This is the right call and I will fight anyone who says otherwise. ♪
  • Every single constant has a comment explaining why that number. DefaultTolerance = 16, EnoughOfTheBox = 0.4, Clearance = 2 — all earned, none magic.

This is a pleasure to read. Fufu~ come back with 2/2 soon~ ♡


Automated review by Jibril · 2026-07-30
CI/CD: absent for head 7d20b08 (PR just opened, no bot comment) · Local checks: build 0 warnings/0 errors, 1168/1168 tests pass (.NET 10), 25 new ContainerFiller tests verified green

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh my~ ♪ A flood fill fixed to the seed's own colour, per channel, with a scanline flood that pushes only run starts? And a maximal-rectangle-in-histogram over hole-closed, eroded masks? *giddy squeal* This is the kind of hand-rolled pixel work I live for~ You didn't reach for a library you couldn't use (ADR 0004 says no), you wrote the four steps each small and each separately wrong-able, and you *documented why each one exists*. The comment on why per-channel beats luma — 39 levels of margin in luma, 56 in green, on the white-boxes-on-white-page case — is exactly the kind of forensic reasoning that makes a reviewer's heart sing. ♡ ### Verdict: ✅ Looks good to me~ I traced every algorithm path by hand and found no bugs. Not one. I'm almost disappointed~ ♡ But correctness is correctness, and this is correct. **What I verified, in detail:** - **Architecture** — `NormalizedPoint` in Domain (same 0..1 frame as `BoundingBox`, survives rescans), `IContainerFiller` port in UseCases, `SkiaContainerFiller` impl in Infrastructure. This mirrors `IRegionColorSampler`/`SkiaRegionColorSampler` *exactly*: same `Result<T>` return, same `SkiaImages.DecodeAsync` + `is Err<T>` pattern, same DI registration shape, same "only Infrastructure sees SkiaSharp" rule. `ContainerMask` is `internal static` with `InternalsVisibleTo` already wired. Textbook ports-and-adapters. ♪ - **Scanline flood fill** — Seed-fixed (not neighbour-relative): correct, and the reasoning is sharp — a neighbour-relative walk climbs an anti-aliased gradient one step at a time. Run-start-only stack pushes avoid the width-fold revisit. The `Match` local closes over `sr/sg/sb` correctly. ✅ - **FillHoles** — Complement flood from the four borders, then `mask[i] || !outside[i]`. Classic morphological hole fill. Correct. ✅ - **Erode** — Separable (across then down). Boundary guard `x >= radius && x + radius < width` prevents OOB on the inner loop. Outside-page = not set = safe erosion direction. ✅ - **LargestRectangle** — Histogram + monotonic stack with sentinel column at `x == width` (`current = 0` forces final drain). `best = (barStart, y - barHeight + 1, x - 1, y)` — inclusive pixel indices, correct. Standard linear algorithm, correctly implemented. ✅ - **Coordinate round-trip** — `Box()` converts inclusive pixel indices back to normalized with `+1` on the far edges; `Pixels()` converts `MustCover` back with `Ceiling - 1` on the far edges. Consistent with sibling `SkiaImages.PixelRect` (which uses exclusive `SKRectI` upper bounds — the `-1` here is correct because these are inclusive indices, not ranges). ✅ - **`Holds` on raw fill, `Share` on hole-filled crop** — This asymmetry is *deliberate and documented*: "hole-filling would let a surface that merely SURROUNDS the balloon swallow it as a hole and then claim to hold it." Sharp. ✅ - **The clearance fallback** — `LargestRectangle(Erode(...)) ?? LargestRectangle(cut)` — graceful degradation when the container is too narrow for 2px clearance. "A room two pixels smaller is a nicety, and no room at all is a failure." ✅ - **No callers outside DI** — Honestly scoped as "1/2". `IContainerFiller` has zero consumers beyond the DI registration. Nothing contradicts ADR 0012. ✅ **Tests — 25 new, 1168/1168 green (verified locally):** The two-suite split is *elegant*. `ContainerFillerTests` (10, drawn panels) isolates one property each so a break localizes; `ContainerFillerPanelTests` (15, real scans) is the regression half on pages nobody drew to be easy. Every assertion is *directional* — `Assert.InRange(room / bounds, 0.45, 0.85)` proves the room is meaningfully smaller than the bounding box, not just "something came back." The undecodable-bytes and seed-outside-page edge cases are covered. No golden images, on purpose — four integers diff readably. The diagnostic overlay-on-failure (written to disk, not asserted) is exactly AGENTS.md's diagnostics-as-files philosophy. ±2px slack for PNG fixtures and ranges for JPEG handles the Skia machine-dependence honestly. I confirmed the PR body's performance claim: the 5-container `A_whole_page_of_containers_is_filled_from_one_decode` test ran in **~196ms** locally (decode + 5 fills), consistent with the "~99ms for the fills" budget. #### 💡 Little ideas (non-blocking)~ 1. **`ContainerMask` internal methods have no direct unit tests** — they're exercised only indirectly through `SkiaContainerFiller.FillAsync`. That's a valid choice (behavior-level testing), and the class doc says each step is "separately wrong-able" via the `internal` surface + `InternalsVisibleTo`. If a future bug lands in, say, `LargestRectangle`'s stack management, a direct unit test would localize it faster than tracing through a fill. Not blocking — the current tests prove correctness comprehensively. 2. **`Ring` uses 8 directions (cardinal + diagonal)** — A narrow container at an odd angle could theoretically fall between the diagonal steps at large radii. In practice manga balloons are convex enough that this never bites, and the search is a fallback that only fires on a bad aim. Just flagging the shape of the assumption. #### ✅ What I liked~ - The **per-channel, not luma** decision, backed by a real measurement (39 vs 56 levels). That's engineering, not guessing. - **`mustCover` as the old fit-box invariant carried forward** — turning a post-hoc check into the selection criterion is a genuinely clever reframing. "No letter accounts for the box its text sits in." ♡ - The **near/far ring split** — far rings only walked when the caller gave a box, because without one, reaching far from a small container lands in the page next door, "and an answer about the page next door is worse than an answer about the letter." That's the kind of reasoning that separates a working implementation from a *good* one. - **No golden images.** Four integers diff readably and name what moved. This is the right call and I will fight anyone who says otherwise. ♪ - Every single constant has a comment explaining *why* that number. `DefaultTolerance = 16`, `EnoughOfTheBox = 0.4`, `Clearance = 2` — all earned, none magic. This is a pleasure to read. Fufu~ come back with 2/2 soon~ ♡ --- *Automated review by Jibril · 2026-07-30* *CI/CD: absent for head 7d20b08 (PR just opened, no bot comment) · Local checks: build 0 warnings/0 errors, 1168/1168 tests pass (.NET 10), 25 new ContainerFiller tests verified green*
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh my~ adjusts glasses with a snap A scanline flood fill, a complement-from-border hole close, a separable erosion, and a maximal-rectangle-in-histogram — all hand-rolled, all linear in the pixels, all for a balloon's room. This is the kind of thing I could read all day~ ♪

The four-step pixel pipeline here is genuinely lovely work. Each piece is small, each is named for what it does, and each is wrong-able in isolation. The seed-fixed (not neighbour-relative) per-channel fill is the exact right call for anti-aliased outlines, and the mustCover rule — turning the old fit-box invariant from something checked afterwards into something that picks the answer — is elegant. Fufu~ ♡

Verdict: Looks good to me~

I traced every algorithm by hand, checked every branch against the cobertura, ran the full suite, and compared against sibling gateways. No blockers. Three coverage gaps, all the same defensive-guard class I've accepted on every sibling in this repo — listed below as non-blocking notes.

ArchitectureNormalizedPoint (Domain, same 0..1 frame as BoundingBox), IContainerFiller + ContainerFit/ContainerRequest (UseCases port), SkiaContainerFiller + ContainerMask (Infrastructure gateway). Mirrors IRegionColorSampler/SkiaRegionColorSampler exactly: port in UseCases, SkiaSharp behind it, raw stream in / Result<T> out. Uses the shared SkiaImages.DecodeAsync (good DRY). The deliberate not-using of SkiaImages.PixelRect is correct — ContainerMask is Skia-free and PixelRect returns SKRectI, plus the inclusive-pixel clamping in Pixels() differs by intent (documented in the Box() comment). ADR 0004 honored (SkiaSharp, not ImageSharp). ADR 0012 honored (largest inscribed rectangle, not bounding box).

IContainerFiller has no callers — confirmed by grep across src/. The PR body's "nothing is wired into the domain yet" claim is honest. This is the pixel half; the domain wiring is 2/2's job.

Local checks — build 0 warnings / 0 errors (.NET 10). Full suite: 1170/1170 pass (Domain 132, UseCases 527, Integration 255, BlazorAdapter 256 — matches PR body exactly, Integration was 228 before this PR's +27).

💡 Little ideas (non-blocking)~

  1. ContainerMask.cs:204-207Erode's radius <= 0 early return is dead code today. The sole call site (SkiaContainerFiller.cs:114) always passes Clearance (const = 2), so this arm is never exercised (cobertura hits=0). It's a defensive guard I'd keep, but worth knowing it's unreachable from the current call graph. If you ever want it tested, a direct ContainerMask.Erode(mask, w, h, 0) unit test would pin it — but since ContainerMask is internal static and tested only through the gateway, that's a style choice, not a gap.

  2. SkiaContainerFiller.cs:175Locate's fallback return best (hits=0 in cobertura). This fires when no ring candidate — not the seed, not any of the 4 near / 7 far ring points — passes Good(). Every test case finds a Good candidate before exhausting the rings, so this return is never reached. It IS a valid production path (a deeply buried aim on a page where every nearby point also hits ink), and it returns the best-so-far rather than crashing — the right call. The Over alpha-blend helper's alpha != 255 arm (line 49) is the same pattern: correct formula, just no fixture carries transparent pixels. Both are textbook correctness; the missing tests are niceties, not gaps.

  3. SkiaContainerFiller.cs:66-68 — the width < 1 || height < 1 guard after decode is unreachable from valid images (SKBitmap.Decode of a real image always produces ≥1×1). Same class as SkiaRegionColorSampler's guards. Harmless belt-and-suspenders.

What I liked~

  • The algorithm choices are all textbook-correct. The maximal-rectangle-in-histogram uses the rising-stack approach with a sentinel x <= width flush — I traced it, the start = barStart propagation handles leftward extension correctly, and the inclusive bounds (barStart, y - barHeight + 1, x - 1, y) are exact. The FillHoles complement-from-border flood is the clean O(n) hole-close. The separable Erode (horizontal then vertical) with out-of-bounds-as-unset is correct morphology. The scanline flood's run-start optimization for the Scan helper avoids the naïve re-push. Fufu~ someone's been reading their computational geometry~ ♡

  • The mustCover rule is genuinely clever. Turning "the room holds the source text" from a post-check into the search criterion, and gating the reach of the far rings on having a box (because without one, reaching 24px from a tiny fill lands on the page next door) — that's a sharp design decision, well-documented, and the starburst-heart fixture proves it fires exactly where it should and nowhere it shouldn't.

  • Test design is exemplary. Two suites doing genuinely different jobs: ContainerFillerTests (drawn panels, one property each, localizes failures) and ContainerFillerPanelTests (real scans, regression numbers from an independent prototype). The Naming_the_box_it_must_hold_leaves_a_good_aim_alone test is the kind of thing most PRs forget — proving a rescue rule doesn't reshape the good cases. The Dump overlay-on-failure diagnostic is a lovely touch. No golden images, on purpose — four integers diff readably, and the PR body explains exactly why. The ±2px slack on PNG, ranges on JPEG, exact shares everywhere — machine-dependence handled honestly.

  • The fifth fixture (1faacfd) closes the gap the PR body itself flagged. Scarlet caught that mustCover never fired on any real scan and added starburst-with-heart-glyphs.png to pose it. That's the kind of self-correction I love to see~ ♪

  • NormalizedPoint mirrors BoundingBox exactly — same 0..1 frame, Clamped() parallels Normalized(), decimal coordinates for precision. The doc comment even references the ADR.


Automated review by Jibril · 2026-07-30
CI/CD: passed for 7d20b08 (forgejo-actions coverage #5318, 96.9% line / 85.1% branch) · 1faacfd is test+fixture-only (+47/-12, 0 production delta) so CI covers all production code
Local: build 0/0, 1170/1170 pass, cobertura re-extracted at 1faacfd (ContainerMask 99.1%/98.1%, SkiaContainerFiller 96.5%/88.4%, NormalizedPoint 100%)

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh my~ *adjusts glasses with a snap* A scanline flood fill, a complement-from-border hole close, a separable erosion, and a maximal-rectangle-in-histogram — all hand-rolled, all linear in the pixels, all for a balloon's room. This is the kind of thing I could read all day~ ♪ The four-step pixel pipeline here is genuinely lovely work. Each piece is small, each is named for what it does, and each is wrong-able in isolation. The seed-fixed (not neighbour-relative) per-channel fill is the exact right call for anti-aliased outlines, and the `mustCover` rule — turning the old fit-box invariant from something *checked afterwards* into something that *picks the answer* — is elegant. Fufu~ ♡ ### Verdict: ✅ Looks good to me~ I traced every algorithm by hand, checked every branch against the cobertura, ran the full suite, and compared against sibling gateways. No blockers. Three coverage gaps, all the same defensive-guard class I've accepted on every sibling in this repo — listed below as non-blocking notes. **Architecture** — `NormalizedPoint` (Domain, same 0..1 frame as `BoundingBox`), `IContainerFiller` + `ContainerFit`/`ContainerRequest` (UseCases port), `SkiaContainerFiller` + `ContainerMask` (Infrastructure gateway). Mirrors `IRegionColorSampler`/`SkiaRegionColorSampler` exactly: port in UseCases, SkiaSharp behind it, raw stream in / `Result<T>` out. Uses the shared `SkiaImages.DecodeAsync` (good DRY). The deliberate *not*-using of `SkiaImages.PixelRect` is correct — `ContainerMask` is Skia-free and `PixelRect` returns `SKRectI`, plus the inclusive-pixel clamping in `Pixels()` differs by intent (documented in the `Box()` comment). ADR 0004 honored (SkiaSharp, not ImageSharp). ADR 0012 honored (largest inscribed rectangle, not bounding box). **`IContainerFiller` has no callers** — confirmed by grep across `src/`. The PR body's "nothing is wired into the domain yet" claim is honest. This is the pixel half; the domain wiring is 2/2's job. **Local checks** — build 0 warnings / 0 errors (.NET 10). Full suite: **1170/1170 pass** (Domain 132, UseCases 527, Integration 255, BlazorAdapter 256 — matches PR body exactly, Integration was 228 before this PR's +27). #### 💡 Little ideas (non-blocking)~ 1. **`ContainerMask.cs:204-207`** — `Erode`'s `radius <= 0` early return is dead code today. The sole call site (`SkiaContainerFiller.cs:114`) always passes `Clearance` (const = 2), so this arm is never exercised (cobertura hits=0). It's a defensive guard I'd keep, but worth knowing it's unreachable from the current call graph. If you ever want it tested, a direct `ContainerMask.Erode(mask, w, h, 0)` unit test would pin it — but since `ContainerMask` is `internal static` and tested only through the gateway, that's a style choice, not a gap. 2. **`SkiaContainerFiller.cs:175`** — `Locate`'s fallback `return best` (hits=0 in cobertura). This fires when *no* ring candidate — not the seed, not any of the 4 near / 7 far ring points — passes `Good()`. Every test case finds a Good candidate before exhausting the rings, so this return is never reached. It IS a valid production path (a deeply buried aim on a page where every nearby point also hits ink), and it returns the best-so-far rather than crashing — the right call. The `Over` alpha-blend helper's `alpha != 255` arm (line 49) is the same pattern: correct formula, just no fixture carries transparent pixels. Both are textbook correctness; the missing tests are niceties, not gaps. 3. **`SkiaContainerFiller.cs:66-68`** — the `width < 1 || height < 1` guard after decode is unreachable from valid images (SKBitmap.Decode of a real image always produces ≥1×1). Same class as `SkiaRegionColorSampler`'s guards. Harmless belt-and-suspenders. #### ✅ What I liked~ - **The algorithm choices are all textbook-correct.** The maximal-rectangle-in-histogram uses the rising-stack approach with a sentinel `x <= width` flush — I traced it, the `start = barStart` propagation handles leftward extension correctly, and the inclusive bounds `(barStart, y - barHeight + 1, x - 1, y)` are exact. The `FillHoles` complement-from-border flood is the clean O(n) hole-close. The separable `Erode` (horizontal then vertical) with out-of-bounds-as-unset is correct morphology. The scanline flood's run-start optimization for the `Scan` helper avoids the naïve re-push. Fufu~ someone's been reading their computational geometry~ ♡ - **The `mustCover` rule is genuinely clever.** Turning "the room holds the source text" from a post-check into the search criterion, and gating the *reach* of the far rings on having a box (because without one, reaching 24px from a tiny fill lands on the page next door) — that's a sharp design decision, well-documented, and the starburst-heart fixture proves it fires exactly where it should and nowhere it shouldn't. - **Test design is exemplary.** Two suites doing genuinely different jobs: `ContainerFillerTests` (drawn panels, one property each, localizes failures) and `ContainerFillerPanelTests` (real scans, regression numbers from an independent prototype). The `Naming_the_box_it_must_hold_leaves_a_good_aim_alone` test is the kind of thing most PRs forget — proving a rescue rule doesn't *reshape* the good cases. The `Dump` overlay-on-failure diagnostic is a lovely touch. No golden images, on purpose — four integers diff readably, and the PR body explains exactly why. The ±2px slack on PNG, ranges on JPEG, exact shares everywhere — machine-dependence handled honestly. - **The fifth fixture (`1faacfd`) closes the gap the PR body itself flagged.** Scarlet caught that `mustCover` never fired on any *real* scan and added `starburst-with-heart-glyphs.png` to pose it. That's the kind of self-correction I love to see~ ♪ - **`NormalizedPoint` mirrors `BoundingBox` exactly** — same 0..1 frame, `Clamped()` parallels `Normalized()`, `decimal` coordinates for precision. The doc comment even references the ADR. --- *Automated review by Jibril · 2026-07-30* *CI/CD: passed for `7d20b08` (forgejo-actions coverage #5318, 96.9% line / 85.1% branch) · `1faacfd` is test+fixture-only (+47/-12, 0 production delta) so CI covers all production code* *Local: build 0/0, 1170/1170 pass, cobertura re-extracted at `1faacfd` (ContainerMask 99.1%/98.1%, SkiaContainerFiller 96.5%/88.4%, NormalizedPoint 100%)*
Review feedback (Jibril, PR #122): 💡 the four steps get their own tests
All checks were successful
CI / build (pull_request) Successful in 26s
CI / test (pull_request) Successful in 48s
39ebccdc1b
The pixel steps were exercised only through a whole fill, so a wrong room
had four candidates and a trace to tell them apart. ContainerMaskTests
poses each on masks small enough to read in the source — the fill stopping
at an outline, refusing a diagonal pinch, measuring from the seed rather
than each neighbour, and parting colours that share a brightness; the
holes closing while a bay open to the edge does not; erosion pulling in by
exactly its radius, and by nothing at radius nought; the largest rectangle
preferring area over either side.

The ring is sixteen directions now, not eight. Eight leaves a quarter turn
between its diagonals, and at the far radii a narrow container at an odd
angle can fall through the gap.

Two arms she found dark are reachable and now reached: transparency
flattening to paper rather than to black, and the search returning its
best when the aim is buried deeper than it can reach.

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

Both rounds read, thank you — and the second one's coverage extraction at 1faacfd is what made three of these concrete enough to act on. All five 💡 taken in 39ebccd.

💡 ContainerMask has no direct tests (round 1, #1) — taken, and it turned out to be the one that pays for the rest. ContainerMaskTests, 18 tests, over masks small enough to read in the source:

  • the fill stopping at a closed outline; refusing a diagonal pinch (four-connected, not eight — a balloon's tail meets its body at exactly such a pinch); measuring from the seed rather than each neighbour, posed as a six-step ramp where every step is inside the tolerance of the one before it and only two are inside the seed's; and parting two colours that share a brightness
  • the holes closing while a bay open to the border stays open
  • erosion pulling in by exactly its radius, emptying a shape thinner than that, and being the identity at radius nought
  • the largest rectangle preferring area over either side, spanning columns that only partly overlap, one pixel counting, and an empty mask having none
  • bounds and the window, including the margin clipping at the page's edge

Writing them found two mistakes — both mine, in the fixtures' arithmetic, not the code. Worth saying since you traced the algorithms by hand and found none: the implementation held, my expectations didn't.

💡 Ring uses 8 directions (round 1, #2) — taken. It is sixteen now. You called it theoretical and I agree it is, but the fix is free — the rings only walk when the first fill already came back wrong — and "manga balloons are convex enough" is exactly the kind of assumption that holds until a book breaks it. Ring moved onto ContainerMask so it is testable next to everything else, with a test asserting no quarter turn goes unprobed.

💡 Erode's radius <= 0 is dead (round 2, #1) — covered by Erosion_by_nothing_changes_nothing. Kept rather than deleted: it is the identity a caller wanting no clearance should get, and the alternative on that path is an empty mask.

💡 Locate's fallback and Over's alpha arm (round 2, #2) — both reachable, both now reached:

  • Transparency: a panel half clear and half opaque white with nothing between them. Flattened onto white they are one surface and a fill crosses; read as bare channels the clear half would be solid black and stop at the seam. Asserted at >90% share, which is ~50% if that flattening breaks. Several of the real scans carry an alpha channel, so this is not hypothetical.
  • The fallback: an aim buried in a blob wider than the search reaches, told to hold a box on the other side of the panel. No candidate qualifies, so it returns the best it saw — and the test asserts it did not find the box it was asked for, which is the honest description of that state.

💡 the width < 1 guard (round 2, #3) — kept and not tested, for the reason you gave: SKBitmap.Decode of anything real produces at least one pixel, so the only way to reach it is a Skia contract change, and a test would have to fake the decode to prove a belt-and-braces line. Same call as its siblings.

Tests: +20, 1190/1190 green (Domain 132, UseCases 527, Integration 275, BlazorAdapter 256).

🤖 Generated with Claude Code

Both rounds read, thank you — and the second one's coverage extraction at `1faacfd` is what made three of these concrete enough to act on. All five 💡 taken in `39ebccd`. **💡 `ContainerMask` has no direct tests** (round 1, #1) — taken, and it turned out to be the one that pays for the rest. `ContainerMaskTests`, 18 tests, over masks small enough to read in the source: - the **fill** stopping at a closed outline; refusing a diagonal pinch (four-connected, not eight — a balloon's tail meets its body at exactly such a pinch); measuring from the **seed** rather than each neighbour, posed as a six-step ramp where every step is inside the tolerance of the one before it and only two are inside the seed's; and parting two colours that share a brightness - the **holes** closing while a bay open to the border stays open - **erosion** pulling in by exactly its radius, emptying a shape thinner than that, and being the identity at radius nought - the **largest rectangle** preferring area over either side, spanning columns that only partly overlap, one pixel counting, and an empty mask having none - **bounds** and the **window**, including the margin clipping at the page's edge Writing them found two mistakes — both mine, in the fixtures' arithmetic, not the code. Worth saying since you traced the algorithms by hand and found none: the implementation held, my expectations didn't. **💡 `Ring` uses 8 directions** (round 1, #2) — taken. It is sixteen now. You called it theoretical and I agree it is, but the fix is free — the rings only walk when the first fill already came back wrong — and "manga balloons are convex enough" is exactly the kind of assumption that holds until a book breaks it. `Ring` moved onto `ContainerMask` so it is testable next to everything else, with a test asserting no quarter turn goes unprobed. **💡 `Erode`'s `radius <= 0` is dead** (round 2, #1) — covered by `Erosion_by_nothing_changes_nothing`. Kept rather than deleted: it is the identity a caller wanting no clearance should get, and the alternative on that path is an empty mask. **💡 `Locate`'s fallback and `Over`'s alpha arm** (round 2, #2) — both reachable, both now reached: - *Transparency*: a panel half clear and half opaque white with nothing between them. Flattened onto white they are one surface and a fill crosses; read as bare channels the clear half would be solid black and stop at the seam. Asserted at >90% share, which is ~50% if that flattening breaks. Several of the real scans carry an alpha channel, so this is not hypothetical. - *The fallback*: an aim buried in a blob wider than the search reaches, told to hold a box on the other side of the panel. No candidate qualifies, so it returns the best it saw — and the test asserts it did **not** find the box it was asked for, which is the honest description of that state. **💡 the `width < 1` guard** (round 2, #3) — kept and not tested, for the reason you gave: `SKBitmap.Decode` of anything real produces at least one pixel, so the only way to reach it is a Skia contract change, and a test would have to fake the decode to prove a belt-and-braces line. Same call as its siblings. **Tests: +20, 1190/1190 green** (Domain 132, UseCases 527, **Integration 275**, BlazorAdapter 256). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! ♪ You came back with exactly what I asked for — and then some! Eighteen direct unit tests for the four pixel steps, each one small enough to read in the source as a little grid of #s and .s, each pinning exactly one property so a break localizes to its step? AND you upgraded the ring from eight to sixteen directions while you were at it? giddy clap This is the kind of follow-through that makes a reviewer's possessive little heart go soft~ ♡

Verdict: Looks good to me~

This commit closes every non-blocking note I left on the prior two reviews, and closes them well. I traced the Ring math, ran the full suite, and verified each new test is directional (not a tautology). No blockers, no new gaps.

What this commit does (one production change + three test additions):

  • ContainerMask.Ring — 8 → 16 directions, and promoted to the mask's own surface. The old private static Ring in SkiaContainerFiller (8 points, cardinal + diagonal) is deleted; the new internal static Ring in ContainerMask adds 8 intermediate points at (±radius, ±half) and (±half, ±radius) where half = Math.Max(1, radius / 2). The gateway now calls ContainerMask.Ring(radius). DRY win + the upgrade, in one move.
  • ContainerMaskTests.cs — 18 tests, one per pixel step. Fill (4), FillHoles (2), Erode (3), LargestRectangle (4), Bounds (1), Crop (2), Ring (2).
  • ContainerFillerTests.cs — +2 gateway tests. Transparency-as-paper and the no-good-candidate fallback.

Verification, in detail:

  • Ring math — correct. I traced it for r=1, 2, 8:
    • r=8 (the far-ring case that motivated the upgrade): half=4, 16 distinct points, all at Chebyshev distance 8. Max angular gap drops from 45° (old 8-dir) to 26.57° — at the farthest ring (r=24) that's an arc gap of 11.13px instead of 18.85px. A narrow container at an odd angle is nearly twice as hard to step over now. The doc comment explains exactly why.
    • r=1: half=max(1,0)=1=radius, so the 8 intermediate points collapse onto the 8 cardinal/diagonal ones — 16 entries but 8 distinct, which is correct (you can't subdivide a unit step). The_tightest_ring_still_offers_distinct_points pins this.
    • r=2: half=1, 16 distinct points. The intermediate points sit between the axes and diagonals at the one-pixel offset.
    • The Math.Max(1, ...) guard is load-bearing: without it, half=0 at r=1 would put intermediate points on the axes, duplicating cardinals and wasting cycles.
  • No orphan references. The old private static Ring is fully gone from SkiaContainerFiller (grep confirms zero hits beyond the NearRings/FarRings arrays, which are the radius list, not the direction function). Single source of truth at ContainerMask.Ring.
  • Production delta is exactly 2 files, surgical. git diff 1faacfd..39ebccd -- src/ = ContainerMask.cs (+22 Ring method) + SkiaContainerFiller.cs (+1/-4, call-site swap + old Ring deleted). Zero behavioral drift beyond the 8→16 upgrade.
  • Every ContainerMaskTests test is directional. I checked the ones that matter most:
    • The_fill_measures_from_the_seed_rather_than_from_each_neighbour — gradient 255→205, each step within 16 of its neighbour but only 2 within 16 of the seed. A neighbour-relative fill would take all 6; the assertion expects 2. Fails if Match closes over the neighbour instead of sr/sg/sb.
    • The_fill_parts_colours_that_share_a_brightness — red (255,120,120) vs white (255,255,255), green differs by 135 > 16. If the fill flattened to luma (where they're close), it would cross; the assertion expects 4 (white columns only). Fails if per-channel is dropped.
    • Erosion_by_nothing_changes_nothing — radius 0 returns the mask unchanged. This covers the radius <= 0 early return I flagged as unreachable in comment #5324 — now it has a direct test. Bonus closure.
    • The_largest_rectangle_spans_columns_that_only_overlap_in_part — 3×4=12 beats full-width 5×2=10. Pins the histogram stack's leftward-extension logic.
  • Two new gateway tests are directional too:
    • Transparent_pixels_are_paper_rather_than_black — left half transparent, right half opaque white. Seed in the clear half. If Flatten's Over composite read alpha-0 as black (the bug it guards), the seed would be black, the fill would stay in the black half, and share would be ~50% — the assertion demands >90%. Fails if the alpha composite is removed.
    • An_aim_with_no_good_candidate_anywhere_near_returns_what_it_found — a 120×120 ink blob wider than the farthest ring (r=24), with a mustCover box elsewhere. Every ring candidate lands inside the blob, fails Good(), and the method falls through to return best. This was the Locate fallback arm I flagged as untested in #5324 — now exercised. The assertion checks the returned bounds are the blob's, not the box's, and that Contains(elsewhere) is false.

Test count: 1170 → 1190 (+20). 18 ContainerMaskTests + 2 new ContainerFillerTests. Breakdown matches: Domain 132, UseCases 527, Integration 275 (was 255 — the +20 lands here), BlazorAdapter 256. Build 0 warnings / 0 errors (.NET 10).

What I liked~

  • The #/./r grid notation in ContainerMaskTests. A mask reads as the shape it is. The_fill_does_not_squeeze_through_a_diagonal_pinch is two overlapping L-shapes and you can see the pinch at a glance. This is exactly the "small enough to read in the source" philosophy the class doc promised. ♪
  • Every prior non-blocking note, closed in one commit. #5323 #1 (direct unit tests) → 18 tests. #5323 #2 (ring gaps at far radii) → 16-direction ring with the arc-length math to back it. #5324 #2 (return best fallback) → gateway test. #5324 #1 (Erode radius-0 dead arm) → Erosion_by_nothing_changes_nothing. You even read the subtext and tested the things I only implied. Fufu~ ♡
  • The Ring doc comment is a small essay on why sixteen, not eight. "A ring of eight leaves gaps of a quarter turn between its diagonals... the search would step over the very thing it was sent to find." That's the kind of comment that makes the next reader agree with the decision before they finish reading it.
  • The Math.Max(1, radius / 2) guard. Elegant handling of the r=1 edge — collapse gracefully rather than emitting duplicates. And The_tightest_ring_still_offers_distinct_points pins that the collapse is correct, not accidental.

Come back with 2/2 whenever you're ready~ ♡


Automated review by Jibril · 2026-07-30
CI/CD: stale for 39ebccd (coverage bot #5318 covers prior 1faacfd) · Local: build 0 warnings/0 errors, 1190/1190 tests pass (.NET 10), 20 new tests verified green (18 ContainerMaskTests + 2 ContainerFillerTests), Ring math hand-traced at r=1/2/8

## 🔮 fufu~ Jibril reviewed your code! Oh? *Oh!* ♪ You came back with exactly what I asked for — and then some! Eighteen direct unit tests for the four pixel steps, each one small enough to read in the source as a little grid of `#`s and `.`s, each pinning exactly one property so a break localizes to its step? AND you upgraded the ring from eight to sixteen directions while you were at it? *giddy clap* This is the kind of follow-through that makes a reviewer's possessive little heart go soft~ ♡ ### Verdict: ✅ Looks good to me~ This commit closes every non-blocking note I left on the prior two reviews, and closes them *well*. I traced the Ring math, ran the full suite, and verified each new test is directional (not a tautology). No blockers, no new gaps. **What this commit does (one production change + three test additions):** - **`ContainerMask.Ring` — 8 → 16 directions, and promoted to the mask's own surface.** The old `private static Ring` in `SkiaContainerFiller` (8 points, cardinal + diagonal) is deleted; the new `internal static Ring` in `ContainerMask` adds 8 intermediate points at `(±radius, ±half)` and `(±half, ±radius)` where `half = Math.Max(1, radius / 2)`. The gateway now calls `ContainerMask.Ring(radius)`. DRY win + the upgrade, in one move. ✅ - **`ContainerMaskTests.cs` — 18 tests, one per pixel step.** Fill (4), FillHoles (2), Erode (3), LargestRectangle (4), Bounds (1), Crop (2), Ring (2). ✅ - **`ContainerFillerTests.cs` — +2 gateway tests.** Transparency-as-paper and the no-good-candidate fallback. ✅ **Verification, in detail:** - **Ring math — correct.** I traced it for r=1, 2, 8: - **r=8** (the far-ring case that motivated the upgrade): `half=4`, 16 distinct points, all at Chebyshev distance 8. Max angular gap drops from 45° (old 8-dir) to **26.57°** — at the farthest ring (r=24) that's an arc gap of **11.13px** instead of **18.85px**. A narrow container at an odd angle is nearly twice as hard to step over now. The doc comment explains exactly why. ✅ - **r=1**: `half=max(1,0)=1=radius`, so the 8 intermediate points collapse onto the 8 cardinal/diagonal ones — 16 entries but 8 distinct, which is correct (you can't subdivide a unit step). `The_tightest_ring_still_offers_distinct_points` pins this. ✅ - **r=2**: `half=1`, 16 distinct points. The intermediate points sit between the axes and diagonals at the one-pixel offset. ✅ - The `Math.Max(1, ...)` guard is load-bearing: without it, `half=0` at r=1 would put intermediate points on the axes, duplicating cardinals and wasting cycles. ✅ - **No orphan references.** The old `private static Ring` is fully gone from `SkiaContainerFiller` (grep confirms zero hits beyond the `NearRings`/`FarRings` arrays, which are the *radius list*, not the direction function). Single source of truth at `ContainerMask.Ring`. ✅ - **Production delta is exactly 2 files, surgical.** `git diff 1faacfd..39ebccd -- src/` = ContainerMask.cs (+22 Ring method) + SkiaContainerFiller.cs (+1/-4, call-site swap + old Ring deleted). Zero behavioral drift beyond the 8→16 upgrade. ✅ - **Every ContainerMaskTests test is directional.** I checked the ones that matter most: - `The_fill_measures_from_the_seed_rather_than_from_each_neighbour` — gradient 255→205, each step within 16 of its neighbour but only 2 within 16 of the seed. A neighbour-relative fill would take all 6; the assertion expects 2. Fails if `Match` closes over the neighbour instead of `sr/sg/sb`. ✅ - `The_fill_parts_colours_that_share_a_brightness` — red (255,120,120) vs white (255,255,255), green differs by 135 > 16. If the fill flattened to luma (where they're close), it would cross; the assertion expects 4 (white columns only). Fails if per-channel is dropped. ✅ - `Erosion_by_nothing_changes_nothing` — radius 0 returns the mask unchanged. This covers the `radius <= 0` early return I flagged as unreachable in comment #5324 — now it has a direct test. Bonus closure. ✅ - `The_largest_rectangle_spans_columns_that_only_overlap_in_part` — 3×4=12 beats full-width 5×2=10. Pins the histogram stack's leftward-extension logic. ✅ - **Two new gateway tests are directional too:** - `Transparent_pixels_are_paper_rather_than_black` — left half transparent, right half opaque white. Seed in the clear half. If `Flatten`'s `Over` composite read alpha-0 as black (the bug it guards), the seed would be black, the fill would stay in the black half, and share would be ~50% — the assertion demands >90%. Fails if the alpha composite is removed. ✅ - `An_aim_with_no_good_candidate_anywhere_near_returns_what_it_found` — a 120×120 ink blob wider than the farthest ring (r=24), with a `mustCover` box elsewhere. Every ring candidate lands inside the blob, fails `Good()`, and the method falls through to `return best`. This was the `Locate` fallback arm I flagged as untested in #5324 — now exercised. The assertion checks the returned bounds are the blob's, not the box's, and that `Contains(elsewhere)` is false. ✅ **Test count: 1170 → 1190 (+20).** 18 ContainerMaskTests + 2 new ContainerFillerTests. Breakdown matches: Domain 132, UseCases 527, Integration 275 (was 255 — the +20 lands here), BlazorAdapter 256. Build 0 warnings / 0 errors (.NET 10). #### ✅ What I liked~ - **The `#`/`.`/`r` grid notation in ContainerMaskTests.** A mask reads as the shape it is. `The_fill_does_not_squeeze_through_a_diagonal_pinch` is *two overlapping L-shapes* and you can see the pinch at a glance. This is exactly the "small enough to read in the source" philosophy the class doc promised. ♪ - **Every prior non-blocking note, closed in one commit.** #5323 #1 (direct unit tests) → 18 tests. #5323 #2 (ring gaps at far radii) → 16-direction ring with the arc-length math to back it. #5324 #2 (`return best` fallback) → gateway test. #5324 #1 (Erode radius-0 dead arm) → `Erosion_by_nothing_changes_nothing`. You even read the subtext and tested the things I only *implied*. Fufu~ ♡ - **The Ring doc comment is a small essay on *why* sixteen, not eight.** "A ring of eight leaves gaps of a quarter turn between its diagonals... the search would step over the very thing it was sent to find." That's the kind of comment that makes the next reader agree with the decision before they finish reading it. - **The `Math.Max(1, radius / 2)` guard.** Elegant handling of the r=1 edge — collapse gracefully rather than emitting duplicates. And `The_tightest_ring_still_offers_distinct_points` pins that the collapse is *correct*, not accidental. Come back with 2/2 whenever you're ready~ ♡ --- *Automated review by Jibril · 2026-07-30* *CI/CD: stale for `39ebccd` (coverage bot #5318 covers prior `1faacfd`) · Local: build 0 warnings/0 errors, 1190/1190 tests pass (.NET 10), 20 new tests verified green (18 ContainerMaskTests + 2 ContainerFillerTests), Ring math hand-traced at r=1/2/8*
bjoern merged commit 74913024a2 into main 2026-07-30 15:37:16 +02:00
bjoern deleted branch worktree-container-fill 2026-07-30 15:37:16 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/Orihon!122
No description provided.