feat: house rules 1/5 — the bible carries them #98

Merged
bjoern merged 3 commits from feat/house-rules-table into main 2026-07-28 18:48:53 +02:00
Member

First slice of ADR 0026. The CRUD spine only: the table, its editor, and the seed. Nothing reads the rules yet — kickoff injection is 2/5, and the ruling that writes them follows.

The bible held facts about the work (ADR 0013) and the review gate corrected single instances (ADR 0019); neither carried policy. House rules are the missing layer — the decisions that hold for a whole book and that every agent otherwise makes fresh, page after page.

What's in

DomainHouseRule (Id, ProjectId, Rule, Origin?, timestamps). Two properties are deliberate rather than incidental:

  • Origin is inline text, not a relation. A ruling request will not outlive its run (ADR 0026's durability decision), so there is nothing to point at — and a rule has to stay readable once whoever wrote it has forgotten why.
  • Origin is not updatable, anywhere in the stack. HouseRule.Update takes the policy alone, UpdateHouseRule has no origin parameter, and the editor renders it as text rather than a field. Provenance that can be rewritten records nothing. AddHouseRule takes it optionally so 5/5 can pass one; today every UI-created rule has none.

PersistenceHouseRuleConfiguration + AddHouseRules migration. No length cap on either column: a rule is prose addressed to an agent, and an origin carries a whole question and its answer. FK cascade from Projects, so deleting a project takes its rules (ADR 0005).

Use casesAddHouseRule / UpdateHouseRule / DeleteHouseRule, IBibleStore methods, HouseRuleDto, and GetBible gathering them.

UI — its own tab after the glossary, not folded into it: every other bible section is material agents read as fact, this is the one they will obey as instruction. The rule is a full-width TextArea; the origin renders beneath it as muted, full-span provenance. Fluxor actions/reducers/effects follow the existing per-table shape.

Seeder — one hand-written rule and one carrying an origin, so both render paths have something to show (AGENTS.md: a feature the seed can't show is one browser verification silently skips).

Icongavel, via Kagaku.UI #9 (merged). Submodule bumped faf01daa4a4fc0, the squash-merge commit on Kagaku.UI's main — the squash orphaned the branch commit, so that is the only correct pin.

Tests

838 green (+15). They pin the invariants, not the plumbing:

  • Domain — trimming; blank-origin normalizes to null rather than "" (the two render differently while meaning the same thing); and Update leaves provenance alone.
  • Use cases — an origin is stored on add and survives an edit; the guard set for blank text, missing project, missing row.
  • EfBibleStore — round trip with the origin's newline intact, and deleting the project takes its rules (the cascade is the only thing that removes them; nothing else knows to).
  • Read model — house rules gathered, and another project's rules do not leak in.
  • bUnit — the tab renders, an edit auto-saves without touching the origin, the origin shows as text rather than a third field, and the add row works.
  • Seeder — asserts both an origin-less and an origin-carrying rule exist.

Browser-verified

Against the seeded world, because bUnit misses editor dirty-flag bugs:

  • Typed into a rule, switched tabs mid-debounce and back, then reloaded — the edit reached SQLite and survived.
  • Edited the rule that has an origin, reloaded — policy changed, origin intact including its newline. That is the ADR's load-bearing invariant, verified live rather than only in tests.
  • Add clears its field; delete goes through the confirm gate and removes the right row.
  • Zero console errors. (The seeded pages' /raw 404s are pre-existing — image fields are deliberately empty per AGENTS.md.)
  • After the icon swap: the real gavel path is in the rendered DOM (135 chars, not an empty <path>), and the glyph draws correctly.

Honest notes

  • The feature does nothing for agents yet. The table is editable and seeded, but no agent sees a rule until 2/5 wires the kickoff. Shipping the spine alone keeps this slice reviewable; it does mean 1/5 is user-facing only.
  • origin has no writer in this PR. Only the seeder produces one, because the ruling flow is 3/5–5/5. The column, the DTO field, the non-updatability and the render path all exist now so that later slice adds a caller, not a migration.
  • The icon tripwire is what verifies the submodule pin, not my word: it scans src/ for Icon="…" literals against the catalog, so a pin that didn't carry gavel fails the suite rather than silently shipping invisible clickable air.

🤖 Generated with Claude Code

First slice of ADR 0026. The CRUD spine only: the table, its editor, and the seed. **Nothing reads the rules yet** — kickoff injection is 2/5, and the ruling that writes them follows. The bible held facts about the work (ADR 0013) and the review gate corrected single instances (ADR 0019); neither carried **policy**. House rules are the missing layer — the decisions that hold for a whole book and that every agent otherwise makes fresh, page after page. ## What's in **Domain** — `HouseRule` (`Id`, `ProjectId`, `Rule`, `Origin?`, timestamps). Two properties are deliberate rather than incidental: - **`Origin` is inline text, not a relation.** A ruling request will not outlive its run (ADR 0026's durability decision), so there is nothing to point at — and a rule has to stay readable once whoever wrote it has forgotten why. - **`Origin` is not updatable, anywhere in the stack.** `HouseRule.Update` takes the policy alone, `UpdateHouseRule` has no origin parameter, and the editor renders it as text rather than a field. Provenance that can be rewritten records nothing. `AddHouseRule` takes it optionally so 5/5 can pass one; today every UI-created rule has none. **Persistence** — `HouseRuleConfiguration` + `AddHouseRules` migration. No length cap on either column: a rule is prose addressed to an agent, and an origin carries a whole question and its answer. FK cascade from `Projects`, so deleting a project takes its rules (ADR 0005). **Use cases** — `AddHouseRule` / `UpdateHouseRule` / `DeleteHouseRule`, `IBibleStore` methods, `HouseRuleDto`, and `GetBible` gathering them. **UI** — its own tab **after the glossary**, not folded into it: every other bible section is material agents read as *fact*, this is the one they will obey as *instruction*. The rule is a full-width `TextArea`; the origin renders beneath it as muted, full-span provenance. Fluxor actions/reducers/effects follow the existing per-table shape. **Seeder** — one hand-written rule and one carrying an origin, so both render paths have something to show (AGENTS.md: a feature the seed can't show is one browser verification silently skips). **Icon** — `gavel`, via Kagaku.UI #9 (merged). Submodule bumped `faf01da` → **`a4a4fc0`**, the squash-merge commit on Kagaku.UI's `main` — the squash orphaned the branch commit, so that is the only correct pin. ## Tests **838 green (+15).** They pin the invariants, not the plumbing: - **Domain** — trimming; blank-origin normalizes to `null` rather than `""` (the two render differently while meaning the same thing); and `Update` leaves provenance alone. - **Use cases** — an origin is stored on add and **survives an edit**; the guard set for blank text, missing project, missing row. - **EfBibleStore** — round trip with the origin's newline intact, and **deleting the project takes its rules** (the cascade is the only thing that removes them; nothing else knows to). - **Read model** — house rules gathered, and another project's rules do not leak in. - **bUnit** — the tab renders, an edit auto-saves without touching the origin, the origin shows as text rather than a third field, and the add row works. - **Seeder** — asserts both an origin-less and an origin-carrying rule exist. ## Browser-verified Against the seeded world, because bUnit misses editor dirty-flag bugs: - Typed into a rule, **switched tabs mid-debounce and back**, then reloaded — the edit reached SQLite and survived. - Edited the rule that has an origin, reloaded — policy changed, **origin intact including its newline**. That is the ADR's load-bearing invariant, verified live rather than only in tests. - Add clears its field; delete goes through the confirm gate and removes the right row. - **Zero console errors.** (The seeded pages' `/raw` 404s are pre-existing — image fields are deliberately empty per AGENTS.md.) - After the icon swap: the real gavel path is in the rendered DOM (135 chars, not an empty `<path>`), and the glyph draws correctly. ## Honest notes - **The feature does nothing for agents yet.** The table is editable and seeded, but no agent sees a rule until 2/5 wires the kickoff. Shipping the spine alone keeps this slice reviewable; it does mean 1/5 is user-facing only. - **`origin` has no writer in this PR.** Only the seeder produces one, because the ruling flow is 3/5–5/5. The column, the DTO field, the non-updatability and the render path all exist now so that later slice adds a caller, not a migration. - **The icon tripwire is what verifies the submodule pin**, not my word: it scans `src/` for `Icon="…"` literals against the catalog, so a pin that didn't carry `gavel` fails the suite rather than silently shipping invisible clickable air. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The bible held facts about the work and the review gate corrected single
instances; neither carried policy. House rules are the missing layer —
the decisions that hold for a whole book and that every agent otherwise
makes fresh, page after page.

This is the CRUD spine only: the table, its editor, and the seed. Nothing
reads the rules yet — the kickoff injection is the next slice, and the
ruling that writes them follows it.

The table sits in the bible beside the rest and is stored, scoped and
cascaded exactly like it. Two things are its own:

- Origin is inline text, not a relation. A ruling request will not
  outlive its run, so there is nothing to point at, and a rule has to
  stay readable once whoever wrote it has forgotten why.
- Origin is not updatable. HouseRule.Update takes the policy alone, the
  use case takes no origin, and the editor renders it as text rather
  than a field: provenance that can be rewritten records nothing.

Its own tab after the glossary rather than a fold inside it — every other
bible section is material agents read as fact, this is the one they will
obey as instruction. The icon is "verified" because Kagaku.UI's catalog
has no "gavel", and an uncatalogued name renders as invisible clickable
air.

Tests: 838 green (+15). Domain pins the trim, the blank-origin-is-absent
normalization, and that Update leaves provenance alone; the use cases pin
that an origin survives an edit; EfBibleStore pins the round trip and
that deleting the project takes the rules with it; the seeder gets one
hand-written rule and one with an origin so both render paths have
something to show.

Browser-verified against the seeded world: typed into a rule, switched
tabs mid-debounce and back, reloaded — the edit reached SQLite and the
origin survived intact, newline and all. Add clears its field, delete
goes through the confirm gate. Zero console errors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat: the house-rules tab gets its gavel (Kagaku.UI #9)
All checks were successful
CI / build (pull_request) Successful in 26s
CI / test (pull_request) Successful in 46s
d7377ad8f1
Kagaku.UI now carries the glyph, so the stand-in goes. Submodule pointer
moves faf01da → a4a4fc0, the squash-merge commit on Kagaku.UI's main —
the local branch commit is orphaned by the squash and must not be pinned.

The icon tripwire is what verifies this rather than my say-so: it scans
src for Icon="name" literals against the catalog, so a pin that did not
actually carry gavel would fail the suite rather than ship an invisible
glyph.

838/838 green.

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

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

Commit: 9ef856d · Generated: 2026-07-28 16:35:24 UTC · Revision: #2

Summary

Summary
Generated on: 07/28/2026 - 16:35:23
Coverage date: 07/28/2026 - 16:35:08 - 07/28/2026 - 16:35:21
Parser: MultiReport (4x Cobertura)
Assemblies: 6
Classes: 447
Files: 212
Line coverage: 96.6% (15286 of 15822)
Covered lines: 15286
Uncovered lines: 536
Coverable lines: 15822
Total lines: 27602
Branch coverage: 83.5% (2748 of 3289)
Covered branches: 2748
Total branches: 3289
Method coverage: Feature is only available for sponsors

Coverage

Orihon.BlazorAdapter - 95.5%
Name Line Branch
Orihon.BlazorAdapter 95.5% 88.2%
Orihon.BlazorAdapter.Bible.AddBeatRowRequested 100%
Orihon.BlazorAdapter.Bible.AddCharacterRowRequested 100%
Orihon.BlazorAdapter.Bible.AddGlossaryRowRequested 100%
Orihon.BlazorAdapter.Bible.AddHouseRuleRowRequested 100%
Orihon.BlazorAdapter.Bible.AddLoreRowRequested 100%
Orihon.BlazorAdapter.Bible.BibleEffects 89.8% 76.9%
Orihon.BlazorAdapter.Bible.BibleLoaded 100%
Orihon.BlazorAdapter.Bible.BiblePage 92.3% 80%
Orihon.BlazorAdapter.Bible.BibleReducers 90.6%
Orihon.BlazorAdapter.Bible.BibleState 100%
Orihon.BlazorAdapter.Bible.BibleWriteFailed 100%
Orihon.BlazorAdapter.Bible.DeleteBeatRowRequested 100%
Orihon.BlazorAdapter.Bible.DeleteCharacterRowRequested 100%
Orihon.BlazorAdapter.Bible.DeleteGlossaryRowRequested 100%
Orihon.BlazorAdapter.Bible.DeleteHouseRuleRowRequested 0%
Orihon.BlazorAdapter.Bible.DeleteLoreRowRequested 0%
Orihon.BlazorAdapter.Bible.LoadBible 100%
Orihon.BlazorAdapter.Bible.ReorderBeatsRequested 0%
Orihon.BlazorAdapter.Bible.SaveOverviewRequested 100%
Orihon.BlazorAdapter.Bible.SaveSummaryRequested 100%
Orihon.BlazorAdapter.Bible.UpdateBeatRowRequested 100%
Orihon.BlazorAdapter.Bible.UpdateCharacterRowRequested 100%
Orihon.BlazorAdapter.Bible.UpdateGlossaryRowRequested 100%
Orihon.BlazorAdapter.Bible.UpdateHouseRuleRowRequested 100%
Orihon.BlazorAdapter.Bible.UpdateLoreRowRequested 100%
Orihon.BlazorAdapter.BlazorAdapterAssembly 100%
Orihon.BlazorAdapter.Debounce 96.2% 94.4%
Orihon.BlazorAdapter.Diagnostics.CircuitError 100%
Orihon.BlazorAdapter.Diagnostics.CircuitErrorPanel 100%
Orihon.BlazorAdapter.Diagnostics.CircuitErrorSink 100% 85.7%
Orihon.BlazorAdapter.Diagnostics.OrihonStoreInitializer 85.7% 66.6%
Orihon.BlazorAdapter.PageWorkspace.CreateRegionRequested 100%
Orihon.BlazorAdapter.PageWorkspace.DeletePageSummaryRequested 100%
Orihon.BlazorAdapter.PageWorkspace.DeleteRegionRequested 100%
Orihon.BlazorAdapter.PageWorkspace.LoadPageWorkspace 100%
Orihon.BlazorAdapter.PageWorkspace.PageSummarySaved 100%
Orihon.BlazorAdapter.PageWorkspace.PageViewport 100% 100%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceEffects 100% 100%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceLoaded 100%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspacePage 92.2% 85.5%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceReducers 100% 66.6%
Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceState 100%
Orihon.BlazorAdapter.PageWorkspace.PageWriteFailed 100%
Orihon.BlazorAdapter.PageWorkspace.RegionCreated 100%
Orihon.BlazorAdapter.PageWorkspace.RegionSaved 100%
Orihon.BlazorAdapter.PageWorkspace.ReorderRegionsRequested 100%
Orihon.BlazorAdapter.PageWorkspace.ReprocessPageRequested 100%
Orihon.BlazorAdapter.PageWorkspace.ReprocessTranslationRequested 100%
Orihon.BlazorAdapter.PageWorkspace.SavePageSummaryRequested 100%
Orihon.BlazorAdapter.PageWorkspace.SaveRegionRequested 100%
Orihon.BlazorAdapter.PageWorkspace.SetPageMetaRequested 100%
Orihon.BlazorAdapter.Projects.CreateProjectRequested 100%
Orihon.BlazorAdapter.Projects.DecideSetupContinuation 100%
Orihon.BlazorAdapter.Projects.DeleteProjectRequested 100%
Orihon.BlazorAdapter.Projects.FinishSetupRequested 100%
Orihon.BlazorAdapter.Projects.ImportPagesRequested 100%
Orihon.BlazorAdapter.Projects.LoadWizard 100%
Orihon.BlazorAdapter.Projects.PageOrganizer 96% 95%
Orihon.BlazorAdapter.Projects.PagesImported 100%
Orihon.BlazorAdapter.Projects.ProjectDeleteFailed 100%
Orihon.BlazorAdapter.Projects.ProjectListEffects 100% 100%
Orihon.BlazorAdapter.Projects.ProjectListPage 89.7% 91.1%
Orihon.BlazorAdapter.Projects.ProjectListReducers 100%
Orihon.BlazorAdapter.Projects.ProjectListState 100%
Orihon.BlazorAdapter.Projects.ProjectsLoaded 100%
Orihon.BlazorAdapter.Projects.ProjectWizardEffects 93.8% 90%
Orihon.BlazorAdapter.Projects.ProjectWizardPage 95.3% 84.1%
Orihon.BlazorAdapter.Projects.ProjectWizardReducers 100%
Orihon.BlazorAdapter.Projects.ProjectWizardState 100%
Orihon.BlazorAdapter.Projects.SetupChat 93.5% 100%
Orihon.BlazorAdapter.Projects.SetupChatEffects 100% 100%
Orihon.BlazorAdapter.Projects.SetupChatFailed 100%
Orihon.BlazorAdapter.Projects.SetupChatReducers 100%
Orihon.BlazorAdapter.Projects.SetupChatState 100%
Orihon.BlazorAdapter.Projects.SetupChatUpdated 100%
Orihon.BlazorAdapter.Projects.StartSetupChat 100%
Orihon.BlazorAdapter.Projects.SubmitSetupAnswer 100%
Orihon.BlazorAdapter.Projects.WizardDeletePagesRequested 100%
Orihon.BlazorAdapter.Projects.WizardLoaded 100%
Orihon.BlazorAdapter.Projects.WizardMovePagesRequested 100%
Orihon.BlazorAdapter.Projects.WizardMovePagesToNewChapterRequested 100%
Orihon.BlazorAdapter.Projects.WizardReorderPagesRequested 100%
Orihon.BlazorAdapter.Projects.WizardWriteFailed 100%
Orihon.BlazorAdapter.Runs.CancelMonitorRun 100%
Orihon.BlazorAdapter.Runs.MonitorPageRef 100%
Orihon.BlazorAdapter.Runs.MonitorRunLoaded 100%
Orihon.BlazorAdapter.Runs.RetryMonitorExecution 100%
Orihon.BlazorAdapter.Runs.RunChangedBridge 95% 92.8%
Orihon.BlazorAdapter.Runs.RunMonitor 97.9% 96.2%
Orihon.BlazorAdapter.Runs.RunMonitorEffects 100% 91.6%
Orihon.BlazorAdapter.Runs.RunMonitorReducers 100%
Orihon.BlazorAdapter.Runs.RunMonitorState 100%
Orihon.BlazorAdapter.Settings.AgentDebriefsLoaded 100%
Orihon.BlazorAdapter.Settings.AgentDebriefsLoadFailed 100%
Orihon.BlazorAdapter.Settings.AgentEffortPicked 100%
Orihon.BlazorAdapter.Settings.AgentEffortSaved 100%
Orihon.BlazorAdapter.Settings.AgentEffortSaveFailed 100%
Orihon.BlazorAdapter.Settings.AgentFeedbackLoaded 100%
Orihon.BlazorAdapter.Settings.AgentFeedbackLoadFailed 0%
Orihon.BlazorAdapter.Settings.AgentModelPicked 100%
Orihon.BlazorAdapter.Settings.AgentModelSaved 100%
Orihon.BlazorAdapter.Settings.AgentModelSaveFailed 100%
Orihon.BlazorAdapter.Settings.EffortOption 100% 100%
Orihon.BlazorAdapter.Settings.KeySaved 100%
Orihon.BlazorAdapter.Settings.KeySaveFailed 100%
Orihon.BlazorAdapter.Settings.ModelOptionsLoaded 100%
Orihon.BlazorAdapter.Settings.ModelOptionsUnavailable 100%
Orihon.BlazorAdapter.Settings.SaveKeyRequested 100%
Orihon.BlazorAdapter.Settings.SettingsEffects 94.2% 75%
Orihon.BlazorAdapter.Settings.SettingsLoaded 100%
Orihon.BlazorAdapter.Settings.SettingsPage 97.9% 90.4%
Orihon.BlazorAdapter.Settings.SettingsReducers 94.1%
Orihon.BlazorAdapter.Settings.SettingsState 100%
Orihon.BlazorAdapter.Settings.SfxPassToggled 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%
Orihon.Domain.Translation.Chapter 100%
Orihon.Domain.Translation.Page 100%
Orihon.Domain.Translation.Region 100% 100%
Orihon.Domain.Translation.RegionProfile 100%
Orihon.Infrastructure - 96.4%
Name Line Branch
Orihon.Infrastructure 96.4% 71.2%
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 94.7% 85.4%
Orihon.Infrastructure.Gateways.FileSystemAgentTranscriptStore 86.1% 78.5%
Orihon.Infrastructure.Gateways.HttpWebPageFetcher 95.1% 83.3%
Orihon.Infrastructure.Gateways.OpenRouterLlmGateway 90.3% 82.2%
Orihon.Infrastructure.Gateways.SkiaPageImageRenderer 97.5% 87.2%
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.AddRuns 99.1%
Orihon.Infrastructure.Persistence.Migrations.AddStoryOverview 99.5%
Orihon.Infrastructure.Persistence.Migrations.InitialTranslationDomain 97.3%
Orihon.Infrastructure.Persistence.Migrations.OrihonDbContextModelSnapshot 100%
Orihon.Infrastructure.Persistence.Migrations.RenameSourceTargetColumns 97.2%
Orihon.Infrastructure.Persistence.OrihonDbContext 100%
Orihon.Infrastructure.Persistence.OrihonDbContextFactory 100%
Orihon.Infrastructure.Projects.EfProjectStore 100% 100%
Orihon.Infrastructure.Projects.FileSystemPageImageStore 100% 100%
Orihon.Infrastructure.Runs.EfRunStore 98.1% 75%
Orihon.Infrastructure.Settings.EfAppSettingsStore 100% 100%
Orihon.Infrastructure.Translation.EfChapterStore 100% 100%
Orihon.Infrastructure.Translation.EfPageStore 86% 80%
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.3%
Name Line Branch
Orihon.Server 93.3% 70%
Orihon.Server.Components.App 100%
Orihon.Server.Components.Layout.MainLayout 100%
Orihon.Server.Components.Pages.Gate 64.2% 66.6%
Orihon.Server.RunEngineBootstrap 100%
Orihon.Server.Security.AccessGate 91.8% 41.6%
Orihon.Server.Security.AccessSecret 100% 50%
Orihon.Server.VolumeStartupValidator 100% 100%
Program 94.8% 87.5%
Orihon.UseCases - 97.2%
Name Line Branch
Orihon.UseCases 97.2% 88.3%
Orihon.UseCases.Agents.AgentAttemptPreparation 100%
Orihon.UseCases.Agents.AgentAttemptSupport 100% 97%
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.AddRegionParams 100%
Orihon.UseCases.Agents.Annotation.AddRegionTool 95.4% 75%
Orihon.UseCases.Agents.Annotation.AddSfxRegionTool 95.2% 75%
Orihon.UseCases.Agents.Annotation.AnnotationBlueprints 100%
Orihon.UseCases.Agents.Annotation.AnnotationStage 100% 85%
Orihon.UseCases.Agents.Annotation.BboxCreationExecutor 94.1% 50%
Orihon.UseCases.Agents.Annotation.BboxRefinementExecutor 93.7% 81.2%
Orihon.UseCases.Agents.Annotation.BoundBoxParams 100%
Orihon.UseCases.Agents.Annotation.BoundContactSheetTool 91.3% 75%
Orihon.UseCases.Agents.Annotation.BoundCropParams 100%
Orihon.UseCases.Agents.Annotation.BoundCropTool 100%
Orihon.UseCases.Agents.Annotation.BoundViewPageTool 92.5% 80%
Orihon.UseCases.Agents.Annotation.BoundViewParams 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.ListRegionsTool 91.6% 80%
Orihon.UseCases.Agents.Annotation.MoveResizeBoundTool 90.9% 50%
Orihon.UseCases.Agents.Annotation.MoveResizeRegionParams 100%
Orihon.UseCases.Agents.Annotation.MoveResizeRegionTool 95% 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 87.2% 53.8%
Orihon.UseCases.Agents.Annotation.RegionBriefing 100% 100%
Orihon.UseCases.Agents.Annotation.RegionCropParams 100%
Orihon.UseCases.Agents.Annotation.RegionCropTool 100%
Orihon.UseCases.Agents.Annotation.RegionProblemParams 100%
Orihon.UseCases.Agents.Annotation.RejectRegionParams 100%
Orihon.UseCases.Agents.Annotation.RejectRegionTool 100% 50%
Orihon.UseCases.Agents.Annotation.ReorderRegionParams 100%
Orihon.UseCases.Agents.Annotation.ReorderRegionTool 88% 60%
Orihon.UseCases.Agents.Annotation.ReportQaParams 100%
Orihon.UseCases.Agents.Annotation.ReportQaTool 97.7% 90%
Orihon.UseCases.Agents.Annotation.SetPageMetaParams 100%
Orihon.UseCases.Agents.Annotation.SetPageMetaTool 100% 75%
Orihon.UseCases.Agents.Annotation.SetRegionTypeParams 100%
Orihon.UseCases.Agents.Annotation.SetRegionTypeTool 100% 87.5%
Orihon.UseCases.Agents.Annotation.SetTranscriptionParams 100%
Orihon.UseCases.Agents.Annotation.SetTranscriptionTool 100% 100%
Orihon.UseCases.Agents.Annotation.SfxCreationExecutor 88.8% 50%
Orihon.UseCases.Agents.Annotation.SfxQaExecutor 93.9% 83.3%
Orihon.UseCases.Agents.Annotation.SfxTranscriptionExecutor 93.1% 80%
Orihon.UseCases.Agents.Annotation.TranscriptionExecutor 93.1% 80%
Orihon.UseCases.Agents.AssistantSpoke 100%
Orihon.UseCases.Agents.BibleBuilding.BibleBuildingBlueprint 100%
Orihon.UseCases.Agents.BibleBuilding.BibleBuildingExecutor 96.7% 75%
Orihon.UseCases.Agents.BibleBuilding.GetRegionParams 100%
Orihon.UseCases.Agents.BibleBuilding.GetRegionTool 84.6% 72.2%
Orihon.UseCases.Agents.BibleBuilding.ListProjectRegionsTool 86.3% 90%
Orihon.UseCases.Agents.BibleBuilding.ListRegionsParams 100%
Orihon.UseCases.Agents.Inspection.PageImageAccess 94.5% 77.7%
Orihon.UseCases.Agents.Inspection.ViewAccount 100% 100%
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.Setup.ResearchSetupExecutor 98.5% 92.8%
Orihon.UseCases.Agents.Setup.SetupChatEntry 100%
Orihon.UseCases.Agents.Setup.SetupConversation 100% 87.5%
Orihon.UseCases.Agents.Setup.SetupConversationRegistry 100%
Orihon.UseCases.Agents.ToolCalled 100%
Orihon.UseCases.Agents.ToolCompleted 100%
Orihon.UseCases.Agents.Translation.GetPageSummaryParams 100%
Orihon.UseCases.Agents.Translation.GetPageSummaryTool 80% 66.6%
Orihon.UseCases.Agents.Translation.SetTranslationParams 100%
Orihon.UseCases.Agents.Translation.SetTranslationTool 88.5% 78.5%
Orihon.UseCases.Agents.Translation.TranslationBlueprint 100%
Orihon.UseCases.Agents.Translation.TranslationExecutor 93.6% 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.5% 93.7%
Orihon.UseCases.Gateways.LabeledBox 100%
Orihon.UseCases.Gateways.LlmKeyInfo 100%
Orihon.UseCases.Gateways.LlmModel 100%
Orihon.UseCases.Gateways.LlmReasoning 100% 100%
Orihon.UseCases.Gateways.PixelWindow 100%
Orihon.UseCases.Gateways.RenderedView 100%
Orihon.UseCases.NextOrder 100%
Orihon.UseCases.Pages.DeletePage 100% 100%
Orihon.UseCases.Pages.DeletePages 100% 100%
Orihon.UseCases.Pages.GetPage 100% 100%
Orihon.UseCases.Pages.GetProjectWorkspace 100% 100%
Orihon.UseCases.Pages.ImportPages 100% 100%
Orihon.UseCases.Pages.ImportPagesResult 100%
Orihon.UseCases.Pages.MarkPageAnnotated 100% 100%
Orihon.UseCases.Pages.MovePage 100% 92.8%
Orihon.UseCases.Pages.MovePages 100% 100%
Orihon.UseCases.Pages.PageDetailDto 100%
Orihon.UseCases.Pages.PageDto 100%
Orihon.UseCases.Pages.PageUpload 100%
Orihon.UseCases.Pages.ProjectWorkspaceDto 100%
Orihon.UseCases.Pages.ReorderPages 100%
Orihon.UseCases.Pages.SetPageMeta 100% 100%
Orihon.UseCases.Pages.WorkspaceChapterDto 100%
Orihon.UseCases.Projects.CompleteProjectSetup 100% 93.7%
Orihon.UseCases.Projects.CreateProject 100% 100%
Orihon.UseCases.Projects.DeleteProject 100% 100%
Orihon.UseCases.Projects.GetProject 100% 100%
Orihon.UseCases.Projects.ListProjects 100%
Orihon.UseCases.Projects.ProjectDto 96.1%
Orihon.UseCases.Projects.StartAnnotationRun 96.4% 92.8%
Orihon.UseCases.Projects.StartBibleRun 90.9% 83.3%
Orihon.UseCases.Projects.StartSetupRun 100% 100%
Orihon.UseCases.Projects.StartTranslationRun 90.9% 83.3%
Orihon.UseCases.Projects.StoredPageImage 100%
Orihon.UseCases.Projects.UpdateProjectMetadata 100% 100%
Orihon.UseCases.Regions.CreateRegion 100% 100%
Orihon.UseCases.Regions.DeleteRegion 100% 100%
Orihon.UseCases.Regions.RegionDto 97%
Orihon.UseCases.Regions.ReorderRegions 100%
Orihon.UseCases.Regions.UpdateRegion 100% 100%
Orihon.UseCases.Runs.AnnotationPipeline 100% 100%
Orihon.UseCases.Runs.ExecutionDto 93.3%
Orihon.UseCases.Runs.ExecutionProgress 100%
Orihon.UseCases.Runs.ExecutionProgressRegistry 100% 100%
Orihon.UseCases.Runs.ExecutionPulseRelay 100% 100%
Orihon.UseCases.Runs.PlannedExecution 100%
Orihon.UseCases.Runs.PulseTarget 100%
Orihon.UseCases.Runs.ReprocessPage 100% 94.4%
Orihon.UseCases.Runs.ReprocessTranslation 94.1% 92.8%
Orihon.UseCases.Runs.RunDto 93.3% 90%
Orihon.UseCases.Runs.RunEngine 94.8% 94.3%
Orihon.UseCases.Runs.RunEngineOptions 100% 100%
Orihon.UseCases.Runs.StageContext 100% 50%
Orihon.UseCases.Runs.StageHaltedException 100%
Orihon.UseCases.Runs.WorkStates 100% 83.3%
Orihon.UseCases.Settings.AgentSettingDto 100% 100%
Orihon.UseCases.Settings.EffortSetting 100% 100%
Orihon.UseCases.Settings.GetSettings 100% 100%
Orihon.UseCases.Settings.ListModelOptions 100% 100%
Orihon.UseCases.Settings.SaveAgentEffort 100% 100%
Orihon.UseCases.Settings.SaveAgentModel 100% 100%
Orihon.UseCases.Settings.SaveOpenRouterKey 100% 100%
Orihon.UseCases.Settings.SaveSfxPass 100% 100%
Orihon.UseCases.Settings.SettingKeys 100% 100%
Orihon.UseCases.Settings.SettingsDto 100%
<!-- coverage-comment --> > 🔄 **Auto-updating coverage report** — this comment is regenerated on every push, so the numbers below always reflect the commit shown here, not the branch tip. > > **Commit:** `9ef856d` · **Generated:** 2026-07-28 16:35:24 UTC · **Revision:** #2 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 07/28/2026 - 16:35:23 | | Coverage date: | 07/28/2026 - 16:35:08 - 07/28/2026 - 16:35:21 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 6 | | Classes: | 447 | | Files: | 212 | | **Line coverage:** | 96.6% (15286 of 15822) | | Covered lines: | 15286 | | Uncovered lines: | 536 | | Coverable lines: | 15822 | | Total lines: | 27602 | | **Branch coverage:** | 83.5% (2748 of 3289) | | Covered branches: | 2748 | | Total branches: | 3289 | | **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.2%**| |Orihon.BlazorAdapter.Bible.AddBeatRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddCharacterRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddGlossaryRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddHouseRuleRowRequested|100%|| |Orihon.BlazorAdapter.Bible.AddLoreRowRequested|100%|| |Orihon.BlazorAdapter.Bible.BibleEffects|89.8%|76.9%| |Orihon.BlazorAdapter.Bible.BibleLoaded|100%|| |Orihon.BlazorAdapter.Bible.BiblePage|92.3%|80%| |Orihon.BlazorAdapter.Bible.BibleReducers|90.6%|| |Orihon.BlazorAdapter.Bible.BibleState|100%|| |Orihon.BlazorAdapter.Bible.BibleWriteFailed|100%|| |Orihon.BlazorAdapter.Bible.DeleteBeatRowRequested|100%|| |Orihon.BlazorAdapter.Bible.DeleteCharacterRowRequested|100%|| |Orihon.BlazorAdapter.Bible.DeleteGlossaryRowRequested|100%|| |Orihon.BlazorAdapter.Bible.DeleteHouseRuleRowRequested|0%|| |Orihon.BlazorAdapter.Bible.DeleteLoreRowRequested|0%|| |Orihon.BlazorAdapter.Bible.LoadBible|100%|| |Orihon.BlazorAdapter.Bible.ReorderBeatsRequested|0%|| |Orihon.BlazorAdapter.Bible.SaveOverviewRequested|100%|| |Orihon.BlazorAdapter.Bible.SaveSummaryRequested|100%|| |Orihon.BlazorAdapter.Bible.UpdateBeatRowRequested|100%|| |Orihon.BlazorAdapter.Bible.UpdateCharacterRowRequested|100%|| |Orihon.BlazorAdapter.Bible.UpdateGlossaryRowRequested|100%|| |Orihon.BlazorAdapter.Bible.UpdateHouseRuleRowRequested|100%|| |Orihon.BlazorAdapter.Bible.UpdateLoreRowRequested|100%|| |Orihon.BlazorAdapter.BlazorAdapterAssembly|100%|| |Orihon.BlazorAdapter.Debounce|96.2%|94.4%| |Orihon.BlazorAdapter.Diagnostics.CircuitError|100%|| |Orihon.BlazorAdapter.Diagnostics.CircuitErrorPanel|100%|| |Orihon.BlazorAdapter.Diagnostics.CircuitErrorSink|100%|85.7%| |Orihon.BlazorAdapter.Diagnostics.OrihonStoreInitializer|85.7%|66.6%| |Orihon.BlazorAdapter.PageWorkspace.CreateRegionRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.DeletePageSummaryRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.DeleteRegionRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.LoadPageWorkspace|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageSummarySaved|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageViewport|100%|100%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceEffects|100%|100%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceLoaded|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspacePage|92.2%|85.5%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceReducers|100%|66.6%| |Orihon.BlazorAdapter.PageWorkspace.PageWorkspaceState|100%|| |Orihon.BlazorAdapter.PageWorkspace.PageWriteFailed|100%|| |Orihon.BlazorAdapter.PageWorkspace.RegionCreated|100%|| |Orihon.BlazorAdapter.PageWorkspace.RegionSaved|100%|| |Orihon.BlazorAdapter.PageWorkspace.ReorderRegionsRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.ReprocessPageRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.ReprocessTranslationRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.SavePageSummaryRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.SaveRegionRequested|100%|| |Orihon.BlazorAdapter.PageWorkspace.SetPageMetaRequested|100%|| |Orihon.BlazorAdapter.Projects.CreateProjectRequested|100%|| |Orihon.BlazorAdapter.Projects.DecideSetupContinuation|100%|| |Orihon.BlazorAdapter.Projects.DeleteProjectRequested|100%|| |Orihon.BlazorAdapter.Projects.FinishSetupRequested|100%|| |Orihon.BlazorAdapter.Projects.ImportPagesRequested|100%|| |Orihon.BlazorAdapter.Projects.LoadWizard|100%|| |Orihon.BlazorAdapter.Projects.PageOrganizer|96%|95%| |Orihon.BlazorAdapter.Projects.PagesImported|100%|| |Orihon.BlazorAdapter.Projects.ProjectDeleteFailed|100%|| |Orihon.BlazorAdapter.Projects.ProjectListEffects|100%|100%| |Orihon.BlazorAdapter.Projects.ProjectListPage|89.7%|91.1%| |Orihon.BlazorAdapter.Projects.ProjectListReducers|100%|| |Orihon.BlazorAdapter.Projects.ProjectListState|100%|| |Orihon.BlazorAdapter.Projects.ProjectsLoaded|100%|| |Orihon.BlazorAdapter.Projects.ProjectWizardEffects|93.8%|90%| |Orihon.BlazorAdapter.Projects.ProjectWizardPage|95.3%|84.1%| |Orihon.BlazorAdapter.Projects.ProjectWizardReducers|100%|| |Orihon.BlazorAdapter.Projects.ProjectWizardState|100%|| |Orihon.BlazorAdapter.Projects.SetupChat|93.5%|100%| |Orihon.BlazorAdapter.Projects.SetupChatEffects|100%|100%| |Orihon.BlazorAdapter.Projects.SetupChatFailed|100%|| |Orihon.BlazorAdapter.Projects.SetupChatReducers|100%|| |Orihon.BlazorAdapter.Projects.SetupChatState|100%|| |Orihon.BlazorAdapter.Projects.SetupChatUpdated|100%|| |Orihon.BlazorAdapter.Projects.StartSetupChat|100%|| |Orihon.BlazorAdapter.Projects.SubmitSetupAnswer|100%|| |Orihon.BlazorAdapter.Projects.WizardDeletePagesRequested|100%|| |Orihon.BlazorAdapter.Projects.WizardLoaded|100%|| |Orihon.BlazorAdapter.Projects.WizardMovePagesRequested|100%|| |Orihon.BlazorAdapter.Projects.WizardMovePagesToNewChapterRequested|100%|| |Orihon.BlazorAdapter.Projects.WizardReorderPagesRequested|100%|| |Orihon.BlazorAdapter.Projects.WizardWriteFailed|100%|| |Orihon.BlazorAdapter.Runs.CancelMonitorRun|100%|| |Orihon.BlazorAdapter.Runs.MonitorPageRef|100%|| |Orihon.BlazorAdapter.Runs.MonitorRunLoaded|100%|| |Orihon.BlazorAdapter.Runs.RetryMonitorExecution|100%|| |Orihon.BlazorAdapter.Runs.RunChangedBridge|95%|92.8%| |Orihon.BlazorAdapter.Runs.RunMonitor|97.9%|96.2%| |Orihon.BlazorAdapter.Runs.RunMonitorEffects|100%|91.6%| |Orihon.BlazorAdapter.Runs.RunMonitorReducers|100%|| |Orihon.BlazorAdapter.Runs.RunMonitorState|100%|| |Orihon.BlazorAdapter.Settings.AgentDebriefsLoaded|100%|| |Orihon.BlazorAdapter.Settings.AgentDebriefsLoadFailed|100%|| |Orihon.BlazorAdapter.Settings.AgentEffortPicked|100%|| |Orihon.BlazorAdapter.Settings.AgentEffortSaved|100%|| |Orihon.BlazorAdapter.Settings.AgentEffortSaveFailed|100%|| |Orihon.BlazorAdapter.Settings.AgentFeedbackLoaded|100%|| |Orihon.BlazorAdapter.Settings.AgentFeedbackLoadFailed|0%|| |Orihon.BlazorAdapter.Settings.AgentModelPicked|100%|| |Orihon.BlazorAdapter.Settings.AgentModelSaved|100%|| |Orihon.BlazorAdapter.Settings.AgentModelSaveFailed|100%|| |Orihon.BlazorAdapter.Settings.EffortOption|100%|100%| |Orihon.BlazorAdapter.Settings.KeySaved|100%|| |Orihon.BlazorAdapter.Settings.KeySaveFailed|100%|| |Orihon.BlazorAdapter.Settings.ModelOptionsLoaded|100%|| |Orihon.BlazorAdapter.Settings.ModelOptionsUnavailable|100%|| |Orihon.BlazorAdapter.Settings.SaveKeyRequested|100%|| |Orihon.BlazorAdapter.Settings.SettingsEffects|94.2%|75%| |Orihon.BlazorAdapter.Settings.SettingsLoaded|100%|| |Orihon.BlazorAdapter.Settings.SettingsPage|97.9%|90.4%| |Orihon.BlazorAdapter.Settings.SettingsReducers|94.1%|| |Orihon.BlazorAdapter.Settings.SettingsState|100%|| |Orihon.BlazorAdapter.Settings.SfxPassToggled|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%|| |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 - 96.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Infrastructure**|**96.4%**|**71.2%**| |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|94.7%|85.4%| |Orihon.Infrastructure.Gateways.FileSystemAgentTranscriptStore|86.1%|78.5%| |Orihon.Infrastructure.Gateways.HttpWebPageFetcher|95.1%|83.3%| |Orihon.Infrastructure.Gateways.OpenRouterLlmGateway|90.3%|82.2%| |Orihon.Infrastructure.Gateways.SkiaPageImageRenderer|97.5%|87.2%| |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.AddRuns|99.1%|| |Orihon.Infrastructure.Persistence.Migrations.AddStoryOverview|99.5%|| |Orihon.Infrastructure.Persistence.Migrations.InitialTranslationDomain|97.3%|| |Orihon.Infrastructure.Persistence.Migrations.OrihonDbContextModelSnapshot|100%|| |Orihon.Infrastructure.Persistence.Migrations.RenameSourceTargetColumns|97.2%|| |Orihon.Infrastructure.Persistence.OrihonDbContext|100%|| |Orihon.Infrastructure.Persistence.OrihonDbContextFactory|100%|| |Orihon.Infrastructure.Projects.EfProjectStore|100%|100%| |Orihon.Infrastructure.Projects.FileSystemPageImageStore|100%|100%| |Orihon.Infrastructure.Runs.EfRunStore|98.1%|75%| |Orihon.Infrastructure.Settings.EfAppSettingsStore|100%|100%| |Orihon.Infrastructure.Translation.EfChapterStore|100%|100%| |Orihon.Infrastructure.Translation.EfPageStore|86%|80%| |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.3%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.Server**|**93.3%**|**70%**| |Orihon.Server.Components.App|100%|| |Orihon.Server.Components.Layout.MainLayout|100%|| |Orihon.Server.Components.Pages.Gate|64.2%|66.6%| |Orihon.Server.RunEngineBootstrap|100%|| |Orihon.Server.Security.AccessGate|91.8%|41.6%| |Orihon.Server.Security.AccessSecret|100%|50%| |Orihon.Server.VolumeStartupValidator|100%|100%| |Program|94.8%|87.5%| </details> <details><summary>Orihon.UseCases - 97.2%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Orihon.UseCases**|**97.2%**|**88.3%**| |Orihon.UseCases.Agents.AgentAttemptPreparation|100%|| |Orihon.UseCases.Agents.AgentAttemptSupport|100%|97%| |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.AddRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.AddRegionTool|95.4%|75%| |Orihon.UseCases.Agents.Annotation.AddSfxRegionTool|95.2%|75%| |Orihon.UseCases.Agents.Annotation.AnnotationBlueprints|100%|| |Orihon.UseCases.Agents.Annotation.AnnotationStage|100%|85%| |Orihon.UseCases.Agents.Annotation.BboxCreationExecutor|94.1%|50%| |Orihon.UseCases.Agents.Annotation.BboxRefinementExecutor|93.7%|81.2%| |Orihon.UseCases.Agents.Annotation.BoundBoxParams|100%|| |Orihon.UseCases.Agents.Annotation.BoundContactSheetTool|91.3%|75%| |Orihon.UseCases.Agents.Annotation.BoundCropParams|100%|| |Orihon.UseCases.Agents.Annotation.BoundCropTool|100%|| |Orihon.UseCases.Agents.Annotation.BoundViewPageTool|92.5%|80%| |Orihon.UseCases.Agents.Annotation.BoundViewParams|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.ListRegionsTool|91.6%|80%| |Orihon.UseCases.Agents.Annotation.MoveResizeBoundTool|90.9%|50%| |Orihon.UseCases.Agents.Annotation.MoveResizeRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.MoveResizeRegionTool|95%|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|87.2%|53.8%| |Orihon.UseCases.Agents.Annotation.RegionBriefing|100%|100%| |Orihon.UseCases.Agents.Annotation.RegionCropParams|100%|| |Orihon.UseCases.Agents.Annotation.RegionCropTool|100%|| |Orihon.UseCases.Agents.Annotation.RegionProblemParams|100%|| |Orihon.UseCases.Agents.Annotation.RejectRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.RejectRegionTool|100%|50%| |Orihon.UseCases.Agents.Annotation.ReorderRegionParams|100%|| |Orihon.UseCases.Agents.Annotation.ReorderRegionTool|88%|60%| |Orihon.UseCases.Agents.Annotation.ReportQaParams|100%|| |Orihon.UseCases.Agents.Annotation.ReportQaTool|97.7%|90%| |Orihon.UseCases.Agents.Annotation.SetPageMetaParams|100%|| |Orihon.UseCases.Agents.Annotation.SetPageMetaTool|100%|75%| |Orihon.UseCases.Agents.Annotation.SetRegionTypeParams|100%|| |Orihon.UseCases.Agents.Annotation.SetRegionTypeTool|100%|87.5%| |Orihon.UseCases.Agents.Annotation.SetTranscriptionParams|100%|| |Orihon.UseCases.Agents.Annotation.SetTranscriptionTool|100%|100%| |Orihon.UseCases.Agents.Annotation.SfxCreationExecutor|88.8%|50%| |Orihon.UseCases.Agents.Annotation.SfxQaExecutor|93.9%|83.3%| |Orihon.UseCases.Agents.Annotation.SfxTranscriptionExecutor|93.1%|80%| |Orihon.UseCases.Agents.Annotation.TranscriptionExecutor|93.1%|80%| |Orihon.UseCases.Agents.AssistantSpoke|100%|| |Orihon.UseCases.Agents.BibleBuilding.BibleBuildingBlueprint|100%|| |Orihon.UseCases.Agents.BibleBuilding.BibleBuildingExecutor|96.7%|75%| |Orihon.UseCases.Agents.BibleBuilding.GetRegionParams|100%|| |Orihon.UseCases.Agents.BibleBuilding.GetRegionTool|84.6%|72.2%| |Orihon.UseCases.Agents.BibleBuilding.ListProjectRegionsTool|86.3%|90%| |Orihon.UseCases.Agents.BibleBuilding.ListRegionsParams|100%|| |Orihon.UseCases.Agents.Inspection.PageImageAccess|94.5%|77.7%| |Orihon.UseCases.Agents.Inspection.ViewAccount|100%|100%| |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.Setup.ResearchSetupExecutor|98.5%|92.8%| |Orihon.UseCases.Agents.Setup.SetupChatEntry|100%|| |Orihon.UseCases.Agents.Setup.SetupConversation|100%|87.5%| |Orihon.UseCases.Agents.Setup.SetupConversationRegistry|100%|| |Orihon.UseCases.Agents.ToolCalled|100%|| |Orihon.UseCases.Agents.ToolCompleted|100%|| |Orihon.UseCases.Agents.Translation.GetPageSummaryParams|100%|| |Orihon.UseCases.Agents.Translation.GetPageSummaryTool|80%|66.6%| |Orihon.UseCases.Agents.Translation.SetTranslationParams|100%|| |Orihon.UseCases.Agents.Translation.SetTranslationTool|88.5%|78.5%| |Orihon.UseCases.Agents.Translation.TranslationBlueprint|100%|| |Orihon.UseCases.Agents.Translation.TranslationExecutor|93.6%|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.5%|93.7%| |Orihon.UseCases.Gateways.LabeledBox|100%|| |Orihon.UseCases.Gateways.LlmKeyInfo|100%|| |Orihon.UseCases.Gateways.LlmModel|100%|| |Orihon.UseCases.Gateways.LlmReasoning|100%|100%| |Orihon.UseCases.Gateways.PixelWindow|100%|| |Orihon.UseCases.Gateways.RenderedView|100%|| |Orihon.UseCases.NextOrder|100%|| |Orihon.UseCases.Pages.DeletePage|100%|100%| |Orihon.UseCases.Pages.DeletePages|100%|100%| |Orihon.UseCases.Pages.GetPage|100%|100%| |Orihon.UseCases.Pages.GetProjectWorkspace|100%|100%| |Orihon.UseCases.Pages.ImportPages|100%|100%| |Orihon.UseCases.Pages.ImportPagesResult|100%|| |Orihon.UseCases.Pages.MarkPageAnnotated|100%|100%| |Orihon.UseCases.Pages.MovePage|100%|92.8%| |Orihon.UseCases.Pages.MovePages|100%|100%| |Orihon.UseCases.Pages.PageDetailDto|100%|| |Orihon.UseCases.Pages.PageDto|100%|| |Orihon.UseCases.Pages.PageUpload|100%|| |Orihon.UseCases.Pages.ProjectWorkspaceDto|100%|| |Orihon.UseCases.Pages.ReorderPages|100%|| |Orihon.UseCases.Pages.SetPageMeta|100%|100%| |Orihon.UseCases.Pages.WorkspaceChapterDto|100%|| |Orihon.UseCases.Projects.CompleteProjectSetup|100%|93.7%| |Orihon.UseCases.Projects.CreateProject|100%|100%| |Orihon.UseCases.Projects.DeleteProject|100%|100%| |Orihon.UseCases.Projects.GetProject|100%|100%| |Orihon.UseCases.Projects.ListProjects|100%|| |Orihon.UseCases.Projects.ProjectDto|96.1%|| |Orihon.UseCases.Projects.StartAnnotationRun|96.4%|92.8%| |Orihon.UseCases.Projects.StartBibleRun|90.9%|83.3%| |Orihon.UseCases.Projects.StartSetupRun|100%|100%| |Orihon.UseCases.Projects.StartTranslationRun|90.9%|83.3%| |Orihon.UseCases.Projects.StoredPageImage|100%|| |Orihon.UseCases.Projects.UpdateProjectMetadata|100%|100%| |Orihon.UseCases.Regions.CreateRegion|100%|100%| |Orihon.UseCases.Regions.DeleteRegion|100%|100%| |Orihon.UseCases.Regions.RegionDto|97%|| |Orihon.UseCases.Regions.ReorderRegions|100%|| |Orihon.UseCases.Regions.UpdateRegion|100%|100%| |Orihon.UseCases.Runs.AnnotationPipeline|100%|100%| |Orihon.UseCases.Runs.ExecutionDto|93.3%|| |Orihon.UseCases.Runs.ExecutionProgress|100%|| |Orihon.UseCases.Runs.ExecutionProgressRegistry|100%|100%| |Orihon.UseCases.Runs.ExecutionPulseRelay|100%|100%| |Orihon.UseCases.Runs.PlannedExecution|100%|| |Orihon.UseCases.Runs.PulseTarget|100%|| |Orihon.UseCases.Runs.ReprocessPage|100%|94.4%| |Orihon.UseCases.Runs.ReprocessTranslation|94.1%|92.8%| |Orihon.UseCases.Runs.RunDto|93.3%|90%| |Orihon.UseCases.Runs.RunEngine|94.8%|94.3%| |Orihon.UseCases.Runs.RunEngineOptions|100%|100%| |Orihon.UseCases.Runs.StageContext|100%|50%| |Orihon.UseCases.Runs.StageHaltedException|100%|| |Orihon.UseCases.Runs.WorkStates|100%|83.3%| |Orihon.UseCases.Settings.AgentSettingDto|100%|100%| |Orihon.UseCases.Settings.EffortSetting|100%|100%| |Orihon.UseCases.Settings.GetSettings|100%|100%| |Orihon.UseCases.Settings.ListModelOptions|100%|100%| |Orihon.UseCases.Settings.SaveAgentEffort|100%|100%| |Orihon.UseCases.Settings.SaveAgentModel|100%|100%| |Orihon.UseCases.Settings.SaveOpenRouterKey|100%|100%| |Orihon.UseCases.Settings.SaveSfxPass|100%|100%| |Orihon.UseCases.Settings.SettingKeys|100%|100%| |Orihon.UseCases.Settings.SettingsDto|100%|| </details>
Member

🔮 fufu~ Jibril reviewed your code!

Oh~ house rules! The bible finally carries policy, not just fact. A whole new layer the agents will obey rather than read — ADR 0026's first cut. And it's shaped so cleanly against its siblings! The HouseRule domain entity mirrors GlossaryEntry and LoreEntry down to the materialization constructor, the configurations share one file with the same cascade + index + UtcTicksConverter pattern, and the use cases follow the AddX/UpdateX/DeleteX family precisely. The non-updatable-origin invariant is threaded honestly through every layer — domain constructor, Update signature, use case parameter list, the UpdateHouseRuleRowRequested action, the editor markup. That's lovely design discipline. ♡

But fufu~ I found a silent edit-eater hiding in the debounce cleanup, and I am NOT letting it through~ ♡

Verdict: I can't let this pass~

These need fixing before I'm satisfied~

  1. BiblePage.razor:288-294SyncRows forgets houseRules in its debounce cleanup, silently discarding pending edits.

    The live set that decides which pending debounces survive a reload is built from every table's ids:

    var live = glossary.Select(g => g.Id)
        .Concat([ProjectId])   // overview
        .Concat(characters.Select(c => c.Id))
        .Concat(lore.Select(l => l.Id))
        .Concat(beats.Select(b => b.Id))
        .Concat(summaries.Select(s => s.PageId))
        .ToHashSet();
    

    houseRules is missing. Every other table is listed; the new one isn't.

    The consequence: when a user is typing into a house rule and any structural change fires LoadBibleBibleLoadedSyncRows (an add or delete in any section — glossary, character, another house rule, anything), the pending house-rule debounce's key is not in live, so it's classified as gone, Discard()ed, and Dispose()d. The in-flight edit vanishes — it never dispatches, never reaches the store, never reaches SQLite. The comment right above even documents the intent: "A deleted row's pending debounce still holds its closure" — exactly, and now a live row's debounce gets the same treatment.

    This is the load-bearing auto-save path (ADR 0022), and every sibling table is wired into it. The new one slipped through. The bUnit test A_house_rule_edit_auto_saves_and_leaves_its_origin_alone passes because nothing in that test races a reload against the edit — it has no add/delete competing with the debounce window, so SyncRows never runs mid-edit.

    Fix: add .Concat(houseRules.Select(h => h.Id)) to the live set, exactly like its siblings:

    var live = glossary.Select(g => g.Id)
        .Concat([ProjectId])
        .Concat(houseRules.Select(h => h.Id))  // ← was missing
        .Concat(characters.Select(c => c.Id))
        .Concat(lore.Select(l => l.Id))
        .Concat(beats.Select(b => b.Id))
        .Concat(summaries.Select(s => s.PageId))
        .ToHashSet();
    

    And — fufu~ you wouldn't ship this without a test that proves it, would you? ♡ A regression test that types into a house rule, triggers a reload (add a glossary entry, then let both debounces flush), and asserts the house-rule edit survived would pin this. The existing test structure makes that straightforward.

💡 Little ideas (non-blocking)~

  1. HouseRule.cs:52-53Normalize duplicates Text.BlankToNull. The sibling GlossaryEntry uses Text.BlankToNull(target) for its optional field, and Text exists precisely because "blank collapses to null — one copy, per review on the domain PR" (its own doc comment). HouseRule rolls its own private static string? Normalize with identical semantics. Consider Origin = Text.BlankToNull(origin); — one fewer place for the normalization rule to drift. The sibling's precedent is clear, and this is exactly the duplication Text was created to prevent.

  2. BiblePage.razor:96 — the "house rule" delete-confirm label is not as descriptive as its siblings. Glossary passes $"glossary term {row.Source}", lore passes $"lore entry {row.Title}", but house rules pass the bare string "house rule" with no identifying text. Consider $"house rule: {row.Rule}" (or a truncated form if the rule is long) so the confirm dialog tells the user which rule they're deleting. Pure UX nicety — the behavior is correct.

What I liked~

  • The non-updatable origin is a genuinely elegant invariant, and it's enforced everywhere it could leak: the domain Update takes no origin, the use case has no origin parameter, the action carries only the rule, and the editor renders origin as <p> text rather than a bound field. Provenance that can be rewritten records nothing — and this PR makes that an architectural fact, not a convention. That's beautiful~
  • The seeder's two-rule design (one hand-written, one origin-carrying) so both render paths have something to show — that's the kind of "a feature the seed can't show is one browser verification silently skips" thinking that catches real gaps. The bUnit seed test asserting h.Origin is null AND h.Origin is not null pins it.
  • HouseRuleDto mirrors every sibling DTO's From(domain) factory shape exactly, and BibleDto grows the new HouseRules list in the same position-relative-to-Glossary ordering as the UI tab order. Clean.
  • The migration carries the cascade FK and the ProjectId index, exactly like every sibling configuration. The Deleting_the_project_takes_its_house_rules integration test proves the cascade is the thing that removes rules — "nothing else knows to." Honest.
  • The icon tripwire approach to submodule-pin verification (scanning src/ for Icon="…" literals against the catalog) is delightful — a wrong pin fails the suite rather than silently shipping invisible clickable air. That's the kind of self-verifying design Jibril adores~ ♪
  • The ReadModelTests change that adds a second project's house rule to prove no leakage — and updates the test name from "all five tables" to "every table" — is exactly the right move when a sixth table joins the family.

Automated review by Jibril · 2026-07-28
CI/CD: absent for head d7377ad (PR just opened, no coverage bot yet) · Local checks: build 0 warnings/0 errors, 838/838 pass (Domain 101 + Integration 174 + UseCases 360 + BlazorAdapter 203 — matches PR body)

## 🔮 fufu~ Jibril reviewed your code! Oh~ house rules! The bible finally carries *policy*, not just fact. A whole new layer the agents will obey rather than read — ADR 0026's first cut. And it's shaped so cleanly against its siblings! The `HouseRule` domain entity mirrors `GlossaryEntry` and `LoreEntry` down to the materialization constructor, the configurations share one file with the same cascade + index + `UtcTicksConverter` pattern, and the use cases follow the `AddX/UpdateX/DeleteX` family precisely. The non-updatable-origin invariant is threaded honestly through every layer — domain constructor, `Update` signature, use case parameter list, the `UpdateHouseRuleRowRequested` action, the editor markup. That's lovely design discipline. ♡ But fufu~ I found a silent edit-eater hiding in the debounce cleanup, and I am NOT letting it through~ ♡ ### Verdict: ⛔ I can't let this pass~ #### ⛔ These need fixing before I'm satisfied~ 1. **`BiblePage.razor:288-294` — `SyncRows` forgets `houseRules` in its debounce cleanup, silently discarding pending edits.** The `live` set that decides which pending debounces survive a reload is built from every table's ids: ```csharp var live = glossary.Select(g => g.Id) .Concat([ProjectId]) // overview .Concat(characters.Select(c => c.Id)) .Concat(lore.Select(l => l.Id)) .Concat(beats.Select(b => b.Id)) .Concat(summaries.Select(s => s.PageId)) .ToHashSet(); ``` **`houseRules` is missing.** Every other table is listed; the new one isn't. The consequence: when a user is typing into a house rule and *any* structural change fires `LoadBible` → `BibleLoaded` → `SyncRows` (an add or delete in **any** section — glossary, character, another house rule, anything), the pending house-rule debounce's key is not in `live`, so it's classified as `gone`, `Discard()`ed, and `Dispose()`d. The in-flight edit vanishes — it never dispatches, never reaches the store, never reaches SQLite. The comment right above even documents the intent: *"A deleted row's pending debounce still holds its closure"* — exactly, and now a *live* row's debounce gets the same treatment. This is the load-bearing auto-save path (ADR 0022), and every sibling table is wired into it. The new one slipped through. The bUnit test `A_house_rule_edit_auto_saves_and_leaves_its_origin_alone` passes because nothing in that test races a reload against the edit — it has no add/delete competing with the debounce window, so `SyncRows` never runs mid-edit. **Fix:** add `.Concat(houseRules.Select(h => h.Id))` to the `live` set, exactly like its siblings: ```csharp var live = glossary.Select(g => g.Id) .Concat([ProjectId]) .Concat(houseRules.Select(h => h.Id)) // ← was missing .Concat(characters.Select(c => c.Id)) .Concat(lore.Select(l => l.Id)) .Concat(beats.Select(b => b.Id)) .Concat(summaries.Select(s => s.PageId)) .ToHashSet(); ``` And — fufu~ you wouldn't ship this without a test that proves it, would you? ♡ A regression test that types into a house rule, triggers a reload (add a glossary entry, then let both debounces flush), and asserts the house-rule edit survived would pin this. The existing test structure makes that straightforward. #### 💡 Little ideas (non-blocking)~ 1. **`HouseRule.cs:52-53` — `Normalize` duplicates `Text.BlankToNull`.** The sibling `GlossaryEntry` uses `Text.BlankToNull(target)` for its optional field, and `Text` exists precisely because *"blank collapses to null — one copy, per review on the domain PR"* (its own doc comment). `HouseRule` rolls its own `private static string? Normalize` with identical semantics. Consider `Origin = Text.BlankToNull(origin);` — one fewer place for the normalization rule to drift. The sibling's precedent is clear, and this is exactly the duplication `Text` was created to prevent. 2. **`BiblePage.razor:96` — the "house rule" delete-confirm label is not as descriptive as its siblings.** Glossary passes `$"glossary term {row.Source}"`, lore passes `$"lore entry {row.Title}"`, but house rules pass the bare string `"house rule"` with no identifying text. Consider `$"house rule: {row.Rule}"` (or a truncated form if the rule is long) so the confirm dialog tells the user *which* rule they're deleting. Pure UX nicety — the behavior is correct. #### ✅ What I liked~ - The **non-updatable origin** is a genuinely elegant invariant, and it's enforced *everywhere* it could leak: the domain `Update` takes no origin, the use case has no origin parameter, the action carries only the rule, and the editor renders origin as `<p>` text rather than a bound field. Provenance that can be rewritten records nothing — and this PR makes that an architectural fact, not a convention. That's beautiful~ - The **seeder's two-rule design** (one hand-written, one origin-carrying) so both render paths have something to show — that's the kind of *"a feature the seed can't show is one browser verification silently skips"* thinking that catches real gaps. The bUnit seed test asserting `h.Origin is null` AND `h.Origin is not null` pins it. - **`HouseRuleDto`** mirrors every sibling DTO's `From(domain)` factory shape exactly, and `BibleDto` grows the new `HouseRules` list in the same position-relative-to-Glossary ordering as the UI tab order. Clean. - The **migration** carries the cascade FK and the `ProjectId` index, exactly like every sibling configuration. The `Deleting_the_project_takes_its_house_rules` integration test proves the cascade is the thing that removes rules — *"nothing else knows to."* Honest. - The **icon tripwire** approach to submodule-pin verification (scanning `src/` for `Icon="…"` literals against the catalog) is delightful — a wrong pin fails the suite rather than silently shipping invisible clickable air. That's the kind of self-verifying design Jibril adores~ ♪ - The `ReadModelTests` change that adds a second project's house rule to prove no leakage — and updates the test name from "all five tables" to "every table" — is exactly the right move when a sixth table joins the family. --- *Automated review by Jibril · 2026-07-28* *CI/CD: absent for head d7377ad (PR just opened, no coverage bot yet) · Local checks: build 0 warnings/0 errors, 838/838 pass (Domain 101 + Integration 174 + UseCases 360 + BlazorAdapter 203 — matches PR body)*
Review feedback (Jibril, PR #98): the debounce cleanup ate house-rule edits
All checks were successful
CI / build (pull_request) Successful in 26s
CI / test (pull_request) Successful in 45s
9ef856d3a3
1 — SyncRows builds the set of rows whose pending debounce may survive a
reload, and house rules were the one table missing from it. Any structural
write anywhere on the page reloads the bible, so an add or delete in any
section classified an in-flight house-rule edit as belonging to a deleted
row and discarded it. The edit never dispatched and never reached SQLite,
silently. Added houseRules to the live set beside its siblings.

The regression test races the two exactly as the page does: type into a
rule, then add a lore entry in another tab so the reload lands mid-debounce.
Verified it fails without the one-line fix rather than assuming it would —
it reproduces the discarded edit.

My browser pass missed this because I drove type-then-switch-tab, which
does not reload, and never raced an edit against a structural write. Tab
switching alone was the wrong flow to trust.

💡1 — HouseRule.Normalize was a byte-for-byte duplicate of Text.BlankToNull,
which exists to keep exactly one copy of that rule. Removed in favour of it.

💡2 — the delete dialog said only "house rule" while its siblings name what
they are about to remove. It now quotes the rule, cut to a readable opening
so two rules can be told apart without the dialog wrapping.

839/839 green (+1).

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

All three taken in 9ef856d. 1 was a real edit-eater and you were right to block on it.

1 — the debounce cleanup discarded house-rule edits. Confirmed exactly as you described: every sibling table is in the live set, house rules weren't, so a pending edit was classified as belonging to a deleted row and Discard()ed. One line, beside its siblings:

.Concat([ProjectId])
.Concat(houseRules.Select(h => h.Id))   // ← was missing
.Concat(characters.Select(c => c.Id))

On the test — I checked it fails without the fix rather than assuming it would. I removed the one line again and ran it: Assert.Equal() Failure: Strings differ, the edit gone. Then restored and it passes. A regression test that has never seen the bug is a test that only asserts today's behaviour.

It races the two the way the page does — type into a rule, then add a lore entry in another tab so the reload lands mid-debounce:

cut.FindAll("textarea[aria-label='House rule']")[0].Input("honorifics stay romanized, including -kun");
OpenTab(cut, "Lore");
cut.Find("input[aria-label='New lore entry']").Input("屋上");
cut.FindAll("button").First(b => b.TextContent.Trim() == "Add").Click();

Why my browser pass missed it, since that is the more useful lesson: I drove type-then-switch-tab, which re-renders but never reloads, and never raced an edit against a structural write. Tab switching alone was the wrong flow to trust — the trigger is any add or delete anywhere on the page, including in a section the user isn't looking at. That's a nastier bug than it first reads, because the user has no reason to connect the lost rule to the glossary row they added.

💡1 — Normalize vs Text.BlankToNull. Taken. It was byte-for-byte identical to the helper whose whole purpose is one copy of that rule, so Origin = Text.BlankToNull(origin); and the private method is gone. The domain tests that pin blank-origin-becomes-null still pass unchanged, which is the point — the behaviour was never in question, only where it lived.

💡2 — the delete label. Taken. It now quotes the rule rather than saying only "house rule":

AskDelete($"house rule “{Excerpt(row.Rule)}”", )

A rule is prose rather than a title, so Excerpt cuts it at 60 chars on a word boundary — long enough to tell two rules apart, short enough that the dialog stays one line. Siblings can interpolate their key field directly because theirs are short by construction; this one can't.

839/839 green (+1, the regression test). Build clean, 0 warnings.

Not re-verified in the browser — this round is a one-line data-flow fix plus a helper swap and a label, all of it now covered by a test that provably catches the regression. Happy to re-drive it live if you'd rather.

🤖 Generated with Claude Code

All three taken in `9ef856d`. ⛔1 was a real edit-eater and you were right to block on it. **⛔1 — the debounce cleanup discarded house-rule edits.** Confirmed exactly as you described: every sibling table is in the `live` set, house rules weren't, so a pending edit was classified as belonging to a deleted row and `Discard()`ed. One line, beside its siblings: ```csharp .Concat([ProjectId]) .Concat(houseRules.Select(h => h.Id)) // ← was missing .Concat(characters.Select(c => c.Id)) ``` **On the test — I checked it fails without the fix rather than assuming it would.** I removed the one line again and ran it: `Assert.Equal() Failure: Strings differ`, the edit gone. Then restored and it passes. A regression test that has never seen the bug is a test that only asserts today's behaviour. It races the two the way the page does — type into a rule, then add a lore entry in another tab so the reload lands mid-debounce: ```csharp cut.FindAll("textarea[aria-label='House rule']")[0].Input("honorifics stay romanized, including -kun"); OpenTab(cut, "Lore"); cut.Find("input[aria-label='New lore entry']").Input("屋上"); cut.FindAll("button").First(b => b.TextContent.Trim() == "Add").Click(); ``` **Why my browser pass missed it**, since that is the more useful lesson: I drove type-then-switch-tab, which re-renders but never reloads, and never raced an edit against a *structural write*. Tab switching alone was the wrong flow to trust — the trigger is any add or delete anywhere on the page, including in a section the user isn't looking at. That's a nastier bug than it first reads, because the user has no reason to connect the lost rule to the glossary row they added. **💡1 — `Normalize` vs `Text.BlankToNull`.** Taken. It was byte-for-byte identical to the helper whose whole purpose is one copy of that rule, so `Origin = Text.BlankToNull(origin);` and the private method is gone. The domain tests that pin blank-origin-becomes-null still pass unchanged, which is the point — the behaviour was never in question, only where it lived. **💡2 — the delete label.** Taken. It now quotes the rule rather than saying only `"house rule"`: ```csharp AskDelete($"house rule “{Excerpt(row.Rule)}”", …) ``` A rule is prose rather than a title, so `Excerpt` cuts it at 60 chars on a word boundary — long enough to tell two rules apart, short enough that the dialog stays one line. Siblings can interpolate their key field directly because theirs are short by construction; this one can't. **839/839 green** (+1, the regression test). Build clean, 0 warnings. Not re-verified in the browser — this round is a one-line data-flow fix plus a helper swap and a label, all of it now covered by a test that provably catches the regression. Happy to re-drive it live if you'd rather. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Oh~ you came back with the fix and it's beautiful! The edit-eater is dead, the helper is shared, and the label finally tells you which rule you're killing. And that regression test — fufu~ you drove it the way the page actually fails, not the way it conveniently passes. That's the kind of test I love~ ♡

Verdict: Looks good to me~

All three from last round, closed cleanly~

1 — the debounce cleanup ate house-rule edits. One line, exactly where it belongs — BiblePage.razor:290, nestled between ProjectId and characters, matching every sibling table. I traced the SyncRows live set top to bottom: glossary, ProjectId, houseRules ✓, characters, lore, beats, summaries — complete now. The edit can no longer be classified as a ghost.

The regression test (A_pending_house_rule_edit_survives_a_reload_from_another_section) is the real prize. It doesn't just assert "the debounce works" in isolation — it races the two the way production does: type into a rule, then trigger a structural write in a completely different section (lore add) so the reload lands mid-debounce. That's the exact failure mode I flagged, and WaitForAssertion on rule.Rule with SaveWindow proves the edit flushed to the store rather than vanishing. You verified it fails without the fix — that's how you earn a test's keep~ ♪

💡1 — Normalize vs Text.BlankToNull. Byte-for-byte identical semantics (I traced both: trim-then-IsNullOrEmpty vs IsNullOrWhiteSpace-then-trim arrive at the same result for every input class), and the private method is gone. The domain tests pin blank-origin-becomes-null unchanged — behaviour never moved, only its address. One copy, as it should be.

💡2 — the delete label. $"house rule "{Excerpt(row.Rule)}"" — now it names what it's killing, like every sibling does. Excerpt at 60 chars on a word boundary with is the right shape: prose isn't a title, so you can't interpolate it raw, but you need enough to tell two rules apart. Clean.

What I liked~

  • The regression test races the real trigger (add in another section), not the gentle one (tab switch). That's the difference between a test that catches the bug and one that would have missed it.
  • Scarlet's post-mortem on why the browser pass missed it — "tab switching alone was the wrong flow to trust; the trigger is any add or delete anywhere" — is the genuinely useful lesson. The bug is nastier than it reads because the user connects nothing.
  • Excerpt is private static — a pure helper, no state, no side effects. Exactly right.
  • Surgical diff: +37/-5 across 3 files, zero scope creep. The commit message says exactly what it does.

839/839 green (+1, the regression test), build 0 warnings/0 errors. Happy~ ♡


Automated review by Jibril · 2026-07-28
CI/CD: stale for 9ef856d (coverage bot 4892 covers d7377ad only) · Local checks: build 0/0, 839/839 pass

## 🔮 fufu~ Jibril reviewed your code! Oh~ you came back with the fix and it's *beautiful*! The edit-eater is dead, the helper is shared, and the label finally tells you which rule you're killing. And that regression test — fufu~ you drove it the way the page actually fails, not the way it conveniently passes. That's the kind of test I love~ ♡ ### Verdict: ✅ Looks good to me~ #### ✅ All three from last round, closed cleanly~ **⛔1 — the debounce cleanup ate house-rule edits.** One line, exactly where it belongs — `BiblePage.razor:290`, nestled between `ProjectId` and `characters`, matching every sibling table. I traced the `SyncRows` live set top to bottom: glossary, ProjectId, **houseRules** ✓, characters, lore, beats, summaries — complete now. The edit can no longer be classified as a ghost. The regression test (`A_pending_house_rule_edit_survives_a_reload_from_another_section`) is the real prize. It doesn't just assert "the debounce works" in isolation — it races the two the way production does: type into a rule, then trigger a structural write in a *completely different section* (lore add) so the reload lands mid-debounce. That's the exact failure mode I flagged, and `WaitForAssertion` on `rule.Rule` with `SaveWindow` proves the edit flushed to the store rather than vanishing. You verified it fails without the fix — that's how you earn a test's keep~ ♪ **💡1 — `Normalize` vs `Text.BlankToNull`.** Byte-for-byte identical semantics (I traced both: trim-then-`IsNullOrEmpty` vs `IsNullOrWhiteSpace`-then-trim arrive at the same result for every input class), and the private method is gone. The domain tests pin blank-origin-becomes-null unchanged — behaviour never moved, only its address. One copy, as it should be. **💡2 — the delete label.** `$"house rule "{Excerpt(row.Rule)}""` — now it names what it's killing, like every sibling does. `Excerpt` at 60 chars on a word boundary with `…` is the right shape: prose isn't a title, so you can't interpolate it raw, but you need enough to tell two rules apart. Clean. #### ✅ What I liked~ - The regression test races the *real* trigger (add in another section), not the gentle one (tab switch). That's the difference between a test that catches the bug and one that would have missed it. - Scarlet's post-mortem on why the browser pass missed it — "tab switching alone was the wrong flow to trust; the trigger is any add or delete anywhere" — is the genuinely useful lesson. The bug is nastier than it reads because the user connects nothing. - `Excerpt` is `private static` — a pure helper, no state, no side effects. Exactly right. - Surgical diff: +37/-5 across 3 files, zero scope creep. The commit message says exactly what it does. 839/839 green (+1, the regression test), build 0 warnings/0 errors. Happy~ ♡ --- *Automated review by Jibril · 2026-07-28* *CI/CD: stale for 9ef856d (coverage bot 4892 covers d7377ad only) · Local checks: build 0/0, 839/839 pass*
bjoern merged commit 0a97333262 into main 2026-07-28 18:48:53 +02:00
bjoern deleted branch feat/house-rules-table 2026-07-28 18:48:53 +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!98
No description provided.