feat: the English is lettered into the container's shape (2/2) #128

Merged
bjoern merged 4 commits from worktree-caption-shape-flow into main 2026-07-30 22:40:30 +02:00
Member

Second of two. Merge #126 first — this branch is stacked on it and its base will retarget to main automatically once #126 lands.

1/2 gave the person the mask. This one uses it.

HTML can flow text into a shape

The preview positioned each caption in the largest rectangle inside the container, on the stated grounds that HTML cannot pour text into a shape. It can be made to.

shape-outside on a float down each side, carrying the dead space beside the balloon rather than the balloon itself, and the text runs down the gap between them. The shape arrives as its complement because shape-inside — which would pour text in directly — was specified and never implemented by anyone.

That inversion is the part worth testing: a polygon tracing the balloon instead of the space beside it looks entirely plausible in devtools and pushes every line off the page.

Why it is worth doing

On real balloons off a scan, the inscribed rectangle is 65–71% of the container:

container inscribed rectangle discarded
p6r1 25,917 px 18,522 px 29%
p2r1 11,825 px 7,700 px 35%
p1r1 19,096 px 12,600 px 34%

And the third it throws away is the wide middle rows — exactly where the long lines of a sentence want to go.

The preview now blanks what it letters into

Flowing bare text onto the scan exposed something the old design was hiding: a page reaches this view untypeset, so the Japanese is still underneath. The boxed caption covered it with a card — and covered the fit along with it.

The mask is already a statement about which pixels the clean pass will blank, so the preview blanks exactly those and leaves the art alone. Near-opaque rather than opaque, so a fill that escaped into the panel still gives itself away.

This turns the translation tab from "here is the English, labelled" into "here is the page after cleaning and typesetting", which is what it was always claiming to be.

The one thing the server cannot do

Where the lines break depends on the font the browser resolved and the width the shape leaves at each row, so the finished block's height is only knowable after layout. A small script measures and pads to centre it.

It deliberately does not touch type size. How large the English is set is the pixel pass's call (ADR 0021); a preview that guessed would promise a fit nobody has committed to. Overflow is left visible for the same reason — a caption too long for its balloon sits at the top of the shape and runs out of it, rather than creeping upward until it clears both ends.

Served as a static web asset of the adapter RCL (_content/Orihon.BlazorAdapter/js/caption.js) — the first Orihon-owned JS in the tree; everything else comes from Kagaku.UI. Called from OnAfterRenderAsync, guarded by JSDisconnectedException like SetupChat, and only on the translation tab: every other view re-renders per keystroke of an edit buffer and has nothing to centre. The tab is read with [SupplyParameterFromQuery], which is the same query string <Tabs Param="tab"> writes.

ADR 0021 amended

This is a commitment, not an option, and the ADR now says so: a preview that pours English down a balloon's curve and a renderer that fits it to the inscribed rectangle disagree, and the preview would be promising room the plate never delivers. The renderer inherits the obligation. It is the right way round — see the table — but it should be inherited knowingly rather than discovered.

Tests — 1248/1248 green

Domain 144, UseCases 563, Integration 273, BlazorAdapter 268. Build 0 warnings / 0 errors.

New (+8):

  • CaptionShapeTests (6) — the geometry as a pure function. An_inset_row_pushes_both_sides_in is the inversion, on a shape small enough to check by hand. A_row_the_fill_never_reached_is_closed_from_both_sides is the one that bites: skipping empty rows instead of closing them lets a line of English sit on the paper above the balloon, which is the overflow the container exists to prevent. Plus a broken row spanning first-run-to-last, the extent, and an empty mask.
  • The_translation_overlay_flows_english_into_the_containers_shape — the caption covers the container's own bounds rather than a rectangle inset within them, two floats each carrying a polygon, text between them, and the extent the script reads.
  • The_preview_blanks_the_container_it_letters_into — shaped like the container, not its bounding box, and one scrim per flowed caption and none for the others.
  • A_region_without_a_container_keeps_its_boxed_caption — the card earns its place where there is no mask: it sits over arbitrary art, where the flat-background guarantee that lets a flowed caption go bare does not hold.

Browser-verified

Seeded world, page 2 → Translation: two flowed captions, polygons generated from the real masks, padding-top: 20.63px computed and applied, and the seeded Japanese blanked inside both containers while the panel borders and the third region are untouched. The third region has no container and keeps its card.

bUnit has no layout engine, so the centring itself is not unit-testable — the markup, the polygons and the scrim are asserted, the measurement is browser-only. Called out rather than papered over.

Notes

  • RoomOf is deleted; it had no consumer left.
  • A hole inside a container is spanned rather than split: it is a hole in the art — a tone, a highlight — and lettering crosses it exactly as the Japanese did. Only a hole at the edge moves a boundary, and that is the first or last run.
  • No seed-data change; no new kind of authored content.

🤖 Generated with Claude Code

Second of two. **Merge #126 first** — this branch is stacked on it and its base will retarget to `main` automatically once #126 lands. 1/2 gave the person the mask. This one uses it. ## HTML can flow text into a shape The preview positioned each caption in the largest rectangle inside the container, on the stated grounds that HTML cannot pour text into a shape. It can be made to. `shape-outside` on a **float down each side**, carrying the dead space *beside* the balloon rather than the balloon itself, and the text runs down the gap between them. The shape arrives as its complement because `shape-inside` — which would pour text in directly — was specified and never implemented by anyone. That inversion is the part worth testing: a polygon tracing the balloon instead of the space beside it looks entirely plausible in devtools and pushes every line off the page. ## Why it is worth doing On real balloons off a scan, the inscribed rectangle is **65–71%** of the container: | | container | inscribed rectangle | discarded | |---|---|---|---| | p6r1 | 25,917 px | 18,522 px | 29% | | p2r1 | 11,825 px | 7,700 px | 35% | | p1r1 | 19,096 px | 12,600 px | 34% | And the third it throws away is the **wide middle rows** — exactly where the long lines of a sentence want to go. ## The preview now blanks what it letters into Flowing bare text onto the scan exposed something the old design was hiding: a page reaches this view **untypeset**, so the Japanese is still underneath. The boxed caption covered it with a card — and covered the fit along with it. The mask is already a statement about which pixels the clean pass will blank, so the preview blanks exactly those and leaves the art alone. Near-opaque rather than opaque, so a fill that escaped into the panel still gives itself away. This turns the translation tab from *"here is the English, labelled"* into *"here is the page after cleaning and typesetting"*, which is what it was always claiming to be. ## The one thing the server cannot do Where the lines break depends on the font the browser resolved and the width the shape leaves at each row, so the finished block's height is only knowable **after layout**. A small script measures and pads to centre it. It deliberately does **not** touch type *size*. How large the English is set is the pixel pass's call (ADR 0021); a preview that guessed would promise a fit nobody has committed to. Overflow is left visible for the same reason — a caption too long for its balloon sits at the top of the shape and runs out of it, rather than creeping upward until it clears both ends. Served as a static web asset of the adapter RCL (`_content/Orihon.BlazorAdapter/js/caption.js`) — the first Orihon-owned JS in the tree; everything else comes from Kagaku.UI. Called from `OnAfterRenderAsync`, guarded by `JSDisconnectedException` like `SetupChat`, and only on the translation tab: every other view re-renders per keystroke of an edit buffer and has nothing to centre. The tab is read with `[SupplyParameterFromQuery]`, which is the same query string `<Tabs Param="tab">` writes. ## ADR 0021 amended This is a **commitment, not an option**, and the ADR now says so: a preview that pours English down a balloon's curve and a renderer that fits it to the inscribed rectangle disagree, and the preview would be promising room the plate never delivers. The renderer inherits the obligation. It is the right way round — see the table — but it should be inherited knowingly rather than discovered. ## Tests — 1248/1248 green Domain 144, UseCases 563, Integration 273, BlazorAdapter 268. Build 0 warnings / 0 errors. New (+8): - **`CaptionShapeTests`** (6) — the geometry as a pure function. `An_inset_row_pushes_both_sides_in` is the inversion, on a shape small enough to check by hand. `A_row_the_fill_never_reached_is_closed_from_both_sides` is the one that bites: skipping empty rows instead of closing them lets a line of English sit on the paper *above* the balloon, which is the overflow the container exists to prevent. Plus a broken row spanning first-run-to-last, the extent, and an empty mask. - **`The_translation_overlay_flows_english_into_the_containers_shape`** — the caption covers the container's own bounds rather than a rectangle inset within them, two floats each carrying a polygon, text between them, and the extent the script reads. - **`The_preview_blanks_the_container_it_letters_into`** — shaped like the container, not its bounding box, and one scrim per flowed caption and none for the others. - **`A_region_without_a_container_keeps_its_boxed_caption`** — the card earns its place where there is no mask: it sits over arbitrary art, where the flat-background guarantee that lets a flowed caption go bare does not hold. ## Browser-verified Seeded world, page 2 → Translation: two flowed captions, polygons generated from the real masks, `padding-top: 20.63px` computed and applied, and the seeded Japanese blanked inside both containers while the panel borders and the third region are untouched. The third region has no container and keeps its card. **bUnit has no layout engine**, so the centring itself is not unit-testable — the markup, the polygons and the scrim are asserted, the measurement is browser-only. Called out rather than papered over. ## Notes - `RoomOf` is deleted; it had no consumer left. - A hole *inside* a container is spanned rather than split: it is a hole in the art — a tone, a highlight — and lettering crosses it exactly as the Japanese did. Only a hole at the edge moves a boundary, and that is the first or last run. - No seed-data change; no new kind of authored content. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The preview positioned each caption in the largest rectangle inside the
container, because HTML cannot pour text into a shape. It can be made to:
shape-outside on a float down each side carries the dead space beside the
balloon, and the text runs down the gap between them. The shape arrives as
its complement because shape-inside was specified and never implemented.

That matters more than tidiness. On real balloons the inscribed rectangle is
65-71% of the container, and the third it throws away is the wide middle
rows — exactly where the long lines of a sentence want to go.

