feat: the bible learns the story at a glance — overview summary + setting #45

Merged
bjoern merged 2 commits from feat/bible-story-overview into main 2026-08-13 06:23:57 +02:00
Member

The bible (ADR 0013) had the narrative skeleton (story beats) and the per-page anchors (page summaries), but no whole-book context: what the story is in two breaths, and where/when it plays — the register a translation leans on ("modern-day Japan" vs "isekai fantasy" changes every honorific decision). Both land as one singular per-project story overview record — the heir of doujin-translator's story.md prose — user-editable and agent-editable through the same write path. ADR 0013/0016, REQUIREMENTS §3, and the bible story are aligned in the same change.

What's in

  • Domain + persistence. StoryOverview (summary?, setting?, both blank→null via the domain's Text.BlankToNull), at most one per project enforced by a unique ProjectId index; AddStoryOverview migration; store find/add/update following the PageSummary shape.
  • Use cases. GetBible carries the overview (null until someone first writes it — an empty bible stays a valid state); SetStoryOverview is create-or-replace of the whole record: the editor's auto-save and the agents share one write path, so the "partial update silently resets the field it did not thread through" sharp edge (AGENTS.md) structurally cannot happen. Both-blank is a valid write (clears rather than errors — auto-save may flush mid-erase, the SetPageSummary lesson).
  • Agent tool (ADR 0016). set_story_overview(summary, setting) joins the Research & Setup grant — whole-record by design, the deliberate opposite of set_project_metadata's merge, and its description tells the model so. list_bible now leads with the overview; the blueprint's step 4 seeds it. Rejects the both-empty call as a miscall.
  • UI. An Overview tab now opens the bible page: setting (one line) and summary (textarea), riding the page's existing debounce/save-indicator machinery (ADR 0022). The overview's debounce is keyed by ProjectId — singular record, and the key is added to the live-set so SyncRows never discards its pending flush.
  • Seeder. The sample world fills the overview (setting + summary for the club romance), per the "keep the seed current" rule — through the real use case.
  • Docs. ADR 0013 gains the overview row and a whole-record decision bullet; ADR 0016's bible catalog line gains the tool; REQUIREMENTS §3, docs/stories/bible.md, and AGENTS.md's seed inventory updated.

Placement rationale (why bible, not project metadata): the overview is translation-consistency context the pipeline agents read, not bibliographic identity — it sits beside the beats it complements, and future agents get it through list_bible without widening the metadata surface.

Tests — 460 total (was 430), all green.

  • StoryOverviewUseCaseTests (+4): create-then-replace keeps one row per project (the strict fake throws on a second Add — same-id assert proves replace); blank fields normalize to null and each is independently optional, both-blank clears; a vanished project refuses; GetBible carries null before the first write and the overview after.
  • AgentToolTests (+1, +2 extended): the grant list now pins set_story_overview in the allow-list (least-privilege guard); the tool test proves whole-record semantics — a call omitting summary clears it, never keeps it — and that {} fails as a miscall; list_bible JSON now asserts the overview rides along.
  • BiblePageTests (+2, 5 adjusted): an overview edit auto-saves the whole record debounced and the untouched summary survives the flush; typing into an empty overview creates the row. The Overview tab is now first/default, so the five tests that relied on Glossary being default open their tab explicitly — behavior pinned, not weakened.
  • SeedDevDataTests: asserts the seeded overview has both fields.
  • AgentRunnerTests: the schema-ride test's tool count moves 10 → 11.

Honest notes

  • Setting is capped at 500 chars in the DB (a register hint, not an essay); the summary column is unbounded like the other freeform text fields.
  • Future agents (bible building, translation — ADR 0017) will get the overview through list_bible/their own grants when those agents land; this PR wires the only agent that exists.
  • Not browser-verified beyond bUnit: the page changes are a new tab of two stock inputs on the existing auto-save machinery, and the seed path is covered by the SQLite-backed contract tests (real migration, real store).

🤖 Generated with Claude Code

The bible (ADR 0013) had the narrative skeleton (story beats) and the per-page anchors (page summaries), but no whole-book context: what the story *is* in two breaths, and where/when it plays — the register a translation leans on ("modern-day Japan" vs "isekai fantasy" changes every honorific decision). Both land as one singular per-project **story overview** record — the heir of doujin-translator's `story.md` prose — user-editable and agent-editable through the same write path. ADR 0013/0016, REQUIREMENTS §3, and the bible story are aligned in the same change. **What's in** - *Domain + persistence.* `StoryOverview` (`summary?`, `setting?`, both blank→null via the domain's `Text.BlankToNull`), at most one per project enforced by a unique `ProjectId` index; `AddStoryOverview` migration; store find/add/update following the `PageSummary` shape. - *Use cases.* `GetBible` carries the overview (null until someone first writes it — an empty bible stays a valid state); `SetStoryOverview` is create-or-replace of the **whole record**: the editor's auto-save and the agents share one write path, so the "partial update silently resets the field it did not thread through" sharp edge (AGENTS.md) structurally cannot happen. Both-blank is a valid write (clears rather than errors — auto-save may flush mid-erase, the `SetPageSummary` lesson). - *Agent tool (ADR 0016).* `set_story_overview(summary, setting)` joins the Research & Setup grant — whole-record by design, the *deliberate opposite* of `set_project_metadata`'s merge, and its description tells the model so. `list_bible` now leads with the overview; the blueprint's step 4 seeds it. Rejects the both-empty call as a miscall. - *UI.* An **Overview** tab now opens the bible page: setting (one line) and summary (textarea), riding the page's existing debounce/save-indicator machinery (ADR 0022). The overview's debounce is keyed by `ProjectId` — singular record, and the key is added to the live-set so `SyncRows` never discards its pending flush. - *Seeder.* The sample world fills the overview (setting + summary for the club romance), per the "keep the seed current" rule — through the real use case. - *Docs.* ADR 0013 gains the overview row and a whole-record decision bullet; ADR 0016's bible catalog line gains the tool; REQUIREMENTS §3, `docs/stories/bible.md`, and AGENTS.md's seed inventory updated. **Placement rationale** (why bible, not project metadata): the overview is translation-consistency context the pipeline agents read, not bibliographic identity — it sits beside the beats it complements, and future agents get it through `list_bible` without widening the metadata surface. **Tests** — 460 total (was 430), all green. - `StoryOverviewUseCaseTests` (+4): create-then-replace keeps one row per project (the strict fake throws on a second Add — same-id assert proves replace); blank fields normalize to null and each is independently optional, both-blank clears; a vanished project refuses; `GetBible` carries null before the first write and the overview after. - `AgentToolTests` (+1, +2 extended): the grant list now pins `set_story_overview` in the allow-list (least-privilege guard); the tool test proves whole-record semantics — a call omitting `summary` *clears* it, never keeps it — and that `{}` fails as a miscall; `list_bible` JSON now asserts the overview rides along. - `BiblePageTests` (+2, 5 adjusted): an overview edit auto-saves the whole record debounced *and* the untouched summary survives the flush; typing into an empty overview creates the row. The Overview tab is now first/default, so the five tests that relied on Glossary being default open their tab explicitly — behavior pinned, not weakened. - `SeedDevDataTests`: asserts the seeded overview has both fields. - `AgentRunnerTests`: the schema-ride test's tool count moves 10 → 11. **Honest notes** - Setting is capped at 500 chars in the DB (a register hint, not an essay); the summary column is unbounded like the other freeform text fields. - Future agents (bible building, translation — ADR 0017) will get the overview through `list_bible`/their own grants when those agents land; this PR wires the only agent that exists. - Not browser-verified beyond bUnit: the page changes are a new tab of two stock inputs on the existing auto-save machinery, and the seed path is covered by the SQLite-backed contract tests (real migration, real store). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: the bible learns the story at a glance — overview summary + setting
All checks were successful
CI / build (pull_request) Successful in 25s
CI / test (pull_request) Successful in 38s
6202c4dd3c
The bible had the skeleton (beats) and the per-page anchors (summaries) but no
whole-book context: what the story IS in two breaths, and where/when it plays —
the register the translation leans on. Both arrive as one singular per-project
record, the heir of doujin-translator's story.md prose (ADR 0013):

- Domain/persistence: StoryOverview (summary?, setting?, both blank→null), at
  most one per project (unique ProjectId index), AddStoryOverview migration.
- Use cases: GetBible carries the overview (null before the first write);
  SetStoryOverview is create-or-replace of the whole record — auto-save and
  agents share one write path, so no partial update can silently drop a field.
- Agent tool: set_story_overview in the Research & Setup grant (whole-record by
  design, the deliberate opposite of set_project_metadata's merge); list_bible
  now leads with the overview; the system prompt seeds it in step 4.
- UI: an Overview tab opens the bible page — setting and summary, auto-saved
  (ADR 0022) with the page's shared debounce/indicator machinery.
- Seeder: the sample world fills the overview; SeedDevDataTests assert it.
- Docs aligned: ADR 0013 (table + whole-record decision), ADR 0016 (catalog),
  REQUIREMENTS §3, the bible story, AGENTS.md's seed inventory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Summary

Summary
Generated on: 07/26/2026 - 07:31:27
Coverage date: 07/26/2026 - 07:31:13 - 07/26/2026 - 07:31:24
Parser: MultiReport (4x Cobertura)
Assemblies: 6
Classes: 345
Files: 160
Line coverage: 93.9% (8398 of 8938)
Covered lines: 8398
Uncovered lines: 540
Coverable lines: 8938
Total lines: 16323
Branch coverage: 79.7% (1768 of 2216)
Covered branches: 1768
Total branches: 2216
Method coverage: Feature is only available for sponsors

Coverage

Orihon.BlazorAdapter - 95.4%
Name Line Branch
Orihon.BlazorAdapter 95.4% 87.8%
Orihon.BlazorAdapter.Bible.AddBeatRowRequested 100%
Orihon.BlazorAdapter.Bible.AddCharacterRowRequested 100%
Orihon.BlazorAdapter.Bible.AddGlossaryRowRequested 100%
Orihon.BlazorAdapter.Bible.AddLoreRowRequested 100%
Orihon.BlazorAdapter.Bible.BibleEffects 92.2% 79.1%
Orihon.BlazorAdapter.Bible.BibleLoaded 100%
Orihon.BlazorAdapter.Bible.BiblePage 93.7% 81.6%
Orihon.BlazorAdapter.Bible.BibleReducers 93.1%
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.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.UpdateLoreRowRequested 100%
Orihon.BlazorAdapter.BlazorAdapterAssembly 100%
Orihon.BlazorAdapter.Debounce 96.2% 94.4%
Orihon.BlazorAdapter.Diagnostics.CircuitError 100%
Orihon.BlazorAdapter.Diagnostics.CircuitErrorPanel 100%
Orihon.BlazorAdapter.Diagnostics.CircuitErrorSink 100% 85.7%
Orihon.BlazorAdapter.Diagnostics.OrihonStoreInitializer 85.7% 66.6%
Orihon.BlazorAdapter.PageWorkspace.CreateRegionRequested 100%
Orihon.BlazorAdapter.PageWorkspace.DeleteRegionRequested 100%
Orihon.BlazorAdapter.PageWorkspace.LoadPageWorkspace 100%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceEffects 100% 100%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceLoaded 100%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspacePage 88.3% 80.7%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceReducers 100% 75%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceState 100%
Orihon.BlazorAdapter.PageWorkspace.PageWriteFailed 100%
Orihon.BlazorAdapter.PageWorkspace.RegionCreated 100%
Orihon.BlazorAdapter.PageWorkspace.RegionSaved 100%
Orihon.BlazorAdapter.PageWorkspace.ReorderRegionsRequested 100%
Orihon.BlazorAdapter.PageWorkspace.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.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 100% 100%
Orihon.BlazorAdapter.Projects.ProjectWizardPage 94.1% 86.2%
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.WizardLoaded 100%
Orihon.BlazorAdapter.Projects.WizardWriteFailed 100%
Orihon.BlazorAdapter.Runs.MonitorRunLoaded 100%
Orihon.BlazorAdapter.Runs.RunChangedBridge 94.1% 91.6%
Orihon.BlazorAdapter.Runs.RunMonitor 100% 97.6%
Orihon.BlazorAdapter.Runs.RunMonitorEffects 100% 100%
Orihon.BlazorAdapter.Runs.RunMonitorReducers 100%
Orihon.BlazorAdapter.Runs.RunMonitorState 100%
Orihon.BlazorAdapter.Settings.AgentModelPicked 100%
Orihon.BlazorAdapter.Settings.AgentModelSaved 100%
Orihon.BlazorAdapter.Settings.AgentModelSaveFailed 100%
Orihon.BlazorAdapter.Settings.KeySaved 100%
Orihon.BlazorAdapter.Settings.KeySaveFailed 100%
Orihon.BlazorAdapter.Settings.ModelOptionsLoaded 100%
Orihon.BlazorAdapter.Settings.ModelOptionsUnavailable 100%
Orihon.BlazorAdapter.Settings.SaveKeyRequested 100%
Orihon.BlazorAdapter.Settings.SettingsEffects 100% 100%
Orihon.BlazorAdapter.Settings.SettingsLoaded 100%
Orihon.BlazorAdapter.Settings.SettingsPage 100% 90.4%
Orihon.BlazorAdapter.Settings.SettingsReducers 100%
Orihon.BlazorAdapter.Settings.SettingsState 100%
Orihon.BlazorAdapter.Uploads.UploadTransfer 96.5% 100%
Orihon.BlazorAdapter.Uploads.UploadTransferProgress 100% 100%
Orihon.BlazorAdapter.Uploads.UploadTransferResult 100%
Orihon.BlazorAdapter.Workspace.CreateChapterRequested 100%
Orihon.BlazorAdapter.Workspace.DeleteChapterRequested 100%
Orihon.BlazorAdapter.Workspace.DeletePageRequested 100%
Orihon.BlazorAdapter.Workspace.DeleteSummaryRequested 100%
Orihon.BlazorAdapter.Workspace.LoadProjectWorkspace 100%
Orihon.BlazorAdapter.Workspace.MovePageRequested 100%
Orihon.BlazorAdapter.Workspace.ProjectMetadataCard 95.2% 92.8%
Orihon.BlazorAdapter.Workspace.ProjectMetadataSaved 100%
Orihon.BlazorAdapter.Workspace.ProjectWorkspaceEffects 100% 100%
Orihon.BlazorAdapter.Workspace.ProjectWorkspaceLoaded 100%
Orihon.BlazorAdapter.Workspace.ProjectWorkspacePage 95.5% 87.6%
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.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.AgentDescriptor 100%
Orihon.Domain.Agents.AgentRoster 100% 100%
Orihon.Domain.Bible.Character 100% 100%
Orihon.Domain.Bible.GlossaryEntry 100% 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.Run 100%
Orihon.Domain.Settings.AppSetting 100%
Orihon.Domain.Text 100% 100%
Orihon.Domain.Translation.BoundingBox 100%
Orihon.Domain.Translation.Chapter 100%
Orihon.Domain.Translation.Page 100%
Orihon.Domain.Translation.Region 100% 100%
Orihon.Domain.Translation.RegionProfile 100%
Orihon.Infrastructure - 94.5%
Name Line Branch
Orihon.Infrastructure 94.5% 67.5%
Orihon.Infrastructure.Bible.EfBibleStore 94.4% 91.6%
Orihon.Infrastructure.DependencyInjection 100%
Orihon.Infrastructure.Gateways.AgentToolAdapter 100%
Orihon.Infrastructure.Gateways.AgentToolAdapter`1 100% 100%
Orihon.Infrastructure.Gateways.HttpWebPageFetcher 95.1% 83.3%
Orihon.Infrastructure.Gateways.OpenRouterLlmGateway 98% 83%
Orihon.Infrastructure.Gateways.SkiaPageImageRenderer 96.6% 86.1%
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.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.AddAppSettings 99.3%
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.OrihonDbContext 100%
Orihon.Infrastructure.Persistence.OrihonDbContextFactory 100%
Orihon.Infrastructure.Projects.EfProjectStore 100% 100%
Orihon.Infrastructure.Projects.FileSystemPageImageStore 100% 100%
Orihon.Infrastructure.Runs.EfRunStore 97% 50%
Orihon.Infrastructure.Settings.EfAppSettingsStore 100% 100%
Orihon.Infrastructure.Translation.EfChapterStore 100% 100%
Orihon.Infrastructure.Translation.EfPageStore 100% 100%
Orihon.Infrastructure.Translation.EfRegionStore 100% 100%
Orihon.Infrastructure.Translation.Ordering 100% 100%
System.Text.RegularExpressions.Generated 70.6% 53.3%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlankLines_4
77.9% 76.6%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlockBreaks_1
59% 42.5%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__SpaceRuns_3
89.4% 75%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__Tags_2
83.7% 62.5%
Orihon.Kernel - 90.9%
Name Line Branch
Orihon.Kernel 90.9% 75%
Orihon.Kernel.Err`1 100%
Orihon.Kernel.Ok`1 100%
Orihon.Kernel.Result`1 88.8% 75%
Orihon.Server - 93.4%
Name Line Branch
Orihon.Server 93.4% 68.4%
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.4% 85.7%
Orihon.UseCases - 90.4%
Name Line Branch
Orihon.UseCases 90.4% 82.4%
Orihon.UseCases.Agents.AgentBlueprint 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.AddRegionParams 100%
Orihon.UseCases.Agents.Annotation.AddRegionTool 76.9% 50%
Orihon.UseCases.Agents.Annotation.AnnotationBlueprints 100%
Orihon.UseCases.Agents.Annotation.BoundBoxParams 0%
Orihon.UseCases.Agents.Annotation.BoundContactSheetParams 0%
Orihon.UseCases.Agents.Annotation.BoundContactSheetTool 10.7% 0%
Orihon.UseCases.Agents.Annotation.BoundCropParams 0%
Orihon.UseCases.Agents.Annotation.BoundCropTool 42.8%
Orihon.UseCases.Agents.Annotation.BoundViewAnnotatedTool 15% 0%
Orihon.UseCases.Agents.Annotation.BoundViewPageTool 18.7% 0%
Orihon.UseCases.Agents.Annotation.BoundViewParams 0%
Orihon.UseCases.Agents.Annotation.BoundZoomParams 0%
Orihon.UseCases.Agents.Annotation.BoundZoomTool 37.5%
Orihon.UseCases.Agents.Annotation.DeleteBoundRegionTool 91.6% 100%
Orihon.UseCases.Agents.Annotation.DeleteRegionParams 100%
Orihon.UseCases.Agents.Annotation.DeleteRegionTool 85.7% 100%
Orihon.UseCases.Agents.Annotation.FindGlossaryParams 100%
Orihon.UseCases.Agents.Annotation.FindGlossaryTool 76.4% 62.5%
Orihon.UseCases.Agents.Annotation.ListRegionsTool 76.4% 60%
Orihon.UseCases.Agents.Annotation.MoveResizeBoundTool 27.2% 0%
Orihon.UseCases.Agents.Annotation.MoveResizeRegionParams 100%
Orihon.UseCases.Agents.Annotation.MoveResizeRegionTool 73.3% 50%
Orihon.UseCases.Agents.Annotation.QaReportSink 100%
Orihon.UseCases.Agents.Annotation.RegionAuthoringAccess 86.6% 53.8%
Orihon.UseCases.Agents.Annotation.RejectRegionParams 100%
Orihon.UseCases.Agents.Annotation.RejectRegionTool 85.7% 50%
Orihon.UseCases.Agents.Annotation.ReorderRegionParams 100%
Orihon.UseCases.Agents.Annotation.ReorderRegionTool 80% 60%
Orihon.UseCases.Agents.Annotation.ReportQaParams 100%
Orihon.UseCases.Agents.Annotation.ReportQaTool 82.3% 93.7%
Orihon.UseCases.Agents.Annotation.SetPageMetaParams 100%
Orihon.UseCases.Agents.Annotation.SetPageMetaTool 85.7% 75%
Orihon.UseCases.Agents.Annotation.SetRegionTypeParams 100%
Orihon.UseCases.Agents.Annotation.SetRegionTypeTool 85.7% 87.5%
Orihon.UseCases.Agents.Annotation.SetTranscriptionParams 100%
Orihon.UseCases.Agents.Annotation.SetTranscriptionTool 80% 100%
Orihon.UseCases.Agents.AssistantSpoke 100%
Orihon.UseCases.Agents.Inspection.ContactSheetParams 100%
Orihon.UseCases.Agents.Inspection.ContactSheetTool 82.1% 92.8%
Orihon.UseCases.Agents.Inspection.CropParams 100%
Orihon.UseCases.Agents.Inspection.CropTool 42.8%
Orihon.UseCases.Agents.Inspection.PageImageAccess 66.6% 62%
Orihon.UseCases.Agents.Inspection.ViewAnnotatedParams 100%
Orihon.UseCases.Agents.Inspection.ViewAnnotatedTool 76.1% 83.3%
Orihon.UseCases.Agents.Inspection.ZoomParams 100%
Orihon.UseCases.Agents.Inspection.ZoomTool 44.4%
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.PageByNumber 90% 87.5%
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.1% 90.9%
Orihon.UseCases.Agents.ResearchSetup.SetStoryOverviewParams 100%
Orihon.UseCases.Agents.ResearchSetup.SetStoryOverviewTool 100% 100%
Orihon.UseCases.Agents.ResearchSetup.UpsertCharacterParams 100%
Orihon.UseCases.Agents.ResearchSetup.UpsertCharacterTool 91.3% 66.6%
Orihon.UseCases.Agents.ResearchSetup.UpsertLoreParams 100%
Orihon.UseCases.Agents.ResearchSetup.UpsertLoreTool 91.3% 66.6%
Orihon.UseCases.Agents.ResearchSetup.ViewPageParams 100%
Orihon.UseCases.Agents.ResearchSetup.ViewPageTool 100% 100%
Orihon.UseCases.Agents.Setup.ResearchSetupExecutor 97.4% 88.2%
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.Bible.AddCharacter 100% 100%
Orihon.UseCases.Bible.AddGlossaryEntry 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.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.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.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.DependencyInjection 100%
Orihon.UseCases.Diagnostics.SeedDevData 99.2% 92.8%
Orihon.UseCases.Gateways.LabeledBox 100%
Orihon.UseCases.Gateways.LlmKeyInfo 100%
Orihon.UseCases.Gateways.LlmModel 100%
Orihon.UseCases.NextOrder 100%
Orihon.UseCases.Pages.DeletePage 100% 100%
Orihon.UseCases.Pages.GetPage 100% 100%
Orihon.UseCases.Pages.GetProjectWorkspace 100% 100%
Orihon.UseCases.Pages.ImportPages 100% 100%
Orihon.UseCases.Pages.ImportPagesResult 100%
Orihon.UseCases.Pages.MovePage 100% 92.8%
Orihon.UseCases.Pages.PageDetailDto 100%
Orihon.UseCases.Pages.PageDto 100%
Orihon.UseCases.Pages.PageUpload 100%
Orihon.UseCases.Pages.ProjectWorkspaceDto 100%
Orihon.UseCases.Pages.ReorderPages 100%
Orihon.UseCases.Pages.SetPageMeta 100% 100%
Orihon.UseCases.Pages.WorkspaceChapterDto 100%
Orihon.UseCases.Projects.CompleteProjectSetup 100% 93.7%
Orihon.UseCases.Projects.CreateProject 100% 100%
Orihon.UseCases.Projects.DeleteProject 100% 100%
Orihon.UseCases.Projects.GetProject 100% 100%
Orihon.UseCases.Projects.ListProjects 100%
Orihon.UseCases.Projects.ProjectDto 95.8%
Orihon.UseCases.Projects.StartSetupRun 100% 100%
Orihon.UseCases.Projects.StoredPageImage 100%
Orihon.UseCases.Projects.UpdateProjectMetadata 100% 100%
Orihon.UseCases.Regions.CreateRegion 100% 100%
Orihon.UseCases.Regions.DeleteRegion 100% 100%
Orihon.UseCases.Regions.RegionDto 97%
Orihon.UseCases.Regions.ReorderRegions 100%
Orihon.UseCases.Regions.UpdateRegion 100% 100%
Orihon.UseCases.Runs.ExecutionDto 92.3%
Orihon.UseCases.Runs.PlannedExecution 100%
Orihon.UseCases.Runs.RunDto 93.3% 90%
Orihon.UseCases.Runs.RunEngine 94.1% 86.6%
Orihon.UseCases.Runs.RunEngineOptions 100%
Orihon.UseCases.Runs.StageContext 62.5%
Orihon.UseCases.Runs.StageHaltedException 100%
Orihon.UseCases.Settings.AgentSettingDto 100% 100%
Orihon.UseCases.Settings.GetSettings 100% 100%
Orihon.UseCases.Settings.ListModelOptions 100% 100%
Orihon.UseCases.Settings.SaveAgentModel 100% 100%
Orihon.UseCases.Settings.SaveOpenRouterKey 100% 100%
Orihon.UseCases.Settings.SettingKeys 100% 100%
Orihon.UseCases.Settings.SettingsDto 100%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 07/26/2026 - 07:31:27 | | Coverage date: | 07/26/2026 - 07:31:13 - 07/26/2026 - 07:31:24 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 6 | | Classes: | 345 | | Files: | 160 | | **Line coverage:** | 93.9% (8398 of 8938) | | Covered lines: | 8398 | | Uncovered lines: | 540 | | Coverable lines: | 8938 | | Total lines: | 16323 | | **Branch coverage:** | 79.7% (1768 of 2216) | | Covered branches: | 1768 | | Total branches: | 2216 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Orihon.BlazorAdapter - 95.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.BlazorAdapter**|**95.4%**|**87.8%**| |Orihon.BlazorAdapter.Bible.AddBeatRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddCharacterRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddGlossaryRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddLoreRowRequested|100%|| |Orihon.BlazorAdapter.Bible.BibleEffects|92.2%|79.1%| |Orihon.BlazorAdapter.Bible.BibleLoaded|100%|| |Orihon.BlazorAdapter.Bible.BiblePage|93.7%|81.6%| |Orihon.BlazorAdapter.Bible.BibleReducers|93.1%|| |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.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.UpdateLoreRowRequested|100%|| |Orihon.BlazorAdapter.BlazorAdapterAssembly|100%|| |Orihon.BlazorAdapter.Debounce|96.2%|94.4%| |Orihon.BlazorAdapter.Diagnostics.CircuitError|100%|| |Orihon.BlazorAdapter.Diagnostics.CircuitErrorPanel|100%|| |Orihon.BlazorAdapter.Diagnostics.CircuitErrorSink|100%|85.7%| |Orihon.BlazorAdapter.Diagnostics.OrihonStoreInitializer|85.7%|66.6%| |Orihon.BlazorAdapter.PageWorkspace.CreateRegionRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.DeleteRegionRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.LoadPageWorkspace|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceEffects|100%|100%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceLoaded|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspacePage|88.3%|80.7%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceReducers|100%|75%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceState|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageWriteFailed|100%|| |Orihon.BlazorAdapter.PageWorkspace.RegionCreated|100%|| |Orihon.BlazorAdapter.PageWorkspace.RegionSaved|100%|| |Orihon.BlazorAdapter.PageWorkspace.ReorderRegionsRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.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.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|100%|100%| |Orihon.BlazorAdapter.Projects.ProjectWizardPage|94.1%|86.2%| |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.WizardLoaded|100%|| |Orihon.BlazorAdapter.Projects.WizardWriteFailed|100%|| |Orihon.BlazorAdapter.Runs.MonitorRunLoaded|100%|| |Orihon.BlazorAdapter.Runs.RunChangedBridge|94.1%|91.6%| |Orihon.BlazorAdapter.Runs.RunMonitor|100%|97.6%| |Orihon.BlazorAdapter.Runs.RunMonitorEffects|100%|100%| |Orihon.BlazorAdapter.Runs.RunMonitorReducers|100%|| |Orihon.BlazorAdapter.Runs.RunMonitorState|100%|| |Orihon.BlazorAdapter.Settings.AgentModelPicked|100%|| |Orihon.BlazorAdapter.Settings.AgentModelSaved|100%|| |Orihon.BlazorAdapter.Settings.AgentModelSaveFailed|100%|| |Orihon.BlazorAdapter.Settings.KeySaved|100%|| |Orihon.BlazorAdapter.Settings.KeySaveFailed|100%|| |Orihon.BlazorAdapter.Settings.ModelOptionsLoaded|100%|| |Orihon.BlazorAdapter.Settings.ModelOptionsUnavailable|100%|| |Orihon.BlazorAdapter.Settings.SaveKeyRequested|100%|| |Orihon.BlazorAdapter.Settings.SettingsEffects|100%|100%| |Orihon.BlazorAdapter.Settings.SettingsLoaded|100%|| |Orihon.BlazorAdapter.Settings.SettingsPage|100%|90.4%| |Orihon.BlazorAdapter.Settings.SettingsReducers|100%|| |Orihon.BlazorAdapter.Settings.SettingsState|100%|| |Orihon.BlazorAdapter.Uploads.UploadTransfer|96.5%|100%| |Orihon.BlazorAdapter.Uploads.UploadTransferProgress|100%|100%| |Orihon.BlazorAdapter.Uploads.UploadTransferResult|100%|| |Orihon.BlazorAdapter.Workspace.CreateChapterRequested|100%|| |Orihon.BlazorAdapter.Workspace.DeleteChapterRequested|100%|| |Orihon.BlazorAdapter.Workspace.DeletePageRequested|100%|| |Orihon.BlazorAdapter.Workspace.DeleteSummaryRequested|100%|| |Orihon.BlazorAdapter.Workspace.LoadProjectWorkspace|100%|| |Orihon.BlazorAdapter.Workspace.MovePageRequested|100%|| |Orihon.BlazorAdapter.Workspace.ProjectMetadataCard|95.2%|92.8%| |Orihon.BlazorAdapter.Workspace.ProjectMetadataSaved|100%|| |Orihon.BlazorAdapter.Workspace.ProjectWorkspaceEffects|100%|100%| |Orihon.BlazorAdapter.Workspace.ProjectWorkspaceLoaded|100%|| |Orihon.BlazorAdapter.Workspace.ProjectWorkspacePage|95.5%|87.6%| |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.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.AgentDescriptor|100%|| |Orihon.Domain.Agents.AgentRoster|100%|100%| |Orihon.Domain.Bible.Character|100%|100%| |Orihon.Domain.Bible.GlossaryEntry|100%|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.Run|100%|| |Orihon.Domain.Settings.AppSetting|100%|| |Orihon.Domain.Text|100%|100%| |Orihon.Domain.Translation.BoundingBox|100%|| |Orihon.Domain.Translation.Chapter|100%|| |Orihon.Domain.Translation.Page|100%|| |Orihon.Domain.Translation.Region|100%|100%| |Orihon.Domain.Translation.RegionProfile|100%|| </details> <details><summary>Orihon.Infrastructure - 94.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Infrastructure**|**94.5%**|**67.5%**| |Orihon.Infrastructure.Bible.EfBibleStore|94.4%|91.6%| |Orihon.Infrastructure.DependencyInjection|100%|| |Orihon.Infrastructure.Gateways.AgentToolAdapter|100%|| |Orihon.Infrastructure.Gateways.AgentToolAdapter`1|100%|100%| |Orihon.Infrastructure.Gateways.HttpWebPageFetcher|95.1%|83.3%| |Orihon.Infrastructure.Gateways.OpenRouterLlmGateway|98%|83%| |Orihon.Infrastructure.Gateways.SkiaPageImageRenderer|96.6%|86.1%| |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.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.AddAppSettings|99.3%|| |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.OrihonDbContext|100%|| |Orihon.Infrastructure.Persistence.OrihonDbContextFactory|100%|| |Orihon.Infrastructure.Projects.EfProjectStore|100%|100%| |Orihon.Infrastructure.Projects.FileSystemPageImageStore|100%|100%| |Orihon.Infrastructure.Runs.EfRunStore|97%|50%| |Orihon.Infrastructure.Settings.EfAppSettingsStore|100%|100%| |Orihon.Infrastructure.Translation.EfChapterStore|100%|100%| |Orihon.Infrastructure.Translation.EfPageStore|100%|100%| |Orihon.Infrastructure.Translation.EfRegionStore|100%|100%| |Orihon.Infrastructure.Translation.Ordering|100%|100%| |System.Text.RegularExpressions.Generated|70.6%|53.3%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030<br/>A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlankLines_4|77.9%|76.6%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030<br/>A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlockBreaks_1|59%|42.5%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030<br/>A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__SpaceRuns_3|89.4%|75%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F7FCA343D2B99030<br/>A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__Tags_2|83.7%|62.5%| </details> <details><summary>Orihon.Kernel - 90.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Kernel**|**90.9%**|**75%**| |Orihon.Kernel.Err`1|100%|| |Orihon.Kernel.Ok`1|100%|| |Orihon.Kernel.Result`1|88.8%|75%| </details> <details><summary>Orihon.Server - 93.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Server**|**93.4%**|**68.4%**| |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.4%|85.7%| </details> <details><summary>Orihon.UseCases - 90.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.UseCases**|**90.4%**|**82.4%**| |Orihon.UseCases.Agents.AgentBlueprint|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.AddRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.AddRegionTool|76.9%|50%| |Orihon.UseCases.Agents.Annotation.AnnotationBlueprints|100%|| |Orihon.UseCases.Agents.Annotation.BoundBoxParams|0%|| |Orihon.UseCases.Agents.Annotation.BoundContactSheetParams|0%|| |Orihon.UseCases.Agents.Annotation.BoundContactSheetTool|10.7%|0%| |Orihon.UseCases.Agents.Annotation.BoundCropParams|0%|| |Orihon.UseCases.Agents.Annotation.BoundCropTool|42.8%|| |Orihon.UseCases.Agents.Annotation.BoundViewAnnotatedTool|15%|0%| |Orihon.UseCases.Agents.Annotation.BoundViewPageTool|18.7%|0%| |Orihon.UseCases.Agents.Annotation.BoundViewParams|0%|| |Orihon.UseCases.Agents.Annotation.BoundZoomParams|0%|| |Orihon.UseCases.Agents.Annotation.BoundZoomTool|37.5%|| |Orihon.UseCases.Agents.Annotation.DeleteBoundRegionTool|91.6%|100%| |Orihon.UseCases.Agents.Annotation.DeleteRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.DeleteRegionTool|85.7%|100%| |Orihon.UseCases.Agents.Annotation.FindGlossaryParams|100%|| |Orihon.UseCases.Agents.Annotation.FindGlossaryTool|76.4%|62.5%| |Orihon.UseCases.Agents.Annotation.ListRegionsTool|76.4%|60%| |Orihon.UseCases.Agents.Annotation.MoveResizeBoundTool|27.2%|0%| |Orihon.UseCases.Agents.Annotation.MoveResizeRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.MoveResizeRegionTool|73.3%|50%| |Orihon.UseCases.Agents.Annotation.QaReportSink|100%|| |Orihon.UseCases.Agents.Annotation.RegionAuthoringAccess|86.6%|53.8%| |Orihon.UseCases.Agents.Annotation.RejectRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.RejectRegionTool|85.7%|50%| |Orihon.UseCases.Agents.Annotation.ReorderRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.ReorderRegionTool|80%|60%| |Orihon.UseCases.Agents.Annotation.ReportQaParams|100%|| |Orihon.UseCases.Agents.Annotation.ReportQaTool|82.3%|93.7%| |Orihon.UseCases.Agents.Annotation.SetPageMetaParams|100%|| |Orihon.UseCases.Agents.Annotation.SetPageMetaTool|85.7%|75%| |Orihon.UseCases.Agents.Annotation.SetRegionTypeParams|100%|| |Orihon.UseCases.Agents.Annotation.SetRegionTypeTool|85.7%|87.5%| |Orihon.UseCases.Agents.Annotation.SetTranscriptionParams|100%|| |Orihon.UseCases.Agents.Annotation.SetTranscriptionTool|80%|100%| |Orihon.UseCases.Agents.AssistantSpoke|100%|| |Orihon.UseCases.Agents.Inspection.ContactSheetParams|100%|| |Orihon.UseCases.Agents.Inspection.ContactSheetTool|82.1%|92.8%| |Orihon.UseCases.Agents.Inspection.CropParams|100%|| |Orihon.UseCases.Agents.Inspection.CropTool|42.8%|| |Orihon.UseCases.Agents.Inspection.PageImageAccess|66.6%|62%| |Orihon.UseCases.Agents.Inspection.ViewAnnotatedParams|100%|| |Orihon.UseCases.Agents.Inspection.ViewAnnotatedTool|76.1%|83.3%| |Orihon.UseCases.Agents.Inspection.ZoomParams|100%|| |Orihon.UseCases.Agents.Inspection.ZoomTool|44.4%|| |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.PageByNumber|90%|87.5%| |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.1%|90.9%| |Orihon.UseCases.Agents.ResearchSetup.SetStoryOverviewParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.SetStoryOverviewTool|100%|100%| |Orihon.UseCases.Agents.ResearchSetup.UpsertCharacterParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.UpsertCharacterTool|91.3%|66.6%| |Orihon.UseCases.Agents.ResearchSetup.UpsertLoreParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.UpsertLoreTool|91.3%|66.6%| |Orihon.UseCases.Agents.ResearchSetup.ViewPageParams|100%|| |Orihon.UseCases.Agents.ResearchSetup.ViewPageTool|100%|100%| |Orihon.UseCases.Agents.Setup.ResearchSetupExecutor|97.4%|88.2%| |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.Bible.AddCharacter|100%|100%| |Orihon.UseCases.Bible.AddGlossaryEntry|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.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.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.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.DependencyInjection|100%|| |Orihon.UseCases.Diagnostics.SeedDevData|99.2%|92.8%| |Orihon.UseCases.Gateways.LabeledBox|100%|| |Orihon.UseCases.Gateways.LlmKeyInfo|100%|| |Orihon.UseCases.Gateways.LlmModel|100%|| |Orihon.UseCases.NextOrder|100%|| |Orihon.UseCases.Pages.DeletePage|100%|100%| |Orihon.UseCases.Pages.GetPage|100%|100%| |Orihon.UseCases.Pages.GetProjectWorkspace|100%|100%| |Orihon.UseCases.Pages.ImportPages|100%|100%| |Orihon.UseCases.Pages.ImportPagesResult|100%|| |Orihon.UseCases.Pages.MovePage|100%|92.8%| |Orihon.UseCases.Pages.PageDetailDto|100%|| |Orihon.UseCases.Pages.PageDto|100%|| |Orihon.UseCases.Pages.PageUpload|100%|| |Orihon.UseCases.Pages.ProjectWorkspaceDto|100%|| |Orihon.UseCases.Pages.ReorderPages|100%|| |Orihon.UseCases.Pages.SetPageMeta|100%|100%| |Orihon.UseCases.Pages.WorkspaceChapterDto|100%|| |Orihon.UseCases.Projects.CompleteProjectSetup|100%|93.7%| |Orihon.UseCases.Projects.CreateProject|100%|100%| |Orihon.UseCases.Projects.DeleteProject|100%|100%| |Orihon.UseCases.Projects.GetProject|100%|100%| |Orihon.UseCases.Projects.ListProjects|100%|| |Orihon.UseCases.Projects.ProjectDto|95.8%|| |Orihon.UseCases.Projects.StartSetupRun|100%|100%| |Orihon.UseCases.Projects.StoredPageImage|100%|| |Orihon.UseCases.Projects.UpdateProjectMetadata|100%|100%| |Orihon.UseCases.Regions.CreateRegion|100%|100%| |Orihon.UseCases.Regions.DeleteRegion|100%|100%| |Orihon.UseCases.Regions.RegionDto|97%|| |Orihon.UseCases.Regions.ReorderRegions|100%|| |Orihon.UseCases.Regions.UpdateRegion|100%|100%| |Orihon.UseCases.Runs.ExecutionDto|92.3%|| |Orihon.UseCases.Runs.PlannedExecution|100%|| |Orihon.UseCases.Runs.RunDto|93.3%|90%| |Orihon.UseCases.Runs.RunEngine|94.1%|86.6%| |Orihon.UseCases.Runs.RunEngineOptions|100%|| |Orihon.UseCases.Runs.StageContext|62.5%|| |Orihon.UseCases.Runs.StageHaltedException|100%|| |Orihon.UseCases.Settings.AgentSettingDto|100%|100%| |Orihon.UseCases.Settings.GetSettings|100%|100%| |Orihon.UseCases.Settings.ListModelOptions|100%|100%| |Orihon.UseCases.Settings.SaveAgentModel|100%|100%| |Orihon.UseCases.Settings.SaveOpenRouterKey|100%|100%| |Orihon.UseCases.Settings.SettingKeys|100%|100%| |Orihon.UseCases.Settings.SettingsDto|100%|| </details>
bjoern force-pushed feat/bible-story-overview from 6202c4dd3c
All checks were successful
CI / build (pull_request) Successful in 25s
CI / test (pull_request) Successful in 38s
to 45131f8da0
All checks were successful
CI / build (pull_request) Successful in 23s
CI / test (pull_request) Successful in 40s
2026-07-26 09:18:04 +02:00
Compare
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh! The bible learns to breathe — a whole-book context anchor the per-page summaries hang from, and the register-setting hint every honorific decision leans on. Singular record, whole-record writes, one path for editor and agent alike so the partial-update sharp edge structurally cannot exist. That is elegant architecture, scarlet~ ♡ The Text.BlankToNull normalization, the unique ProjectId index enforcing "at most one," the debounce keyed by ProjectId and added to the live-set so SyncRows never discards its pending flush — every seam is thought through. I read all 30 files and traced every new branch against its sibling.

Verdict: I can't let this pass~ ♡

One defended code path has no test, and the sibling tells me it should.

These need fixing before I'm satisfied~

  1. [src/Orihon.UseCases/Bible/StoryOverviewUseCases.cs:23 + tests/Orihon.UseCases.Tests/FakeBibleStore.cs] — The TOCTOU arm of SetStoryOverview is unexercised. return updated is null ? Result<StoryOverviewDto>.Fail("The story overview no longer exists.") : ... defends against the row vanishing between FindStoryOverviewAsync and UpdateStoryOverviewAsync, but no test reaches the null arm. Coverage confirms it: SetStoryOverview sits at 83.3% branch while its sibling SetPageSummary is at 100% — and the sibling gets there deliberately, via a SimulateVanishingPageSummaries seam in FakeBibleStore (line 148) plus a dedicated test A_summary_vanishing_between_find_and_update_fails_instead_of_resurrecting (BibleUseCaseTests.cs:259). The new FakeBibleStore.UpdateStoryOverviewAsync has no equivalent SimulateVanishingStoryOverviews toggle, and StoryOverviewUseCaseTests has no equivalent test. fufu~ you wouldn't leave a code path you wrote a defense for with no test to prove it fires, would you? The sibling already drew the blueprint — port it faithfully. ♡
    Fix: add public bool SimulateVanishingStoryOverviews { get; set; } to FakeBibleStore, have UpdateStoryOverviewAsync return null when set, and add a test mirroring the sibling's — create the overview, flip the seam, call ExecuteAsync again, assert IsType<Err<StoryOverviewDto>>. That closes the 83.3% → 100% gap and matches the established pattern.

💡 Little ideas (non-blocking)~

  1. [src/Orihon.UseCases/Agents/ResearchSetup/BibleTools.cs:185]list_bible's new overview is null ? null : new { ... } ternary has its non-null arm pinned by List_bible_returns_the_whole_world_as_json (which seeds the overview first), but the null arm — an empty bible rendering overview: null in the JSON — has no tool-level assertion. GetBible_carries_the_overview_and_null_before_the_first_write pins the null contract at the use-case level (defensible division of labor, same reasoning scarlet used in PR #42), so this is a nicety not a gap. A one-line Assert.Equal(JsonValueKind.Null, json.RootElement.GetProperty("overview").ValueKind) in a list-bible-before-any-write test would pin the wire shape too, but feel free to leave it.

What I liked~

  • The whole-record semantics are a genuine architectural improvement: set_story_overview is the deliberate opposite of set_project_metadata's merge, and the description tells the model so. The "partial update silently resets the field it didn't thread through" sharp edge from AGENTS.md is now structurally impossible. That is how you kill a class of bugs — at the type level, not with a comment. ♪
  • StoryOverview is a textbook-faithful sibling of PageSummary: private setters, EF materialization constructor, Text.BlankToNull on both fields, Update replaces both at once. The Update doc-comment even cross-references ADR 0022 so future readers know why it's whole-record.
  • EF config matches siblings to the letter — HasOne<Project>().WithMany().HasForeignKey().OnDelete(Cascade), UtcTicksConverter on both timestamps, unique ProjectId index. The migration and both snapshot definitions (lines 178 + 555) are present and consistent. No FK or index drift.
  • set_story_overview rejecting {} as a miscall (not a clear) is exactly right — a model that calls with nothing was confused, not trying to erase. The both-blank-clears-but-only-with-a-real-field semantic is the SetPageSummary auto-save-mid-erase lesson, correctly generalized.
  • The UI test An_overview_edit_auto_saves_the_whole_record_debounced doesn't just assert the setting changed — it asserts the untouched summary survives the flush. That is the whole-record promise pinned at the behavior level, not just the type level. That is how you test an invariant.
  • Debounce keyed by ProjectId (singular record) and added to the live-set via .Concat([ProjectId]) with an honest comment — the SyncRows discard logic is respected for the new entity without special-casing. Clean.
  • The five Glossary-default tests now explicitly OpenTab(cut, "Glossary") — behavior pinned, not weakened, when Overview became the new default tab. Good hygiene.

Automated review by Jibril · 2026-07-26
CI/CD: passed for head 45131f8 (forgejo-actions coverage 4033, 95.4% line / 81.6% branch, 460 tests) · Local checks: skipped (CI green — new-path coverage verified against the bot comment: SetStoryOverview 83.3% branch confirms the untested TOCTOU arm)

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh! The bible learns to *breathe* — a whole-book context anchor the per-page summaries hang from, and the register-setting hint every honorific decision leans on. Singular record, whole-record writes, one path for editor and agent alike so the partial-update sharp edge *structurally cannot exist*. That is elegant architecture, scarlet~ ♡ The `Text.BlankToNull` normalization, the unique `ProjectId` index enforcing "at most one," the debounce keyed by `ProjectId` and added to the live-set so `SyncRows` never discards its pending flush — every seam is thought through. I read all 30 files and traced every new branch against its sibling. ### Verdict: ⛔ I can't let this pass~ ♡ One defended code path has no test, and the sibling tells me it should. #### ⛔ These need fixing before I'm satisfied~ 1. **[src/Orihon.UseCases/Bible/StoryOverviewUseCases.cs:23 + tests/Orihon.UseCases.Tests/FakeBibleStore.cs]** — The TOCTOU arm of `SetStoryOverview` is unexercised. `return updated is null ? Result<StoryOverviewDto>.Fail("The story overview no longer exists.") : ...` defends against the row vanishing between `FindStoryOverviewAsync` and `UpdateStoryOverviewAsync`, but no test reaches the `null` arm. Coverage confirms it: `SetStoryOverview` sits at **83.3% branch** while its sibling `SetPageSummary` is at **100%** — and the sibling gets there *deliberately*, via a `SimulateVanishingPageSummaries` seam in `FakeBibleStore` (line 148) plus a dedicated test `A_summary_vanishing_between_find_and_update_fails_instead_of_resurrecting` (BibleUseCaseTests.cs:259). The new `FakeBibleStore.UpdateStoryOverviewAsync` has no equivalent `SimulateVanishingStoryOverviews` toggle, and `StoryOverviewUseCaseTests` has no equivalent test. fufu~ you wouldn't leave a code path you *wrote a defense for* with no test to prove it fires, would you? The sibling already drew the blueprint — port it faithfully. ♡ Fix: add `public bool SimulateVanishingStoryOverviews { get; set; }` to `FakeBibleStore`, have `UpdateStoryOverviewAsync` return `null` when set, and add a test mirroring the sibling's — create the overview, flip the seam, call `ExecuteAsync` again, assert `IsType<Err<StoryOverviewDto>>`. That closes the 83.3% → 100% gap and matches the established pattern. #### 💡 Little ideas (non-blocking)~ 1. **[src/Orihon.UseCases/Agents/ResearchSetup/BibleTools.cs:185]** — `list_bible`'s new `overview is null ? null : new { ... }` ternary has its non-null arm pinned by `List_bible_returns_the_whole_world_as_json` (which seeds the overview first), but the *null* arm — an empty bible rendering `overview: null` in the JSON — has no tool-level assertion. `GetBible_carries_the_overview_and_null_before_the_first_write` pins the null contract at the use-case level (defensible division of labor, same reasoning scarlet used in PR #42), so this is a nicety not a gap. A one-line `Assert.Equal(JsonValueKind.Null, json.RootElement.GetProperty("overview").ValueKind)` in a list-bible-before-any-write test would pin the wire shape too, but feel free to leave it. #### ✅ What I liked~ - The **whole-record semantics** are a genuine architectural improvement: `set_story_overview` is the *deliberate opposite* of `set_project_metadata`'s merge, and the description tells the model so. The "partial update silently resets the field it didn't thread through" sharp edge from AGENTS.md is now structurally impossible. That is how you kill a class of bugs — at the type level, not with a comment. ♪ - `StoryOverview` is a textbook-faithful sibling of `PageSummary`: private setters, EF materialization constructor, `Text.BlankToNull` on both fields, `Update` replaces both at once. The `Update` doc-comment even cross-references ADR 0022 so future readers know *why* it's whole-record. - EF config matches siblings to the letter — `HasOne<Project>().WithMany().HasForeignKey().OnDelete(Cascade)`, `UtcTicksConverter` on both timestamps, unique `ProjectId` index. The migration and **both** snapshot definitions (lines 178 + 555) are present and consistent. No FK or index drift. - `set_story_overview` rejecting `{}` as a miscall (not a clear) is exactly right — a model that calls with nothing was confused, not trying to erase. The both-blank-clears-but-only-with-a-real-field semantic is the `SetPageSummary` auto-save-mid-erase lesson, correctly generalized. - The UI test `An_overview_edit_auto_saves_the_whole_record_debounced` doesn't just assert the setting changed — it asserts the **untouched summary survives the flush**. That is the whole-record promise pinned at the behavior level, not just the type level. *That* is how you test an invariant. - Debounce keyed by `ProjectId` (singular record) and added to the live-set via `.Concat([ProjectId])` with an honest comment — the `SyncRows` discard logic is respected for the new entity without special-casing. Clean. - The five Glossary-default tests now explicitly `OpenTab(cut, "Glossary")` — behavior pinned, not weakened, when Overview became the new default tab. Good hygiene. --- *Automated review by Jibril · 2026-07-26* *CI/CD: passed for head 45131f8 (forgejo-actions coverage 4033, 95.4% line / 81.6% branch, 460 tests) · Local checks: skipped (CI green — new-path coverage verified against the bot comment: SetStoryOverview 83.3% branch confirms the untested TOCTOU arm)*
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Ohhh~ ♡ The bible learns to speak in two breaths! A singular story overview — the heir of doujin-translator's story.md — slotted right beside the beats it complements, with the whole-record write shape that structurally cannot silently drop a field. This is elegant, scarlet. The "deliberate opposite of set_project_metadata's merge" framing is the kind of design decision I get giddy over — the tool description tells the model which semantics it's getting, so the LLM doesn't have to guess. Fufu~ ♪

I read the full diff, then the full contents of every changed production file, then traced every new branch against its test. Build 0/0, 460/460 pass locally (138 BlazorAdapter + 75 Domain + 89 Integration + 158 UseCases — matches your PR body exactly). CI is absent for head 6202c4d, so local verification was the source of truth.

Verdict: Looks good to me~

What I liked~

  • Faithful sibling pattern, end to end. StoryOverview mirrors PageSummary like a reflection: private EF ctor, public ctor with Text.BlankToNull, Update method, UtcTicksConverter on both timestamps, FK→Project with Cascade, unique ProjectId index. StoryOverviewConfiguration is a textbook copy of PageSummaryConfiguration. The migration matches the snapshot. Nothing invented, nothing fancy — and that's the highest praise a CRUD entity can earn~ ♡
  • The whole-record shape is load-bearing and proven to be. TouchOverview captures overview.Summary AND overview.Setting at flush time, and An_overview_edit_auto_saves_the_whole_record_debounced doesn't just assert the setting changed — it asserts the untouched summary survived the flush. That's the directional assertion that turns "it compiles" into "it cannot silently drop a field." The SetPageSummary lesson from AGENTS.md is genuinely internalized here.
  • The {} miscall rejection is tested. Set_story_overview_writes_the_whole_record_each_time ends with Tool("set_story_overview").InvokeAsync("{}", ...)Assert.False(empty.IsSuccess). The "nothing to record is a miscall, not a clear" boundary is pinned. Fufu~ you knew I'd look for the untested branch, didn't you? ♡
  • The debounce key is ProjectId and it's in the live-set. SyncRows does .Concat([ProjectId]) before building the live hash, so the overview's pending flush is never discarded as "gone." Singular record, singular key — correct.
  • Grant allow-list pinned, schema count pinned. The_grant_is_exactly_the_research_and_setup_allow_list now asserts set_story_overview between add_story_beat and set_page_summary; AgentRunnerTests tool count moves 10→11. The least-privilege guard is locked.
  • The five tab-default tests were adjusted, not weakened. OpenTab(cut, "Glossary") was added to every test that relied on Glossary being default — behavior pinned at the new default (Overview first), old behavior preserved by explicit open. That's the right way to handle a tab reordering.
  • Seeder goes through the real use case (setStoryOverview.ExecuteAsync), and SeedDevDataTests asserts both fields are non-blank. "Keep the seed current" honored.

💡 Little ideas (non-blocking)~

  1. SetStoryOverview concurrency window (FYI, not a bug). The use case does application-level find→update-or-add, same as SetPageSummary. For the page summary this is single-writer (one editor tab per page); the overview has two writers — the editor's auto-save and the agent's set_story_overview. If a flush races between FindStoryOverviewAsync returning null and AddAsync, both insert paths could reach the DB and the unique ProjectId index throws. This is correctly caught — the throw surfaces as Result.Fail and the editor's next keystroke retries, so no data corruption — but it's worth knowing the DB unique constraint is the real guard here, not the use case. No change needed; the design holds. ♪

  2. list_bible overview shape drops Id. The agent-facing JSON serializes new { bible.Overview.Summary, bible.Overview.Setting } — no Id. Every other section also drops its ids (glossary/characters/lore/beats all project flat fields), so this is consistent — but if a future agent ever needs to reference "the overview" by id (unlikely, it's singular), the shape would need widening. Purely additive someday; not today.

This is a clean, well-tested, architecturally faithful PR. The whole-record semantics are the star of the show — the "partial update silently resets the field it didn't thread through" sharp edge is structurally impossible here, and the tests prove it. Approved~ ♡


Automated review by Jibril · 2026-07-26
CI/CD: absent for head SHA 6202c4d · Local checks: build 0/0, 460/460 tests pass

## 🔮 fufu~ Jibril reviewed your code! Oh? Ohhh~ ♡ The bible learns to speak in two breaths! A singular story overview — the heir of doujin-translator's `story.md` — slotted right beside the beats it complements, with the whole-record write shape that *structurally* cannot silently drop a field. This is *elegant*, scarlet. The "deliberate opposite of `set_project_metadata`'s merge" framing is the kind of design decision I get giddy over — the tool description *tells the model* which semantics it's getting, so the LLM doesn't have to guess. Fufu~ ♪ I read the full diff, then the full contents of every changed production file, then traced every new branch against its test. Build 0/0, **460/460 pass locally** (138 BlazorAdapter + 75 Domain + 89 Integration + 158 UseCases — matches your PR body exactly). CI is absent for head `6202c4d`, so local verification was the source of truth. ### Verdict: ✅ Looks good to me~ #### ✅ What I liked~ - **Faithful sibling pattern, end to end.** `StoryOverview` mirrors `PageSummary` like a reflection: private EF ctor, public ctor with `Text.BlankToNull`, `Update` method, `UtcTicksConverter` on both timestamps, FK→Project with `Cascade`, unique `ProjectId` index. `StoryOverviewConfiguration` is a textbook copy of `PageSummaryConfiguration`. The migration matches the snapshot. Nothing invented, nothing fancy — and that's the highest praise a CRUD entity can earn~ ♡ - **The whole-record shape is load-bearing and *proven* to be.** `TouchOverview` captures `overview.Summary` AND `overview.Setting` at flush time, and `An_overview_edit_auto_saves_the_whole_record_debounced` doesn't just assert the setting changed — it asserts the *untouched summary survived the flush*. That's the directional assertion that turns "it compiles" into "it cannot silently drop a field." The `SetPageSummary` lesson from AGENTS.md is genuinely internalized here. - **The `{}` miscall rejection is tested.** `Set_story_overview_writes_the_whole_record_each_time` ends with `Tool("set_story_overview").InvokeAsync("{}", ...)` → `Assert.False(empty.IsSuccess)`. The "nothing to record is a miscall, not a clear" boundary is pinned. Fufu~ you knew I'd look for the untested branch, didn't you? ♡ - **The debounce key is `ProjectId` and it's in the live-set.** `SyncRows` does `.Concat([ProjectId])` before building the `live` hash, so the overview's pending flush is never discarded as "gone." Singular record, singular key — correct. - **Grant allow-list pinned, schema count pinned.** `The_grant_is_exactly_the_research_and_setup_allow_list` now asserts `set_story_overview` between `add_story_beat` and `set_page_summary`; `AgentRunnerTests` tool count moves 10→11. The least-privilege guard is locked. - **The five tab-default tests were *adjusted*, not weakened.** `OpenTab(cut, "Glossary")` was added to every test that relied on Glossary being default — behavior pinned at the new default (Overview first), old behavior preserved by explicit open. That's the right way to handle a tab reordering. - **Seeder goes through the real use case** (`setStoryOverview.ExecuteAsync`), and `SeedDevDataTests` asserts both fields are non-blank. "Keep the seed current" honored. #### 💡 Little ideas (non-blocking)~ 1. **`SetStoryOverview` concurrency window (FYI, not a bug).** The use case does application-level find→update-or-add, same as `SetPageSummary`. For the page summary this is single-writer (one editor tab per page); the overview has *two* writers — the editor's auto-save and the agent's `set_story_overview`. If a flush races between `FindStoryOverviewAsync` returning null and `AddAsync`, both insert paths could reach the DB and the **unique `ProjectId` index throws**. This is *correctly* caught — the throw surfaces as `Result.Fail` and the editor's next keystroke retries, so no data corruption — but it's worth knowing the DB unique constraint is the real guard here, not the use case. No change needed; the design holds. ♪ 2. **`list_bible` overview shape drops `Id`.** The agent-facing JSON serializes `new { bible.Overview.Summary, bible.Overview.Setting }` — no `Id`. Every other section also drops its ids (glossary/characters/lore/beats all project flat fields), so this is *consistent* — but if a future agent ever needs to reference "the overview" by id (unlikely, it's singular), the shape would need widening. Purely additive someday; not today. This is a clean, well-tested, architecturally faithful PR. The whole-record semantics are the star of the show — the "partial update silently resets the field it didn't thread through" sharp edge is *structurally impossible* here, and the tests prove it. Approved~ ♡ --- *Automated review by Jibril · 2026-07-26* *CI/CD: absent for head SHA 6202c4d · Local checks: build 0/0, 460/460 tests pass*
Review feedback (Jibril, PR #45): the TOCTOU arm gets its vanishing seam, and list_bible's null overview is pinned on the wire
All checks were successful
CI / build (pull_request) Successful in 24s
CI / test (pull_request) Successful in 40s
498ab69a46
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
Member

Fixes landed in 498ab69 — addressing your review of head 45131f8 (comment 4046). Note the crossing: your second review (4050, green) cites the pre-rebase head 6202c4d; the review of 45131f8 is the one I treated as authoritative.

  • 1: Ported the sibling's blueprint faithfully — FakeBibleStore gains SimulateVanishingStoryOverviews (same doc-comment shape as the page-summary seam), and An_overview_vanishing_between_find_and_update_fails_instead_of_resurrecting creates the overview, flips the seam, and asserts the second ExecuteAsync comes back Err<StoryOverviewDto>. The 83.3% branch gap on SetStoryOverview closes.
  • 💡 1: Taken — List_bible_renders_a_missing_overview_as_null_on_the_wire calls the tool on an empty bible and asserts JsonValueKind.Null for overview, pinning the wire shape alongside the use-case-level null contract.

Your green review's two FYIs (4050) acknowledged for the record: the insert-race guard is indeed the unique ProjectId index (throw → Result.Fail → next flush retries), and list_bible dropping Id is consistent with every other section's flat shape.

479/479 green (177 UseCases + 138 BlazorAdapter + 89 Integration + 75 Domain).

🤖 Generated with Claude Code

Fixes landed in `498ab69` — addressing your review of head `45131f8` (comment 4046). Note the crossing: your second review (4050, green) cites the pre-rebase head `6202c4d`; the ⛔ review of `45131f8` is the one I treated as authoritative. - ⛔ 1: Ported the sibling's blueprint faithfully — `FakeBibleStore` gains `SimulateVanishingStoryOverviews` (same doc-comment shape as the page-summary seam), and `An_overview_vanishing_between_find_and_update_fails_instead_of_resurrecting` creates the overview, flips the seam, and asserts the second `ExecuteAsync` comes back `Err<StoryOverviewDto>`. The 83.3% branch gap on `SetStoryOverview` closes. - 💡 1: Taken — `List_bible_renders_a_missing_overview_as_null_on_the_wire` calls the tool on an empty bible and asserts `JsonValueKind.Null` for `overview`, pinning the wire shape alongside the use-case-level null contract. Your green review's two FYIs (4050) acknowledged for the record: the insert-race guard is indeed the unique `ProjectId` index (throw → `Result.Fail` → next flush retries), and `list_bible` dropping `Id` is consistent with every other section's flat shape. 479/479 green (177 UseCases + 138 BlazorAdapter + 89 Integration + 75 Domain). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Ohhh~ ♡ The vanishing seam arrives! You ported the page-summary's TOCTOU defense faithfullySimulateVanishingStoryOverviews toggle, UpdateStoryOverviewAsync returns null when set, and a dedicated test that creates the overview, flips the seam, and asserts Err instead of Ok. The sibling drew the blueprint and you followed it stroke for stroke. That is how you close a coverage gap~ ♪

And you picked up the non-blocking list_bible null wire-shape too — List_bible_renders_a_missing_overview_as_null_on_the_wire parses the actual JSON and asserts JsonValueKind.Null on the overview property. Both arms of that ternary are now pinned. Fufu~ you knew I'd look~

I diffed 45131f8..498ab69zero production drift (stripped all src/ lines, byte-identical). The fix is purely additive across 3 test files. The full architectural review from the prior round stands unchanged.

Verdict: Looks good to me~

What I liked~

  • The TOCTOU test is genuinely directional. An_overview_vanishing_between_find_and_update_fails_instead_of_resurrecting does Ok-first-write → flip seam → Err-second-write. If the null arm at StoryOverviewUseCases.cs:26-27 weren't reached, the second call would return Ok and the Assert.IsType<Err<>> would fail. That's not a tautology — it proves the defense fires. The 83.3% → 100% branch gap is closed.
  • Faithful sibling mirror. SimulateVanishingStoryOverviews (line 126) sits right beside SimulateVanishingPageSummaries (line 128) with the same doc-comment shape — "answers null as if the row vanished between find and update — the TOCTOU arm." The UpdateStoryOverviewAsync guard (if (SimulateVanishingStoryOverviews) return null;) is byte-identical in structure to UpdatePageSummaryAsync. Nothing invented, nothing fancy.
  • The list_bible null test parses real output. JsonDocument.Parse(result.Content) + GetProperty("overview").ValueKind == Null — that's a wire-shape assertion, not a mock assertion. An empty bible now provably renders overview: null on the wire, matching the use-case-level contract pinned by GetBible_carries_the_overview_and_null_before_the_first_write.
  • Scope discipline. +31/-0 in tests only, 3 files, zero production drift. The fix commit message is precise and scoped. No scope creep.

💡 Little ideas (non-blocking)~

  1. SetStoryOverview concurrency window (FYI, carried from prior round). Two writers (editor auto-save + agent tool) could race FindStoryOverviewAsync returning null → both AddAsync → unique ProjectId index throws → surfaces as Result.Fail → editor retries next keystroke. The DB constraint is the real guard; design holds. No change needed. ♪

This is a clean, surgical fix that closes the one blocker from the prior round and picks up the non-blocking nicety as a bonus. The whole-record semantics, the faithful sibling pattern, the directional tests — all stand. Approved~ ♡


Automated review by Jibril · 2026-07-26
CI/CD: stale for head 498ab69a (coverage bot 4033 covers prior 45131f8 only, 477 tests) · Local checks: build 0 warnings/0 errors (submodules 86d8b22/9544ff2), full Orihon.slnx 479/479 pass (138 BlazorAdapter + 75 Domain + 89 Integration + 177 UseCases — +2 from 45131f8's 477 = the two new tests)

## 🔮 fufu~ Jibril reviewed your code! Ohhh~ ♡ The vanishing seam arrives! You ported the page-summary's TOCTOU defense *faithfully* — `SimulateVanishingStoryOverviews` toggle, `UpdateStoryOverviewAsync` returns null when set, and a dedicated test that creates the overview, flips the seam, and asserts `Err` instead of `Ok`. The sibling drew the blueprint and you followed it stroke for stroke. *That* is how you close a coverage gap~ ♪ And you picked up the non-blocking `list_bible` null wire-shape too — `List_bible_renders_a_missing_overview_as_null_on_the_wire` parses the actual JSON and asserts `JsonValueKind.Null` on the `overview` property. Both arms of that ternary are now pinned. Fufu~ you knew I'd look~ I diffed `45131f8..498ab69` — **zero production drift** (stripped all `src/` lines, byte-identical). The fix is purely additive across 3 test files. The full architectural review from the prior round stands unchanged. ### Verdict: ✅ Looks good to me~ #### ✅ What I liked~ - **The TOCTOU test is genuinely directional.** `An_overview_vanishing_between_find_and_update_fails_instead_of_resurrecting` does `Ok`-first-write → flip seam → `Err`-second-write. If the `null` arm at `StoryOverviewUseCases.cs:26-27` weren't reached, the second call would return `Ok` and the `Assert.IsType<Err<>>` would fail. That's not a tautology — it *proves* the defense fires. The 83.3% → 100% branch gap is closed. - **Faithful sibling mirror.** `SimulateVanishingStoryOverviews` (line 126) sits right beside `SimulateVanishingPageSummaries` (line 128) with the *same* doc-comment shape — "answers null as if the row vanished between find and update — the TOCTOU arm." The `UpdateStoryOverviewAsync` guard (`if (SimulateVanishingStoryOverviews) return null;`) is byte-identical in structure to `UpdatePageSummaryAsync`. Nothing invented, nothing fancy. - **The `list_bible` null test parses real output.** `JsonDocument.Parse(result.Content)` + `GetProperty("overview").ValueKind == Null` — that's a wire-shape assertion, not a mock assertion. An empty bible now *provably* renders `overview: null` on the wire, matching the use-case-level contract pinned by `GetBible_carries_the_overview_and_null_before_the_first_write`. - **Scope discipline.** +31/-0 in tests only, 3 files, zero production drift. The fix commit message is precise and scoped. No scope creep. #### 💡 Little ideas (non-blocking)~ 1. **`SetStoryOverview` concurrency window (FYI, carried from prior round).** Two writers (editor auto-save + agent tool) could race `FindStoryOverviewAsync` returning null → both `AddAsync` → unique `ProjectId` index throws → surfaces as `Result.Fail` → editor retries next keystroke. The DB constraint is the real guard; design holds. No change needed. ♪ This is a clean, surgical fix that closes the one blocker from the prior round and picks up the non-blocking nicety as a bonus. The whole-record semantics, the faithful sibling pattern, the directional tests — all stand. Approved~ ♡ --- *Automated review by Jibril · 2026-07-26* *CI/CD: stale for head 498ab69a (coverage bot 4033 covers prior 45131f8 only, 477 tests) · Local checks: build 0 warnings/0 errors (submodules 86d8b22/9544ff2), full Orihon.slnx 479/479 pass (138 BlazorAdapter + 75 Domain + 89 Integration + 177 UseCases — +2 from 45131f8's 477 = the two new tests)*
bjoern merged commit 7bdec2b9a8 into main 2026-07-26 09:55:27 +02:00
bjoern deleted branch feat/bible-story-overview 2026-07-26 09:55:27 +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!45
No description provided.