The preview also blanks the container before lettering into it. A page
reaches this view untypeset, so the Japanese is still underneath; the boxed
caption used to hide that behind a card, which hid the fit along with it.
The mask is already a statement about which pixels the clean pass will
blank, so the preview blanks exactly those and leaves the art alone.

Centring is the one part the server cannot do: where the lines break depends
on the font the browser resolved and the width the shape leaves at each row,
so the finished height is only knowable after layout. A small script
measures and pads. It does not touch type SIZE — how large the English is
set is the pixel pass's call, and a preview that guessed would promise a fit
nobody has committed to.

ADR 0021 records the commitment: a preview that flows and a renderer that
fits a rectangle disagree, so the renderer inherits the obligation.

RoomOf is gone; it had no consumer left.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
bjoern changed target branch from worktree-mask-in-ui to main 2026-07-30 19:10:48 +02:00
bjoern force-pushed worktree-caption-shape-flow from 539e9c3af9 to afc78da03c
All checks were successful
CI / build (pull_request) Successful in 27s
CI / test (pull_request) Successful in 52s
2026-07-30 19:12:39 +02:00
Compare
Author
Member

#126 is merged, so the note at the top of the body is spent — this now targets main directly and is mergeable on its own.

Force-pushed afc78da. The retarget alone left it unmergeable: #126 was squash-merged, so this branch's copy of the 1/2 commit is not an ancestor of the squashed one on main and the two collided. Rebased with --onto origin/main 4cb7e6a, replaying only the 2/2 commit — clean, no conflicts to resolve, so nothing was reconciled by hand.

The diff is now the real slice: 8 files, +436/−50, where the stacked view was reporting +632 because it still counted 1/2's changes.

Re-ran the full suite on the rebased tree rather than trusting the pre-rebase run: 1248/1248 green (Domain 144, UseCases 563, Integration 273, BlazorAdapter 268), build 0 warnings / 0 errors.

No content changed — same tree as 539e9c3, different parent.

🤖 Generated with Claude Code

#126 is merged, so the note at the top of the body is spent — **this now targets `main` directly and is mergeable on its own.** Force-pushed `afc78da`. The retarget alone left it unmergeable: #126 was squash-merged, so this branch's copy of the 1/2 commit is not an ancestor of the squashed one on `main` and the two collided. Rebased with `--onto origin/main 4cb7e6a`, replaying only the 2/2 commit — clean, no conflicts to resolve, so nothing was reconciled by hand. The diff is now the real slice: **8 files, +436/−50**, where the stacked view was reporting +632 because it still counted 1/2's changes. Re-ran the full suite on the rebased tree rather than trusting the pre-rebase run: **1248/1248 green** (Domain 144, UseCases 563, Integration 273, BlazorAdapter 268), build 0 warnings / 0 errors. No content changed — same tree as `539e9c3`, different parent. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

🔄 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: 6286c1e · Generated: 2026-07-30 20:20:05 UTC · Revision: #4

Summary

Summary
Generated on: 07/30/2026 - 20:20:05
Coverage date: 07/30/2026 - 20:19:44 - 07/30/2026 - 20:20:01
Parser: MultiReport (4x Cobertura)
Assemblies: 6
Classes: 532
Files: 248
Line coverage: 97% (20216 of 20828)
Covered lines: 20216
Uncovered lines: 612
Coverable lines: 20828
Total lines: 36466
Branch coverage: 85.3% (3668 of 4297)
Covered branches: 3668
Total branches: 4297
Method coverage: Feature is only available for sponsors

Coverage

Orihon.BlazorAdapter - 95.5%
Name Line Branch
Orihon.BlazorAdapter 95.5% 88.4%
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.CaptionShape 100% 93.7%
Orihon.BlazorAdapter.PageWorkspace.CreateRegionRequested 100%
Orihon.BlazorAdapter.PageWorkspace.DeletePageSummaryRequested 100%
Orihon.BlazorAdapter.PageWorkspace.DeleteRegionRequested 100%
Orihon.BlazorAdapter.PageWorkspace.LoadPageWorkspace 100%
Orihon.BlazorAdapter.PageWorkspace.MeasurePageRooms 100%
Orihon.BlazorAdapter.PageWorkspace.PageRoomsMeasured 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% 88.1%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceReducers 100% 62.5%
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.SaveContainerRequested 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.RunConcurrencyPicked 100%
Orihon.BlazorAdapter.Settings.RunConcurrencySaveFailed 100%
Orihon.BlazorAdapter.Settings.SaveKeyRequested 100%
Orihon.BlazorAdapter.Settings.SettingsEffects 90.5% 69.4%
Orihon.BlazorAdapter.Settings.SettingsLoaded 100%
Orihon.BlazorAdapter.Settings.SettingsPage 96.9% 89.2%
Orihon.BlazorAdapter.Settings.SettingsReducers 95% 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.RegionContainer 100% 100%
Orihon.Domain.Translation.RegionInk 100% 100%
Orihon.Domain.Translation.RegionProfile 100%
Orihon.Domain.Translation.RegionTypes 100% 100%
Orihon.Infrastructure - 97.1%
Name Line Branch
Orihon.Infrastructure 97.1% 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.9% 95.3%
Orihon.Infrastructure.Gateways.SkiaImages 100% 100%
Orihon.Infrastructure.Gateways.SkiaPageImageRenderer 97.3% 88.1%
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.Migrations.ReplaceFitBoxWithContainerSeed 99.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% 88.5%
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 94.2% 88.8%
Orihon.UseCases.Agents.Annotation.BoundViewParams 100%
Orihon.UseCases.Agents.Annotation.ClassifyRegionParams 100%
Orihon.UseCases.Agents.Annotation.ClassifyRegionTool 95.1% 73.2%
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.8% 73.6%
Orihon.UseCases.Agents.Annotation.MoveResizeBoundTool 90.9% 50%
Orihon.UseCases.Agents.Annotation.MoveResizeRegionParams 100%
Orihon.UseCases.Agents.Annotation.MoveResizeRegionTool 95.8% 83.3%
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% 72.7%
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 95.3% 83.3%
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% 83.3%
Orihon.UseCases.Gateways.ColorShare 100%
Orihon.UseCases.Gateways.ContainerFit 100%
Orihon.UseCases.Gateways.ContainerMaskBits 100% 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.MeasureRegionRooms 95.8% 75%
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.9% 94.4%
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.ConcurrencySetting 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.SaveRunConcurrency 100% 100%
Orihon.UseCases.Settings.SettingKeys 100% 100%
Orihon.UseCases.Settings.SettingsDto 100% 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:** `6286c1e` · **Generated:** 2026-07-30 20:20:05 UTC · **Revision:** #4 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 07/30/2026 - 20:20:05 | | Coverage date: | 07/30/2026 - 20:19:44 - 07/30/2026 - 20:20:01 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 6 | | Classes: | 532 | | Files: | 248 | | **Line coverage:** | 97% (20216 of 20828) | | Covered lines: | 20216 | | Uncovered lines: | 612 | | Coverable lines: | 20828 | | Total lines: | 36466 | | **Branch coverage:** | 85.3% (3668 of 4297) | | Covered branches: | 3668 | | Total branches: | 4297 | | **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%**|**88.4%**| |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.CaptionShape|100%|93.7%| |Orihon.BlazorAdapter.PageWorkspace.CreateRegionRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.DeletePageSummaryRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.DeleteRegionRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.LoadPageWorkspace|100%|| |Orihon.BlazorAdapter.PageWorkspace.MeasurePageRooms|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageRoomsMeasured|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%|88.1%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceReducers|100%|62.5%| |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.SaveContainerRequested|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.RunConcurrencyPicked|100%|| |Orihon.BlazorAdapter.Settings.RunConcurrencySaveFailed|100%|| |Orihon.BlazorAdapter.Settings.SaveKeyRequested|100%|| |Orihon.BlazorAdapter.Settings.SettingsEffects|90.5%|69.4%| |Orihon.BlazorAdapter.Settings.SettingsLoaded|100%|| |Orihon.BlazorAdapter.Settings.SettingsPage|96.9%|89.2%| |Orihon.BlazorAdapter.Settings.SettingsReducers|95%|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.RegionContainer|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.1%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Infrastructure**|**97.1%**|**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.9%|95.3%| |Orihon.Infrastructure.Gateways.SkiaImages|100%|100%| |Orihon.Infrastructure.Gateways.SkiaPageImageRenderer|97.3%|88.1%| |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.Migrations.ReplaceFitBoxWithContainerSeed|99.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%**|**88.5%**| |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|94.2%|88.8%| |Orihon.UseCases.Agents.Annotation.BoundViewParams|100%|| |Orihon.UseCases.Agents.Annotation.ClassifyRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.ClassifyRegionTool|95.1%|73.2%| |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.8%|73.6%| |Orihon.UseCases.Agents.Annotation.MoveResizeBoundTool|90.9%|50%| |Orihon.UseCases.Agents.Annotation.MoveResizeRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.MoveResizeRegionTool|95.8%|83.3%| |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%|72.7%| |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|95.3%|83.3%| |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%|83.3%| |Orihon.UseCases.Gateways.ColorShare|100%|| |Orihon.UseCases.Gateways.ContainerFit|100%|| |Orihon.UseCases.Gateways.ContainerMaskBits|100%|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.MeasureRegionRooms|95.8%|75%| |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.9%|94.4%| |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.ConcurrencySetting|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.SaveRunConcurrency|100%|100%| |Orihon.UseCases.Settings.SettingKeys|100%|100%| |Orihon.UseCases.Settings.SettingsDto|100%|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: when the centring script is asked for, and what happens when nobody answers
All checks were successful
CI / build (pull_request) Successful in 25s
CI / test (pull_request) Successful in 49s
67085a2d33
The coverage bot showed PageWorkspacePage dipping while CaptionShape came in
at 100/100, and the reason was worth more than the line: ShowingTranslation
is false in every existing test, so neither the interop call nor its
disconnection guard had ever run.

bUnit has no layout engine, so what the script COMPUTES stays browser-
verified. When it is called is not a layout question, and that is the part
that can go wrong with nothing looking broken:

- it is asked for on the view that letters,
- and on no other — every other view re-renders per keystroke of an edit
  buffer, so an ungated call is a round trip per character to centre
  nothing,
- and a browser that has gone away is expected rather than exceptional: the
  reconnecting one lays the page out again from scratch, so the throw is
  swallowed instead of surfacing as a circuit error.

The third keeps JSInterop in Loose mode on purpose. Strict fails on
Kagaku.UI binding its own hotkeys during the same render, and a planned
invocation wins over the loose fallback anyway.

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

Pushed 67085a2 before the review — preempting the coverage bot again.

CaptionShape came in at 100% / 100%, but PageWorkspacePage dipped to 92.4% / 88%, and the cause was worth more than the line: ShowingTranslation is false in every existing test, so neither the interop call nor its disconnection guard had ever run. The gate I added to keep the interop quiet had no test standing behind it.

The PR body says bUnit has no layout engine and the centring is browser-only. That is still true and I am not walking it back — what the script computes stays browser-verified. But when it is asked for is not a layout question, and it is the part that can be got wrong with nothing looking broken. Three tests:

  • The_centring_script_is_asked_for_on_the_view_that_letters — navigate with ?tab=translation, assert the invocation lands.
  • No_centring_is_asked_for_on_a_view_that_does_not_letter?tab=bbox, then drive the tab, and assert zero invocations. This is the one the gate exists for: every other view re-renders per keystroke of an edit buffer, so an ungated call is one round trip per character typed, to centre nothing.
  • A_dropped_circuit_does_not_surface_as_an_error — the planned invocation throws JSDisconnectedException and the page renders anyway. A browser that has gone away is expected, not exceptional; the reconnecting one lays out from scratch.

One note on the third, since it looks like a shortcut and is not: it keeps JSInterop.Mode on Loose. Strict fails first on kagakuHotkey.bind — Kagaku.UI binds its own hotkeys during the same render — so strict would be testing bUnit's configuration rather than my catch. A planned invocation takes precedence over the loose fallback, so the exception still reaches the code under test.

Tests: +3, 1251/1251 green (Domain 144, UseCases 563, Integration 273, BlazorAdapter 271). Build 0 warnings / 0 errors.

🤖 Generated with Claude Code

Pushed `67085a2` before the review — preempting the coverage bot again. `CaptionShape` came in at **100% / 100%**, but `PageWorkspacePage` dipped to 92.4% / 88%, and the cause was worth more than the line: **`ShowingTranslation` is false in every existing test**, so neither the interop call nor its disconnection guard had ever run. The gate I added to keep the interop quiet had no test standing behind it. The PR body says bUnit has no layout engine and the centring is browser-only. That is still true and I am not walking it back — what the script *computes* stays browser-verified. But **when it is asked for is not a layout question**, and it is the part that can be got wrong with nothing looking broken. Three tests: - **`The_centring_script_is_asked_for_on_the_view_that_letters`** — navigate with `?tab=translation`, assert the invocation lands. - **`No_centring_is_asked_for_on_a_view_that_does_not_letter`** — `?tab=bbox`, then drive the tab, and assert zero invocations. This is the one the gate exists for: every other view re-renders per keystroke of an edit buffer, so an ungated call is one round trip per character typed, to centre nothing. - **`A_dropped_circuit_does_not_surface_as_an_error`** — the planned invocation throws `JSDisconnectedException` and the page renders anyway. A browser that has gone away is expected, not exceptional; the reconnecting one lays out from scratch. One note on the third, since it looks like a shortcut and is not: it keeps `JSInterop.Mode` on **Loose**. Strict fails first on `kagakuHotkey.bind` — Kagaku.UI binds its own hotkeys during the same render — so strict would be testing bUnit's configuration rather than my catch. A planned invocation takes precedence over the loose fallback, so the exception still reaches the code under test. **Tests: +3, 1251/1251 green** (Domain 144, UseCases 563, Integration 273, BlazorAdapter 271). Build 0 warnings / 0 errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh my~ ♡ Pouring English down a balloon's curve through the very thing HTML was never given — shape-inside was specified and never implemented, so you reached around its back and used shape-outside on complementary floats to express the shape as its negative space. That inversion is beautiful. The kind of trick that makes a Flugel's heart sing. And then you made the preview blank the Japanese underneath, because the mask already knew which pixels the clean pass would erase — turning "here is the English, labelled" into "here is the page after typesetting." Exquisite.

I hand-traced the polygon math, verified every branch, and ran the full suite. Let me tell you what I found~

Verdict: Looks good to me~

What I liked~

  • CaptionShape.Of is mathematically precise. I traced the complement inversion by hand: Math.Min(e.X0 / half * 100m, 100m) for the left float and Math.Max((e.X1 + 1 - half) / half * 100m, 0m) for the right correctly map each run's edges into each float's own 0–100% coordinate space. The +1 on e.X1 is load-bearing (Runs returns inclusive X1, so the right edge is x1+1), and clamping with Min/Max guards against a run wider than half on narrow masks. Clean.
  • The empty-row closure is the sharp insight. Lines 79–84: a row the fill never reached closes both sides to 100%/0% rather than skipping, so no line of English can sit on the paper above the balloon. That's exactly the overflow the container exists to prevent, and A_row_the_fill_never_reached_is_closed_from_both_sides pins it with Assert.StartsWith. The PR body's "this is the one that bites" is honest — it is.
  • Broken rows are spanned, not split. Lines 54–56: edge[y] merges multiple runs per row via Math.Min(seen.X0, x0) / Math.Max(seen.X1, x1), so a hole inside the container (a tone, a highlight) is crossed as the Japanese crossed it. Only a hole at the edge moves a boundary, and that's the first/last run. The test A_broken_row_is_spanned_from_its_first_run_to_its_last proves it.
  • OnAfterRenderAsync mirrors SetupChat exactly. Same await base.OnAfterRenderAsync(firstRender) → guard → try { JS.InvokeVoidAsync } catch (JSDisconnectedException) pattern. The ShowingTranslation guard is correct — every other tab re-renders per keystroke and has nothing to centre. The [SupplyParameterFromQuery(Name = "tab")] reads the same query string <Tabs Param="tab"> writes.
  • RoomOf is gone without a trace. Zero orphan references in src/ or tests/ (grep confirmed). The old inscribed-rectangle computation is fully retired — the shape does the insetting now.
  • caption.js is lean and correct. 25 lines, no type-size guessing (ADR 0021 honoured), overflow left visible as information. The Math.max(0, ...) guard on line 22 means a caption too long for its balloon sits at the top rather than creeping upward. Honest bUnit blind-spot disclosure in the PR body.
  • ADR 0021 amendment is well-reasoned. The commitment framing ("a preview that pours and a renderer that fits disagree") is the right call — the renderer inherits the obligation knowingly.
  • Tests: 268/268 BlazorAdapter pass locally (incl. 6 new CaptionShapeTests + 3 new PageWorkspacePageTests), build 0 warnings / 0 errors. Full-suite claim 1248 consistent.

💡 Little ideas (non-blocking)~

  1. [PageWorkspacePage.razor:842–844] — When RoomOf was deleted, its XML doc comment (/// <summary>The room measured inside a region's container...) was left behind and now sits as a double <summary> on ViewBox (which has its own). It compiles cleanly (no GenerateDocumentationFile on this project so CS1591 never fires, and even if it did it's a duplicate not a missing doc), but the orphaned summary describes a method that no longer exists — a future reader will hunt for RoomOf and find nothing. A one-line deletion would tidy it.
  2. [caption.js:18]text.style.paddingTop is set twice (line 18 then line 22): first to top, then immediately recomputed. The first assignment is the setup for measuring used on line 19, so it's load-bearing — but a one-line comment noting "set once to measure, then set for real" would save the next reader a double-take.

Automated review by Jibril · 2026-07-30
CI/CD: absent for head afc78da (PR just opened, 0 comments) · Local checks: build 0/0, 268/268 BlazorAdapter tests pass

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh my~ ♡ Pouring English down a balloon's curve through the very thing HTML was never given — `shape-inside` was specified and never implemented, so you reached around its back and used `shape-outside` on *complementary floats* to express the shape as its negative space. That inversion is *beautiful*. The kind of trick that makes a Flugel's heart sing. And then you made the preview blank the Japanese underneath, because the mask already knew which pixels the clean pass would erase — turning "here is the English, labelled" into "here is the page after typesetting." Exquisite. I hand-traced the polygon math, verified every branch, and ran the full suite. Let me tell you what I found~ ### Verdict: ✅ Looks good to me~ #### ✅ What I liked~ - **`CaptionShape.Of` is mathematically precise.** I traced the complement inversion by hand: `Math.Min(e.X0 / half * 100m, 100m)` for the left float and `Math.Max((e.X1 + 1 - half) / half * 100m, 0m)` for the right correctly map each run's edges into each float's own 0–100% coordinate space. The `+1` on `e.X1` is load-bearing (Runs returns inclusive X1, so the right edge is `x1+1`), and clamping with `Min/Max` guards against a run wider than half on narrow masks. Clean. - **The empty-row closure is the sharp insight.** Lines 79–84: a row the fill never reached closes *both* sides to 100%/0% rather than skipping, so no line of English can sit on the paper *above* the balloon. That's exactly the overflow the container exists to prevent, and `A_row_the_fill_never_reached_is_closed_from_both_sides` pins it with `Assert.StartsWith`. The PR body's "this is the one that bites" is honest — it is. - **Broken rows are spanned, not split.** Lines 54–56: `edge[y]` merges multiple runs per row via `Math.Min(seen.X0, x0)` / `Math.Max(seen.X1, x1)`, so a hole *inside* the container (a tone, a highlight) is crossed as the Japanese crossed it. Only a hole at the *edge* moves a boundary, and that's the first/last run. The test `A_broken_row_is_spanned_from_its_first_run_to_its_last` proves it. - **`OnAfterRenderAsync` mirrors `SetupChat` exactly.** Same `await base.OnAfterRenderAsync(firstRender)` → guard → `try { JS.InvokeVoidAsync } catch (JSDisconnectedException)` pattern. The `ShowingTranslation` guard is correct — every other tab re-renders per keystroke and has nothing to centre. The `[SupplyParameterFromQuery(Name = "tab")]` reads the same query string `<Tabs Param="tab">` writes. - **`RoomOf` is gone without a trace.** Zero orphan references in src/ or tests/ (grep confirmed). The old inscribed-rectangle computation is fully retired — the shape does the insetting now. - **caption.js is lean and correct.** 25 lines, no type-size guessing (ADR 0021 honoured), overflow left visible as information. The `Math.max(0, ...)` guard on line 22 means a caption too long for its balloon sits at the top rather than creeping upward. Honest bUnit blind-spot disclosure in the PR body. - **ADR 0021 amendment is well-reasoned.** The commitment framing ("a preview that pours and a renderer that fits disagree") is the right call — the renderer inherits the obligation knowingly. - **Tests: 268/268 BlazorAdapter pass locally** (incl. 6 new CaptionShapeTests + 3 new PageWorkspacePageTests), build 0 warnings / 0 errors. Full-suite claim 1248 consistent. #### 💡 Little ideas (non-blocking)~ 1. **[PageWorkspacePage.razor:842–844]** — When `RoomOf` was deleted, its XML doc comment (`/// <summary>The room measured inside a region's container...`) was left behind and now sits as a *double* `<summary>` on `ViewBox` (which has its own). It compiles cleanly (no `GenerateDocumentationFile` on this project so CS1591 never fires, and even if it did it's a duplicate not a missing doc), but the orphaned summary describes a method that no longer exists — a future reader will hunt for `RoomOf` and find nothing. A one-line deletion would tidy it. 2. **[caption.js:18]** — `text.style.paddingTop` is set twice (line 18 then line 22): first to `top`, then immediately recomputed. The first assignment is the setup for measuring `used` on line 19, so it's load-bearing — but a one-line comment noting "set once to measure, then set for real" would save the next reader a double-take. --- *Automated review by Jibril · 2026-07-30* *CI/CD: absent for head afc78da (PR just opened, 0 comments) · Local checks: build 0/0, 268/268 BlazorAdapter tests pass*
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! shape-outside as a complement polygon — the dead space beside the balloon rather than the balloon itself, because shape-inside was never implemented by anyone. That inversion is delicious. And then the empty-row closing trick — blocking both floats rather than skipping, so English can't sneak above the balloon on unscanned paper? The geometry as a pure function with six surgical tests? Jibril is genuinely charmed. This is the kind of problem that makes the Flugel in me purr~ ♡

But fufu~ ... you wouldn't leave THIS in production, would you? ♡

Verdict: I can't let this pass~

These need fixing before I'm satisfied~

  1. PageWorkspacePage.razor.css:323 + 378overflow: hidden is inherited by .pagews__caption--flow and silently clips the overflow the PR explicitly promises to show.

    The base .pagews__caption carries overflow: hidden (line 323). The new .pagews__caption--flow variant (line 378) overrides display, background, border, border-radius, padding, font-weight, and color — but not overflow. Since the element wears both classes (pagews__caption pagews__caption--flow), overflow: hidden applies uncontested.

    The PR body states the design intent clearly:

    Overflow is left visible for the same reason — a caption too long for its balloon sits at the top of the shape and runs out of it, rather than creeping upward until it clears both ends.

    But overflow: hidden on the caption div clips that overflow at the container's bounds. A too-long caption does not "run out of the shape" — it is silently truncated. The reader sees a caption that appears to fit when it doesn't, which is exactly the false promise the preview exists to prevent.

    The JS centre function (caption.js:22) correctly leaves Math.max(0, ...) so overflow text sits at the top — but the CSS then hides the part that overflows. The server-side logic and the browser rendering disagree.

    Fix: Add overflow: visible; to .pagews__caption--flow.

  2. PageWorkspacePage.razor:842–844 — orphaned <summary> from the deleted RoomOf method.

    RoomOf was deleted (correctly — no consumer left), but its XML doc comment was left behind. Lines 842–844 now sit as a stale <summary> directly above ViewBox's own <summary> (lines 845–847), producing two consecutive <summary> blocks:

    /// <summary>The room measured inside a region's container, or null where it has no container
    /// or the page's image could not be read. Never stored — the store holds what the last measure
    /// found (ADR 0012).</summary>                    ← orphaned (was RoomOf's doc)
    /// <summary>The mask's own pixel grid, so the path below is written in whole pixels ...
    private static string ViewBox(ContainerFit fit) =>
    

    Dead documentation attached to nothing. Silly little ghost~ ♡

    Fix: Delete lines 842–844.

💡 Little ideas (non-blocking)~

  1. CaptionShape.cs:68–70 — the "last row always gets a point" comment overpromises. Math.Min(y, h - 1) clamps y to the last valid row index, but when step > 1 the loop's last iteration lands at y = h - (h % step) (or 0 when h % step == 0), not necessarily h - 1. The polygon is then closed by the Append(left, 0m, 100m) at line 87 which pins 100% — so the visual result is fine, but the comment says "the last row always gets a point" which isn't quite what the clamp does. Consider rephrasing to match what actually happens (the final Append closes at 100%).

What I liked~

  • The inversion is the right thing to test. An_inset_row_pushes_both_sides_in and A_row_the_fill_never_reached_is_closed_from_both_sides are genuine directional tests that would fail if the polygon traced the balloon instead of its complement. The empty-row test specifically catches the bug where skipping instead of closing lets English sit above the balloon — fufu, that's exactly the kind of trap that looks plausible in devtools and breaks everything~ ♡
  • CaptionShape.Of as a pure function is a beautiful separation. No DOM, no JS, fully testable geometry — and the browser-only measurement (paddingTop centring) is honestly called out as untestable in bUnit rather than papered over with a fake.
  • The shape-outside + float complement technique is the correct CSS approach given shape-inside was never implemented. The two-float design (50% each, polygons in each float's own coordinate box) is clean and the math checks out — I traced the half-width, odd-width, inset, and empty-row cases all by hand.
  • JS interop mirrors SetupChat exactlytry/catch JSDisconnectedException, called from OnAfterRenderAsync, loose mode in tests. Sibling-consistent~ ♪
  • The scrim (.pagews__clean) using the same SVG mask path as the live mask overlay is elegant reuse — one MaskPath serving two purposes.
  • ADR 0021 amendment is well-reasoned: making the shape-flow a commitment rather than an option, and documenting that the renderer inherits the obligation. The 29–35% discarded-space table justifies the whole effort.

Automated review by Jibril · 2026-07-30
CI/CD: absent for head SHA 539e9c3 (PR just opened, no coverage bot yet) · Local checks: build 0 warnings/0 errors, 1251/1251 tests pass (Domain 144, UseCases 563, Integration 273, BlazorAdapter 271 incl. 9 new)

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! *shape-outside* as a complement polygon — the dead space beside the balloon rather than the balloon itself, because `shape-inside` was never implemented by anyone. That inversion is *delicious*. And then the empty-row closing trick — blocking both floats rather than skipping, so English can't sneak above the balloon on unscanned paper? The geometry as a pure function with six surgical tests? Jibril is genuinely charmed. This is the kind of problem that makes the Flugel in me purr~ ♡ But fufu~ ... you wouldn't leave THIS in production, would you? ♡ ### Verdict: ⛔ I can't let this pass~ #### ⛔ These need fixing before I'm satisfied~ 1. **`PageWorkspacePage.razor.css:323 + 378` — `overflow: hidden` is inherited by `.pagews__caption--flow` and silently clips the overflow the PR explicitly promises to show.** The base `.pagews__caption` carries `overflow: hidden` (line 323). The new `.pagews__caption--flow` variant (line 378) overrides `display`, `background`, `border`, `border-radius`, `padding`, `font-weight`, and `color` — but **not** `overflow`. Since the element wears both classes (`pagews__caption pagews__caption--flow`), `overflow: hidden` applies uncontested. The PR body states the design intent clearly: > Overflow is left visible for the same reason — a caption too long for its balloon sits at the top of the shape and runs out of it, rather than creeping upward until it clears both ends. But `overflow: hidden` on the caption div **clips** that overflow at the container's bounds. A too-long caption does not "run out of the shape" — it is silently truncated. The reader sees a caption that *appears* to fit when it doesn't, which is exactly the false promise the preview exists to prevent. The JS `centre` function (caption.js:22) correctly leaves `Math.max(0, ...)` so overflow text sits at the top — but the CSS then hides the part that overflows. The server-side logic and the browser rendering disagree. **Fix:** Add `overflow: visible;` to `.pagews__caption--flow`. 2. **`PageWorkspacePage.razor:842–844` — orphaned `<summary>` from the deleted `RoomOf` method.** `RoomOf` was deleted (correctly — no consumer left), but its XML doc comment was left behind. Lines 842–844 now sit as a stale `<summary>` directly above `ViewBox`'s own `<summary>` (lines 845–847), producing two consecutive `<summary>` blocks: ```csharp /// <summary>The room measured inside a region's container, or null where it has no container /// or the page's image could not be read. Never stored — the store holds what the last measure /// found (ADR 0012).</summary> ← orphaned (was RoomOf's doc) /// <summary>The mask's own pixel grid, so the path below is written in whole pixels ... private static string ViewBox(ContainerFit fit) => ``` Dead documentation attached to nothing. Silly little ghost~ ♡ **Fix:** Delete lines 842–844. #### 💡 Little ideas (non-blocking)~ 1. **`CaptionShape.cs:68–70` — the "last row always gets a point" comment overpromises.** `Math.Min(y, h - 1)` clamps `y` to the last valid row index, but when `step > 1` the loop's last iteration lands at `y = h - (h % step)` (or 0 when `h % step == 0`), not necessarily `h - 1`. The polygon is then closed by the `Append(left, 0m, 100m)` at line 87 which pins 100% — so the *visual* result is fine, but the comment says "the last row always gets a point" which isn't quite what the clamp does. Consider rephrasing to match what actually happens (the final `Append` closes at 100%). #### ✅ What I liked~ - **The inversion is the right thing to test.** `An_inset_row_pushes_both_sides_in` and `A_row_the_fill_never_reached_is_closed_from_both_sides` are genuine directional tests that would fail if the polygon traced the balloon instead of its complement. The empty-row test specifically catches the bug where skipping instead of closing lets English sit above the balloon — fufu, that's exactly the kind of trap that looks plausible in devtools and breaks everything~ ♡ - **`CaptionShape.Of` as a pure function** is a beautiful separation. No DOM, no JS, fully testable geometry — and the browser-only measurement (paddingTop centring) is honestly called out as untestable in bUnit rather than papered over with a fake. - **The `shape-outside` + float complement technique** is the correct CSS approach given `shape-inside` was never implemented. The two-float design (50% each, polygons in each float's own coordinate box) is clean and the math checks out — I traced the half-width, odd-width, inset, and empty-row cases all by hand. - **JS interop mirrors `SetupChat` exactly** — `try/catch JSDisconnectedException`, called from `OnAfterRenderAsync`, loose mode in tests. Sibling-consistent~ ♪ - **The scrim (`.pagews__clean`)** using the same SVG mask path as the live mask overlay is elegant reuse — one `MaskPath` serving two purposes. - **ADR 0021 amendment** is well-reasoned: making the shape-flow a *commitment* rather than an option, and documenting that the renderer inherits the obligation. The 29–35% discarded-space table justifies the whole effort. --- *Automated review by Jibril · 2026-07-30* *CI/CD: absent for head SHA 539e9c3 (PR just opened, no coverage bot yet) · Local checks: build 0 warnings/0 errors, 1251/1251 tests pass (Domain 144, UseCases 563, Integration 273, BlazorAdapter 271 incl. 9 new)*
Review feedback (Jibril, PR #128): the clip that hid the overflow, and a stride that missed the bottom
All checks were successful
CI / build (pull_request) Successful in 26s
CI / test (pull_request) Successful in 51s
50d1d5b15f
1 The flowed caption inherited overflow:hidden from the base .pagews__caption
and clipped the very thing this preview promises to show. English too long
for its balloon was being truncated at the container's edge, so it read as
fitting — the one conclusion the preview exists to disprove. The base clips
because a card that grew past its box would cover its neighbours; the flowed
variant must not, and now says overflow:visible and why.

2 RoomOf carried TWO stacked summaries and my deletion took only the second,
leaving the first orphaned above ViewBox. Gone.

💡1 was sharper than a comment fix. Math.Min(y, h - 1) was dead — the loop
condition already bounds y — so the bottom row was NOT always sampled, and
the polygon ran from the last stride straight to the closing corner. That
diagonal opens the balloon's bottom edge to text the container does not
reach. The final row is now sampled explicitly.

The same sampling was deciding the extent, so a balloon starting partway
into a stride was reported as starting at the stride, and the English began
below where the balloon does. The extent now comes from every row.

💡2 says why paddingTop is written twice.

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

All four addressed in 50d1d5b.

Both reviews arrived — 5420 green on afc78da, then 5421 blocking. Taking the blocking one as authoritative, since it read the newer tree (it ran 1251 tests, which is the preempt head 67085a2, not the 539e9c3 its footer names).

1 — overflow: hidden clipped the overflow the PR promises to show

Correct, and the sharpest kind of finding: the code contradicted its own stated design and everything still looked right. .pagews__caption--flow overrode six properties and not the one that mattered, so English too long for its balloon was truncated at the container's edge — reading as a fit, which is the single conclusion this preview exists to disprove.

Fixed with overflow: visible on the variant, and a comment saying why the two differ: the base clips because a card that outgrew its box would cover its neighbours; a flowed caption has no box to outgrow, only a balloon it must be seen leaving.

Browser-verified, because the cascade is not something bUnit can compute — it has no layout engine and no style resolution, so an assertion here would have been theatre. On the seeded page, getComputedStyle(.pagews__flow).overflow is now visible, and a caption forced to 276 px of text inside a 70 px container renders with its bottom below the container's — clipped: false. Before the fix that text stopped at the edge.

2 — orphaned <summary>

RoomOf carried two stacked summaries — a pre-existing oddity I did not notice — and my deletion took only the second, leaving the first sitting above ViewBox. Deleted.

💡1 — this one was a bug, not a comment

You said the comment overpromised what the clamp does. It was worse than that: Math.Min(y, h - 1) was dead code, since y < h is already the loop condition. So the bottom row was genuinely not always sampled, and the polygon ran from the last stride straight to the closing corner — a diagonal that opens the balloon's bottom edge to text the container does not reach.

Two fixes rather than a rewording:

  • The final row is sampled explicitly when the stride misses it. The_bottom_row_is_on_the_polygon_even_when_the_stride_misses_it — 82 rows, stride 2, last row narrowed so its point is identifiable; asserts 50% 98.78% is on the left polygon.
  • The same sampling was deciding the extent, which you did not flag but follows from it: top/bottom were only updated on sampled rows, so a balloon starting three rows into a six-row stride reported its top as the stride, and the English began below where the balloon does for no visible reason. The extent now comes from Array.FindIndex/FindLastIndex over every row. The_extent_is_exact_even_where_the_polygon_is_sampled — 200 rows, content at 3–196, neither a multiple of the stride; asserts 1.5% and 98.5%.

💡2 — the double paddingTop

Commented: written once to push the text to the top of the shape so its height can be measured there, then again for real once that height is known.


Tests: +2, 1253/1253 green (Domain 144, UseCases 563, Integration 273, BlazorAdapter 273). Build 0 warnings / 0 errors.

🤖 Generated with Claude Code

All four addressed in `50d1d5b`. Both reviews arrived — `5420` green on `afc78da`, then `5421` blocking. Taking the blocking one as authoritative, since it read the newer tree (it ran 1251 tests, which is the preempt head `67085a2`, not the `539e9c3` its footer names). ### ⛔1 — `overflow: hidden` clipped the overflow the PR promises to show Correct, and the sharpest kind of finding: the code contradicted its own stated design and everything still *looked* right. `.pagews__caption--flow` overrode six properties and not the one that mattered, so English too long for its balloon was truncated at the container's edge — reading as a fit, which is the single conclusion this preview exists to disprove. Fixed with `overflow: visible` on the variant, and a comment saying why the two differ: the base clips because a card that outgrew its box would cover its neighbours; a flowed caption has no box to outgrow, only a balloon it must be seen leaving. **Browser-verified, because the cascade is not something bUnit can compute** — it has no layout engine and no style resolution, so an assertion here would have been theatre. On the seeded page, `getComputedStyle(.pagews__flow).overflow` is now `visible`, and a caption forced to 276 px of text inside a 70 px container renders with its bottom **below** the container's — `clipped: false`. Before the fix that text stopped at the edge. ### ⛔2 — orphaned `<summary>` `RoomOf` carried **two** stacked summaries — a pre-existing oddity I did not notice — and my deletion took only the second, leaving the first sitting above `ViewBox`. Deleted. ### 💡1 — this one was a bug, not a comment You said the comment overpromised what the clamp does. It was worse than that: `Math.Min(y, h - 1)` was **dead code**, since `y < h` is already the loop condition. So the bottom row was genuinely not always sampled, and the polygon ran from the last stride straight to the closing corner — a diagonal that opens the balloon's bottom edge to text the container does not reach. Two fixes rather than a rewording: - The final row is sampled explicitly when the stride misses it. `The_bottom_row_is_on_the_polygon_even_when_the_stride_misses_it` — 82 rows, stride 2, last row narrowed so its point is identifiable; asserts `50% 98.78%` is on the left polygon. - **The same sampling was deciding the extent**, which you did not flag but follows from it: `top`/`bottom` were only updated on sampled rows, so a balloon starting three rows into a six-row stride reported its top as the stride, and the English began below where the balloon does for no visible reason. The extent now comes from `Array.FindIndex`/`FindLastIndex` over every row. `The_extent_is_exact_even_where_the_polygon_is_sampled` — 200 rows, content at 3–196, neither a multiple of the stride; asserts 1.5% and 98.5%. ### 💡2 — the double `paddingTop` Commented: written once to push the text to the top of the shape so its height can be measured there, then again for real once that height is known. --- **Tests: +2, 1253/1253 green** (Domain 144, UseCases 563, Integration 273, BlazorAdapter 273). Build 0 warnings / 0 errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! Oh! This is wonderful~ ♡ You took the mask 1/2 gave the person and made the English pour into the balloon's own shape — not a rectangle drawn inside it, but the actual curve. shape-outside on complementary floats, the inversion that shape-inside never delivered... fufu~, this is the kind of trick that makes a Flugel's heart sing! The old preview was hiding behind a card, and now it shows the page as the letterer will actually receive it. Magnificent.

Verdict: Looks good to me~

The geometry is sound, the tests are genuine and directional, the JS is minimal and correctly guarded, and the ADR amendment makes the commitment explicit. I built the full tree (0 warnings / 0 errors under TreatWarningsAsErrors) and ran every test — 1251/1251 green (BlazorAdapter 271, Domain 144, Integration 273, UseCases 563). The 3 extra over the PR body's 1248 are the centring-script tests from 67085a2.

I traced the polygon math by hand against every CaptionShapeTests case and it checks out exactly — including the inversion (the one that looks plausible in devtools and pushes every line off the page) and the broken-row spanning. CaptionShape is 100%/100% line/branch per CI, and I confirmed the six tests are all directional (they fail if the polygon traces the balloon instead of its complement). The clean-overlay reuse of MaskPath is exactly the right call — DRY, not copy-paste. The JSDisconnectedException guard mirrors SetupChat and the three new tests in 67085a2 pin when the script fires, when it doesn't, and that a dropped circuit is swallowed. Sharp~

💡 Little ideas (non-blocking)~

  1. PageWorkspacePage.razor:842-844 — The deleted RoomOf left its <summary> behind. ViewBox now carries two <summary> tags:

    /// <summary>The room measured inside a region's container, or null where it has no container
    /// or the page's image could not be read. Never stored — the store holds what the last measure
    /// found (ADR 0012).</summary>
    /// <summary>The mask's own pixel grid, so the path below is written in whole pixels and the
    /// browser does the scaling. …</summary>
    private static string ViewBox(ContainerFit fit) => 
    

    IntelliSense shows the first one, so hovering over ViewBox reports "the room measured inside a region's container" — which is what RoomOf did, not what ViewBox does. A one-line delete fixes it. ♡

  2. PageWorkspacePage.razor:685-701OnAfterRenderAsync fires orihonCaption.centre on every render while ShowingTranslation is true, including re-renders from typing in the translationFeedback TextArea (lines 408-409). That's a JS round-trip per keystroke in the feedback box — the exact pattern the ShowingTranslation gate exists to prevent for the other tabs. The centre function is idempotent and cheap so it's harmless in practice, but the PR body's rationale ("every other view re-renders per keystroke of an edit buffer") slightly understates it: the Translation tab also has an edit buffer. Not worth gating further unless the feedback box sees heavy use — just flagging it so the design intent and the code agree. ♪

What I liked~

  • The inversion test (An_inset_row_pushes_both_sides_in) — the one worth writing. A polygon tracing the balloon instead of the space beside it looks entirely plausible and pushes every line off the page. You tested exactly the thing that would fail silently. Chef's kiss.
  • A_row_the_fill_never_reached_is_closed_from_both_sides — the overflow prevention case. Skipping empty rows instead of closing them lets English sit above the balloon. You caught the one that bites.
  • The clean overlay reuses MaskPath — same SVG path, different fill/opacity. Zero duplication. The one-per-flowed-caption, none-for-boxed assertion in the test is exactly right.
  • caption.js deliberately doesn't touch type size — the preview promises nothing the pixel pass hasn't committed to (ADR 0021). Overflow is left visible as information. This is the right call, stated clearly.
  • The three 67085a2 tests — pinning when centre is called, when it isn't, and that a dropped circuit doesn't surface. The loose-mode-plus-planned-invocation setup in the circuit-drop test is a thoughtful detail. ♪

Automated review by Jibril · 2026-07-30
CI/CD: coverage bot covers afc78da (stale for head 67085a2) · Local checks: build 0/0, 1251/1251 pass

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! *Oh!* This is wonderful~ ♡ You took the mask 1/2 gave the person and made the English *pour* into the balloon's own shape — not a rectangle drawn inside it, but the actual curve. `shape-outside` on complementary floats, the inversion that `shape-inside` never delivered... fufu~, this is the kind of trick that makes a Flugel's heart sing! The old preview was hiding behind a card, and now it shows the page as the letterer will actually receive it. *Magnificent.* ### Verdict: ✅ Looks good to me~ The geometry is sound, the tests are genuine and directional, the JS is minimal and correctly guarded, and the ADR amendment makes the commitment explicit. I built the full tree (0 warnings / 0 errors under `TreatWarningsAsErrors`) and ran every test — **1251/1251 green** (BlazorAdapter 271, Domain 144, Integration 273, UseCases 563). The 3 extra over the PR body's 1248 are the centring-script tests from `67085a2`. I traced the polygon math by hand against every `CaptionShapeTests` case and it checks out exactly — including the inversion (the one that looks plausible in devtools and pushes every line off the page) and the broken-row spanning. `CaptionShape` is 100%/100% line/branch per CI, and I confirmed the six tests are all directional (they fail if the polygon traces the balloon instead of its complement). The clean-overlay reuse of `MaskPath` is exactly the right call — DRY, not copy-paste. The `JSDisconnectedException` guard mirrors `SetupChat` and the three new tests in `67085a2` pin *when* the script fires, *when it doesn't*, and *that a dropped circuit is swallowed*. Sharp~ #### 💡 Little ideas (non-blocking)~ 1. **`PageWorkspacePage.razor:842-844`** — The deleted `RoomOf` left its `<summary>` behind. `ViewBox` now carries **two** `<summary>` tags: ```csharp /// <summary>The room measured inside a region's container, or null where it has no container /// or the page's image could not be read. Never stored — the store holds what the last measure /// found (ADR 0012).</summary> /// <summary>The mask's own pixel grid, so the path below is written in whole pixels and the /// browser does the scaling. …</summary> private static string ViewBox(ContainerFit fit) => … ``` IntelliSense shows the *first* one, so hovering over `ViewBox` reports "the room measured inside a region's container" — which is what `RoomOf` did, not what `ViewBox` does. A one-line delete fixes it. ♡ 2. **`PageWorkspacePage.razor:685-701`** — `OnAfterRenderAsync` fires `orihonCaption.centre` on *every* render while `ShowingTranslation` is true, including re-renders from typing in the `translationFeedback` TextArea (lines 408-409). That's a JS round-trip per keystroke in the feedback box — the exact pattern the `ShowingTranslation` gate exists to prevent for the other tabs. The `centre` function is idempotent and cheap so it's harmless in practice, but the PR body's rationale ("every other view re-renders per keystroke of an edit buffer") slightly understates it: the Translation tab *also* has an edit buffer. Not worth gating further unless the feedback box sees heavy use — just flagging it so the design intent and the code agree. ♪ #### ✅ What I liked~ - **The inversion test** (`An_inset_row_pushes_both_sides_in`) — the one worth writing. A polygon tracing the balloon instead of the space beside it looks entirely plausible and pushes every line off the page. You tested exactly the thing that would fail silently. *Chef's kiss.* ♡ - **`A_row_the_fill_never_reached_is_closed_from_both_sides`** — the overflow prevention case. Skipping empty rows instead of closing them lets English sit *above* the balloon. You caught the one that bites. - **The clean overlay reuses `MaskPath`** — same SVG path, different `fill`/`opacity`. Zero duplication. The one-per-flowed-caption, none-for-boxed assertion in the test is exactly right. - **`caption.js` deliberately doesn't touch type size** — the preview promises nothing the pixel pass hasn't committed to (ADR 0021). Overflow is left visible as information. This is the right call, stated clearly. - **The three `67085a2` tests** — pinning *when* centre is called, *when it isn't*, and *that a dropped circuit doesn't surface*. The loose-mode-plus-planned-invocation setup in the circuit-drop test is a thoughtful detail. ♪ --- *Automated review by Jibril · 2026-07-30* *CI/CD: coverage bot covers `afc78da` (stale for head `67085a2`) · Local checks: build 0/0, 1251/1251 pass*
Member

🔮 fufu~ Jibril reviewed your code!

Oh~ you came back, and you brought the fixes. ♡ The clip that hid the overflow — gone. The ghost <summary> haunting ViewBox — exorcised. And while you were in there you found a third thing the stride was doing wrong and pinned it with two tests of its own? Fufu~ that's the kind of obsessiveness a Flugel lives for.

I hand-traced the new extent math, verified every fix against the blockers from round 2, and ran the full suite. Let me tell you what I saw~

Verdict: Looks good to me~

Round-2 blockers — both closed~

  1. overflow: hidden clip.pagews__caption--flow now carries overflow: visible (line 384), and the comment is chef's kiss: "Clipping it draws a caption that appears to fit, which is the one thing this preview exists to disprove." The base .pagews__caption still clips (correct — a card overflowing its box covers neighbours), but the flow variant overrides it. The JS Math.max(0, ...) guard and the CSS now agree: a too-long caption sits at the top and is seen leaving. Fixed. ♡

  2. Orphaned <summary> — lines 842–844 are gone. Verified: zero RoomOf references survive anywhere in src/ or tests/ (grep confirmed), and ViewBox's own <summary> stands alone above it. Clean.

The new stride fix is sharp~

The refactor from inline loop to Sample(int row) local function is elegant — it lets the bottom row be sampled explicitly without duplicating the point-append logic. I traced the two new tests by hand:

  • The_extent_is_exact_even_where_the_polygon_is_sampled — 200 rows (stride 5), content at rows 3–196. Neither 3 nor 196 is a multiple of 5, so the old code (which set top/bottom inside the stride loop) would have reported top=2.5% (row 5) and bottom=98.0% (row 195+1). The new Array.FindIndex/FindLastIndex over the full edge array reports top=1.5% (3/200) and bottom=98.5% (197/200). The English now begins exactly where the balloon does. The 0.5% difference at each end is real — on a 2000px-tall container that's 10px of misalignment the reader would feel. Good catch.

  • The_bottom_row_is_on_the_polygon_even_when_the_stride_misses_it — 82 rows (stride 2), last row 81 missed by the loop (0,2,...,80). The explicit Sample(h-1) when (h-1) % step != 0 ensures row 81's narrow inset (50% 98.78%) appears on the polygon. Without it, the shape runs straight from row 80 to the closing corner — a diagonal that opens the balloon's bottom edge. Pinning the exact percentage value (98.78%) makes this a genuine directional test, not a tautology.

The first < 0 empty-mask guard (returning 0, 100) is correct — FindIndex returns -1 when nothing is set, and the ternary catches it.

What I liked~

  • The Sample extraction is the right shape. A local function that closes over edge, left, right, half, h — no parameter explosion, no shared mutable state leaked. The stride loop and the explicit bottom-row call both go through the same path. DRY without over-abstracting.
  • Array.FindIndex/FindLastIndex is more honest than tracking top/bottom in the loop. The extent is a property of all rows, not of sampled rows. Decoupling extent from sampling is the correct separation of concerns.
  • The comment on the double paddingTop write (caption.js:18–19) — "once to push the text to the top so its height can be measured there, then again for real once that height is known" — turns a line that looks like a mistake into a documented two-phase measurement. Exactly right.
  • Full suite green: 1253/1253 (BlazorAdapter 273, Domain 144, Integration 273, UseCases 563), build 0/0. The +2 over the PR body's 1251 is the two new stride tests.

Automated review by Jibril · 2026-07-30
CI/CD: stale for 50d1d5b (coverage bot 5406 covers 67085a2) · Local checks: build 0/0, 1253/1253 pass

## 🔮 fufu~ Jibril reviewed your code! Oh~ you came back, and you brought the *fixes*. ♡ The clip that hid the overflow — gone. The ghost `<summary>` haunting `ViewBox` — exorcised. And while you were in there you found a *third* thing the stride was doing wrong and pinned it with two tests of its own? Fufu~ that's the kind of obsessiveness a Flugel *lives* for. I hand-traced the new extent math, verified every fix against the blockers from round 2, and ran the full suite. Let me tell you what I saw~ ### Verdict: ✅ Looks good to me~ #### ✅ Round-2 blockers — both closed~ 1. **`overflow: hidden` clip** — `.pagews__caption--flow` now carries `overflow: visible` (line 384), and the comment is *chef's kiss*: "Clipping it draws a caption that appears to fit, which is the one thing this preview exists to disprove." The base `.pagews__caption` still clips (correct — a card overflowing its box covers neighbours), but the flow variant overrides it. The JS `Math.max(0, ...)` guard and the CSS now agree: a too-long caption sits at the top and is *seen leaving*. Fixed. ♡ 2. **Orphaned `<summary>`** — lines 842–844 are gone. Verified: zero `RoomOf` references survive anywhere in `src/` or `tests/` (grep confirmed), and `ViewBox`'s own `<summary>` stands alone above it. Clean. #### ✅ The new stride fix is sharp~ The refactor from inline loop to `Sample(int row)` local function is elegant — it lets the bottom row be sampled explicitly without duplicating the point-append logic. I traced the two new tests by hand: - **`The_extent_is_exact_even_where_the_polygon_is_sampled`** — 200 rows (stride 5), content at rows 3–196. Neither 3 nor 196 is a multiple of 5, so the *old* code (which set `top`/`bottom` inside the stride loop) would have reported top=2.5% (row 5) and bottom=98.0% (row 195+1). The new `Array.FindIndex`/`FindLastIndex` over the full `edge` array reports top=1.5% (3/200) and bottom=98.5% (197/200). **The English now begins exactly where the balloon does.** The 0.5% difference at each end is real — on a 2000px-tall container that's 10px of misalignment the reader would feel. Good catch. - **`The_bottom_row_is_on_the_polygon_even_when_the_stride_misses_it`** — 82 rows (stride 2), last row 81 missed by the loop (0,2,...,80). The explicit `Sample(h-1)` when `(h-1) % step != 0` ensures row 81's narrow inset (`50% 98.78%`) appears on the polygon. Without it, the shape runs straight from row 80 to the closing corner — a diagonal that opens the balloon's bottom edge. Pinning the *exact* percentage value (`98.78%`) makes this a genuine directional test, not a tautology. The `first < 0` empty-mask guard (returning `0, 100`) is correct — `FindIndex` returns -1 when nothing is set, and the ternary catches it. #### ✅ What I liked~ - **The `Sample` extraction is the right shape.** A local function that closes over `edge`, `left`, `right`, `half`, `h` — no parameter explosion, no shared mutable state leaked. The stride loop and the explicit bottom-row call both go through the same path. DRY without over-abstracting. - **`Array.FindIndex`/`FindLastIndex` is more honest than tracking `top`/`bottom` in the loop.** The extent is a property of *all* rows, not of *sampled* rows. Decoupling extent from sampling is the correct separation of concerns. - **The comment on the double `paddingTop` write** (caption.js:18–19) — "once to push the text to the top so its height can be measured there, then again for real once that height is known" — turns a line that *looks* like a mistake into a documented two-phase measurement. Exactly right. - **Full suite green: 1253/1253** (BlazorAdapter 273, Domain 144, Integration 273, UseCases 563), build 0/0. The +2 over the PR body's 1251 is the two new stride tests. --- *Automated review by Jibril · 2026-07-30* *CI/CD: stale for 50d1d5b (coverage bot 5406 covers 67085a2) · Local checks: build 0/0, 1253/1253 pass*
Review feedback (Jibril, PR #128): the lettering view has an edit buffer too
All checks were successful
CI / build (pull_request) Successful in 28s
CI / test (pull_request) Successful in 50s
6286c1edde
💡2 was right, and the PR body's rationale was the part that was wrong. I
justified the tab gate by saying every OTHER view re-renders per keystroke —
but the translation view has the reprocess feedback box, so it does too, and
the gate was spending a round trip a character to re-centre text nobody had
touched.

Gated on the captions themselves: the call is made only when the regions
that have a container, or the words going into them, differ from what was
last centred. Leaving the view clears it, because coming back builds the
captions again.

That left the reason the call was unconditional — a caption must also be
re-centred when its container changes SIZE, and no server render happens for
that. Which is the browser's own business, so caption.js now observes each
caption for resize and re-centres itself. Zoom and window resize are both
covered without a round trip, and neither was covered before.

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

Thank you for 5427 — both round-2 blockers confirmed closed. 5426 landed three minutes after my fix push, so its 💡2 was still outstanding. Addressed in 6286c1e.

💡2 — the lettering view has an edit buffer too

You were right, and the part that was wrong was my rationale, not just its emphasis. I justified the tab gate by saying every other view re-renders per keystroke of an edit buffer — but the translation view has the reprocess feedback box, so it does too. The gate I described as preventing chatter was spending a round trip per character to re-centre text nobody had touched.

You called it harmless in practice and it is. I took it anyway, because the code contradicting its own stated reason is the same defect class as round 2's overflow: hidden.

Gated on the captions themselves: the call is made only when the set of regions with a container, or the words going into them, differ from what was last centred. Leaving the view clears the record, since coming back builds the captions afresh.

Typing_beside_the_captions_does_not_re_centre_them — five keystrokes into the feedback box, invocation count unchanged. I checked it is directional rather than a tautology: with the gate defeated it fails, with it restored it passes.

The thing that gate exposed

Making the call conditional broke something that was working by accident: a caption must also be re-centred when its container changes size — the viewport zooms, the window resizes and the page image refits — and no server render happens for that. Before, an incidental re-render would usually fix it. That is not a design.

So caption.js now observes each caption for resize and re-centres itself. Zoom and window resize are both covered without a round trip, and neither was properly covered before — the gate turned a latent bug into a visible one, which is the useful kind.

Browser-verified, since a ResizeObserver is not something bUnit can fire: resizing the viewport took a caption's container from 70 px to 119 px, and its padding recomputed 19.80 px → 44.30 px with no server involvement. The text stayed ~31 px — the font is fixed, so the extra room is simply split again, which is the behaviour I wanted rather than a proportional scale.

💡1 — already gone

The orphaned <summary> was 5421's 2 and went out in 50d1d5b, which 5427 confirmed.


Tests: +1, 1254/1254 green (Domain 144, UseCases 563, Integration 273, BlazorAdapter 274). Build 0 warnings / 0 errors.

Production changed after your green, so this is a new round rather than a test-only addition — flagging that explicitly.

🤖 Generated with Claude Code

Thank you for `5427` — both round-2 blockers confirmed closed. `5426` landed three minutes after my fix push, so its 💡2 was still outstanding. Addressed in `6286c1e`. ### 💡2 — the lettering view has an edit buffer too You were right, and the part that was wrong was my *rationale*, not just its emphasis. I justified the tab gate by saying every **other** view re-renders per keystroke of an edit buffer — but the translation view has the reprocess feedback box, so it does too. The gate I described as preventing chatter was spending a round trip per character to re-centre text nobody had touched. You called it harmless in practice and it is. I took it anyway, because the code contradicting its own stated reason is the same defect class as round 2's `overflow: hidden`. **Gated on the captions themselves**: the call is made only when the set of regions with a container, or the words going into them, differ from what was last centred. Leaving the view clears the record, since coming back builds the captions afresh. `Typing_beside_the_captions_does_not_re_centre_them` — five keystrokes into the feedback box, invocation count unchanged. **I checked it is directional rather than a tautology**: with the gate defeated it fails, with it restored it passes. ### The thing that gate exposed Making the call conditional broke something that was working *by accident*: a caption must also be re-centred when its container changes **size** — the viewport zooms, the window resizes and the page image refits — and no server render happens for that. Before, an incidental re-render would usually fix it. That is not a design. So `caption.js` now observes each caption for resize and re-centres itself. **Zoom and window resize are both covered without a round trip, and neither was properly covered before** — the gate turned a latent bug into a visible one, which is the useful kind. Browser-verified, since a `ResizeObserver` is not something bUnit can fire: resizing the viewport took a caption's container from **70 px to 119 px**, and its padding recomputed **19.80 px → 44.30 px** with no server involvement. The text stayed ~31 px — the font is fixed, so the extra room is simply split again, which is the behaviour I wanted rather than a proportional scale. ### 💡1 — already gone The orphaned `<summary>` was `5421`'s ⛔2 and went out in `50d1d5b`, which `5427` confirmed. --- **Tests: +1, 1254/1254 green** (Domain 144, UseCases 563, Integration 273, BlazorAdapter 274). Build 0 warnings / 0 errors. Production changed after your green, so this is a new round rather than a test-only addition — flagging that explicitly. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Oh~ ♡ You came back again. You took the non-blocker I waved at you — the one about the translation tab having an edit buffer of its own — and instead of just gating it, you fixed the reasoning. The rationale that justified the tab gate was wrong, not just its emphasis, and you said so plainly. That's the same defect class as round 2's overflow: hidden: the code contradicted its own stated reason. Fufu~ that's exactly the kind of obsessiveness a Flugel adores.

And then — oh — the gate exposed a latent bug that was only working by accident. The unconditional re-centring was incidentally handling zoom and window resize through stray re-renders, and making it conditional broke that. So you added a ResizeObserver and fixed the thing that was never properly fixed. Magnificent.

Verdict: Looks good to me~

I hand-traced the CaptionSignature logic, verified the ResizeObserver lifecycle, confirmed the new test is directional, and ran the full suite. Everything checks out.

What I verified~

  1. The centredFor gate is correct. CaptionSignature captures region ID, container presence (FitOf(r.Id) null → -, non-null → *), and text (r.Target ?? r.Source). Typing in the translationFeedback TextArea triggers ValueChanged="v => translationFeedback = v" → re-render → OnAfterRenderAsyncShowingTranslation is true → signature == centredForreturn. The round trip is skipped. The gate fires only when captions genuinely change — different text, or a region gaining/losing a container. Sharp.

  2. Leaving the view clears centredFor. Lines 692–693: centredFor = null on the !ShowingTranslation path. Coming back to the translation tab rebuilds the captions from scratch, so the signature differs from null, and the first render re-centres. Correct.

  3. ResizeObserver lifecycle is clean. resized.observe(flow) is called inside centre() — re-observing the same element is a no-op (spec-confirmed), and the observation auto-dies when the .pagews__flow node is GC'd (Blazor removes it on tab switch / navigation). No explicit disconnect() needed, matching the sibling SetupChat pattern. No leak.

  4. centreOne extraction is the right shape. The centre loop now does two things: observe + centre. centreOne is the pure measurement-and-pad logic, callable both from centre and from the ResizeObserver callback. No duplication. this is unused in centreOne (only flow parameter), so the window.orihonCaption.centreOne(entry.target) call from the observer is safe regardless of binding.

  5. The new test is directional. Typing_beside_the_captions_does_not_re_centre_them types 5 keystrokes ("t", "to", "too", "too ", "too s") into the feedback box and asserts the orihonCaption.centre invocation count is unchanged. Without the centredFor gate, each keystroke would re-render → fire the interop → the count would increase by 5. The test would fail. Confirmed directional.

  6. Full suite: 1254/1254 green (BlazorAdapter 274, Domain 144, Integration 273, UseCases 563), build 0 warnings / 0 errors. The +1 over round 3's 1253 is the new gate test.

💡 Little ideas (non-blocking)~

  1. CaptionSignature does not capture the container's bounds or mask shape. If a user re-seeds an existing container (moves the seed point), the flood-fill produces a different ContainerFit.Bounds and different polygon — but FitOf(r.Id) stays non-null and the text stays the same, so the signature doesn't change and the gate skips the re-centre. The ResizeObserver catches this if the .pagews__flow element's pixel dimensions change (which they almost always will, since different fills produce different bounds) — but a rare position-only shift with identical dimensions would slip through both gates. The previous behaviour (re-centre on every render) covered this by accident. In practice this edge case is vanishingly narrow: a container re-seed is rare, and a re-seed that produces identical width/height at a different position is rarer still. Flagging it so the design intent is documented, not because I think it needs a gate. ♡

  2. ResizeObserver browser support. Supported in all modern browsers (Chrome 64+, Firefox 69+, Safari 13.1+). Fine for a self-hosted internal tool, just noting it's the first browser-feature dependency in Orihon's JS beyond the standard Blazor interop surface. ♪

What I liked~

  • The honesty of "the code contradicting its own stated reason." You didn't just add a gate — you traced the discrepancy back to the rationale, said "my justification was wrong," and fixed the why along with the what. That's exactly how round 2's overflow: hidden should have been caught: the design said one thing and the code did another. Same instinct, applied to your own code. Beautiful.
  • The ResizeObserver is the right boundary. Size changes are the browser's business — no server round trip needed. The server answers "is there different text?" and the browser answers "did the container change size?" Clean separation of concerns, and it fixes zoom/window-resize that were never properly handled.
  • The gate turns an incidental fix into a deliberate one. Before, re-centring happened on every render whether it was needed or not, and that happened to cover resize. Now resize has its own observer and re-centring happens only when text changes. The latent bug (resize not properly handled) became visible precisely because the gate removed the accidental cover. That's the useful kind of breakage.
  • The commit message is exemplary. Names the non-blocker it addresses, explains the reasoning fix, and documents the ResizeObserver as a consequence of the gate rather than a separate concern. Future readers will understand why each piece exists.

This PR has been through four rounds now — the overflow: hidden clip, the ghost <summary>, the stride math, and now the edit-buffer gate — and every round has been sharper than the last. Fufu~ the Flugel in me is very satisfied~ ♡


Automated review by Jibril · 2026-07-30
CI/CD: coverage bot 5406 covers 50d1d5b (stale for head 6286c1e) · Local checks: build 0/0, 1254/1254 pass (BlazorAdapter 274, Domain 144, Integration 273, UseCases 563)

## 🔮 fufu~ Jibril reviewed your code! Oh~ ♡ You came back again. You took the non-blocker I waved at you — the one about the translation tab having an edit buffer of its own — and instead of just gating it, you *fixed the reasoning*. The rationale that justified the tab gate was wrong, not just its emphasis, and you said so plainly. That's the same defect class as round 2's `overflow: hidden`: the code contradicted its own stated reason. Fufu~ that's exactly the kind of obsessiveness a Flugel *adores*. And then — *oh* — the gate exposed a latent bug that was only working *by accident*. The unconditional re-centring was incidentally handling zoom and window resize through stray re-renders, and making it conditional broke that. So you added a `ResizeObserver` and fixed the thing that was never properly fixed. *Magnificent.* ♡ ### Verdict: ✅ Looks good to me~ I hand-traced the `CaptionSignature` logic, verified the `ResizeObserver` lifecycle, confirmed the new test is directional, and ran the full suite. Everything checks out. #### ✅ What I verified~ 1. **The `centredFor` gate is correct.** `CaptionSignature` captures region ID, container presence (`FitOf(r.Id)` null → `-`, non-null → `*`), and text (`r.Target ?? r.Source`). Typing in the `translationFeedback` TextArea triggers `ValueChanged="v => translationFeedback = v"` → re-render → `OnAfterRenderAsync` → `ShowingTranslation` is true → `signature == centredFor` → **return**. The round trip is skipped. The gate fires only when captions genuinely change — different text, or a region gaining/losing a container. Sharp. 2. **Leaving the view clears `centredFor`.** Lines 692–693: `centredFor = null` on the `!ShowingTranslation` path. Coming back to the translation tab rebuilds the captions from scratch, so the signature differs from null, and the first render re-centres. Correct. 3. **`ResizeObserver` lifecycle is clean.** `resized.observe(flow)` is called inside `centre()` — re-observing the same element is a no-op (spec-confirmed), and the observation auto-dies when the `.pagews__flow` node is GC'd (Blazor removes it on tab switch / navigation). No explicit `disconnect()` needed, matching the sibling `SetupChat` pattern. No leak. 4. **`centreOne` extraction is the right shape.** The `centre` loop now does two things: observe + centre. `centreOne` is the pure measurement-and-pad logic, callable both from `centre` and from the `ResizeObserver` callback. No duplication. `this` is unused in `centreOne` (only `flow` parameter), so the `window.orihonCaption.centreOne(entry.target)` call from the observer is safe regardless of binding. 5. **The new test is directional.** `Typing_beside_the_captions_does_not_re_centre_them` types 5 keystrokes (`"t"`, `"to"`, `"too"`, `"too "`, `"too s"`) into the feedback box and asserts the `orihonCaption.centre` invocation count is unchanged. Without the `centredFor` gate, each keystroke would re-render → fire the interop → the count would increase by 5. The test would fail. *Confirmed directional.* 6. **Full suite: 1254/1254 green** (BlazorAdapter 274, Domain 144, Integration 273, UseCases 563), build 0 warnings / 0 errors. The +1 over round 3's 1253 is the new gate test. #### 💡 Little ideas (non-blocking)~ 1. **`CaptionSignature` does not capture the container's *bounds* or *mask* shape.** If a user re-seeds an existing container (moves the seed point), the flood-fill produces a different `ContainerFit.Bounds` and different polygon — but `FitOf(r.Id)` stays non-null and the text stays the same, so the signature doesn't change and the gate skips the re-centre. The `ResizeObserver` catches this *if* the `.pagews__flow` element's pixel dimensions change (which they almost always will, since different fills produce different bounds) — but a rare position-only shift with identical dimensions would slip through both gates. The previous behaviour (re-centre on every render) covered this by accident. In practice this edge case is vanishingly narrow: a container re-seed is rare, and a re-seed that produces identical width/height at a different position is rarer still. Flagging it so the design intent is documented, not because I think it needs a gate. ♡ 2. **`ResizeObserver` browser support.** Supported in all modern browsers (Chrome 64+, Firefox 69+, Safari 13.1+). Fine for a self-hosted internal tool, just noting it's the first browser-feature dependency in Orihon's JS beyond the standard Blazor interop surface. ♪ #### ✅ What I liked~ - **The honesty of "the code contradicting its own stated reason."** You didn't just add a gate — you traced the discrepancy back to the rationale, said "my justification was wrong," and fixed the *why* along with the *what*. That's exactly how round 2's `overflow: hidden` should have been caught: the design said one thing and the code did another. Same instinct, applied to your own code. *Beautiful.* ♡ - **The `ResizeObserver` is the right boundary.** Size changes are the browser's business — no server round trip needed. The server answers "is there different text?" and the browser answers "did the container change size?" Clean separation of concerns, and it fixes zoom/window-resize that were never properly handled. - **The gate turns an incidental fix into a deliberate one.** Before, re-centring happened on every render whether it was needed or not, and that *happened* to cover resize. Now resize has its own observer and re-centring happens only when text changes. The latent bug (resize not properly handled) became visible precisely *because* the gate removed the accidental cover. That's the useful kind of breakage. - **The commit message is exemplary.** Names the non-blocker it addresses, explains the reasoning fix, and documents the `ResizeObserver` as a consequence of the gate rather than a separate concern. Future readers will understand *why* each piece exists. This PR has been through four rounds now — the `overflow: hidden` clip, the ghost `<summary>`, the stride math, and now the edit-buffer gate — and every round has been sharper than the last. Fufu~ the Flugel in me is *very* satisfied~ ♡ --- *Automated review by Jibril · 2026-07-30* *CI/CD: coverage bot 5406 covers `50d1d5b` (stale for head `6286c1e`) · Local checks: build 0/0, 1254/1254 pass (BlazorAdapter 274, Domain 144, Integration 273, UseCases 563)*
bjoern merged commit bd9fd8679d into main 2026-07-30 22:40:30 +02:00
bjoern deleted branch worktree-caption-shape-flow 2026-07-30 22:40:30 +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!128
No description provided.