feat(editor): the manuscript surface module on its harness page (editor track M2a) #37

Merged
bjoern merged 3 commits from feat/manuscript-surface into main 2026-09-26 03:14:34 +02:00
Member

Editor track slice M2a (ADR 0030, docs/PROJECT_PLAN.md). M2 is split in two:

  • M2a (this PR) builds and proves the ProseMirror surface on its own, on a harness page against a fake host.
  • M2b will wire it into the chapter page, with the amended use cases, the markup migration and the Razor host.

No app change here: nothing loads the module yet, and dotnet build does not build it until M2b.

The surface (src/Tsumugi.BlazorAdapter/ManuscriptEditor/src/)

  • Schema: paragraph and scene-break blocks, the marks strong and em, and line breaks. A block's segment id lives in its attributes. The node view puts it on the DOM as data-segment-id; no toDOM writes it, so it never reaches the clipboard.
  • Ids (ids.ts, commands.ts):
    • The module mints UUIDv7 ids. A transaction hook keeps them unique by mapping: the owner keeps its id and a copy gets a fresh one.
    • Enter at a paragraph's start (a selection there is deleted first) puts the new block before it.
    • Backspace, Delete and the word/line delete keys keep the non-empty paragraph's id. They select a scene break before a second press removes it.
    • Pasted and dropped paragraphs arrive as whole blocks with fresh ids. A selection across paragraphs is cut around its ends; it is never joined.
    • A drag of whole paragraphs — a triple-click's text included — moves the same blocks to the nearest block boundary.
    • The browser's own paragraph and line-break input is cancelled and made by the editor's commands.
  • Events (derive.ts, session.ts, transport.ts) are derived from a before/after comparison of the block lists:
    • SegmentText on a paragraph's 700 ms pause (held while an IME composes), with its sequence number; Split; Merge; Reshape with its reason, the order it was based on and the new one.
    • Strictly in order, one at a time; pending typing is flushed first. Stamps are read when an event is sent.
    • An event over 24 KB goes in parts (EventPart / EventParts).
    • Each event is answered with the stored form of what it wrote; the stored provenance it carries redraws the paragraph's gutter mark. A structural event's answer also carries the stored order, which is laid onto the screen once nothing structural of the module's is on its way.
  • Failure modes:
    • A refusal is adopted. Unsaved typing survives where the server didn't change the paragraph, as does an empty chapter's first paragraph; the adoption is held while composing.
    • A failed call is answered by one resync reshape, retried only at the next change.
    • flush() resolves only once kept typing is saved too, and destroy() still sends typing.
  • Remote deltas (reconcile.ts, diff.ts):
    • Reconciled by id with token-level hunks; each CJK character is its own token.
    • The caret is kept as (segment, offset); deltas apply in order and are held while composing.
    • Typing and in-flight writes win their paragraph, and a block whose removal is in flight never comes back.
  • Around it (view.ts, surface.css):
    • The undo keys are claimed so the shell's hotkey stays the one undo path; the Edit menu's undo is reported as Undo() / Redo().
    • Every format* input is cancelled.
    • Also: Dirty(bool), the toolbar's scene break, the empty chapter's unsaved first paragraph, the provenance gutter (display only — the click and the working state are M4), and the ARIA attributes. The CSS uses Kagaku.UI tokens only.
  • Bundle: 71.8 KB gzipped, against the spike's measured ~67 KB.

Proof

  • vitest (168 tests): derivation, reconcile, ids, transport, conversion and the diff, plus the session against harness/fake-server.ts. The fake server models the host contract M2b's C# will implement: codec-trimmed texts, stamp checks, fresh ids, the base-order check with remotely added blocks woven in, and refusals answered with the chapter.
  • Properties: random editing leaves the server holding exactly the chapter on screen. The runs mix another tab editing, adding, removing and moving segments with refusals and failed calls. 1,000 runs in CI; 10,000 locally before this PR.
  • Playwright on the harness page: 88 checks in Chromium, Firefox and WebKit. The 5 skips are the Chromium-only CDP input-method tests, plus a text drag Playwright cannot start in Firefox. A new CI job editor-browsers runs it in mcr.microsoft.com/playwright:v1.63.0-noble, because WebKit does not start on the build host. I ran the same image locally with podman.
  • MANUAL-MATRIX.md covers real input methods (fcitx5-mozc), dead keys, spellcheck, the Edit menu, a Firefox drag, Safari and a screen reader, runnable on npm run harness.
  • Pre-PR review, three lenses (protocol, browser layer, tests/build/docs): 13 + 13 + 23 findings, all addressed. The last mutation round's 28 probes are all killed.
  • Review round 1: the answer's provenance redraw is pinned by two vitest cases, one for an edit and one for a split's new paragraph. Both fail with the redraw removed.

ADR 0030 amended where the build is more precise

  • Dirty has two edges.
  • Echoes are the events' answers, not seq-tagged deltas.
  • A reshape carries a base order: the server refuses a moved base and weaves in blocks added elsewhere. Structural answers carry the stored order.
  • What a refusal keeps; how a failed call resyncs.
  • Parts for any event over 24 KB.
  • The scene-break and delete keys, and the paste and drag rules.
  • A reshape's reason list.
  • Deltas go through the host's per-page queue.
  • WebKit now runs.

Also: AGENTS.md gains the module's commands, .dockerignore excludes Playwright output, and the plan's M2 line names this PR.

🤖 Generated with Claude Code

Editor track slice **M2a** (ADR 0030, `docs/PROJECT_PLAN.md`). M2 is split in two: - **M2a (this PR)** builds and proves the ProseMirror surface on its own, on a harness page against a fake host. - **M2b** will wire it into the chapter page, with the amended use cases, the markup migration and the Razor host. **No app change here:** nothing loads the module yet, and `dotnet build` does not build it until M2b. ## The surface (`src/Tsumugi.BlazorAdapter/ManuscriptEditor/src/`) - **Schema:** paragraph and scene-break blocks, the marks `strong` and `em`, and line breaks. A block's segment id lives in its attributes. The node view puts it on the DOM as `data-segment-id`; no `toDOM` writes it, so it never reaches the clipboard. - **Ids** (`ids.ts`, `commands.ts`): - The module mints UUIDv7 ids. A transaction hook keeps them unique by mapping: the owner keeps its id and a copy gets a fresh one. - Enter at a paragraph's start (a selection there is deleted first) puts the new block before it. - Backspace, Delete and the word/line delete keys keep the non-empty paragraph's id. They select a scene break before a second press removes it. - Pasted and dropped paragraphs arrive as whole blocks with fresh ids. A selection across paragraphs is cut around its ends; it is never joined. - A drag of whole paragraphs — a triple-click's text included — moves the same blocks to the nearest block boundary. - The browser's own paragraph and line-break input is cancelled and made by the editor's commands. - **Events** (`derive.ts`, `session.ts`, `transport.ts`) are derived from a before/after comparison of the block lists: - `SegmentText` on a paragraph's 700 ms pause (held while an IME composes), with its sequence number; `Split`; `Merge`; `Reshape` with its reason, the order it was based on and the new one. - Strictly in order, one at a time; pending typing is flushed first. Stamps are read when an event is sent. - An event over 24 KB goes in parts (`EventPart` / `EventParts`). - Each event is answered with the stored form of what it wrote; the stored provenance it carries redraws the paragraph's gutter mark. A structural event's answer also carries the stored order, which is laid onto the screen once nothing structural of the module's is on its way. - **Failure modes:** - A refusal is adopted. Unsaved typing survives where the server didn't change the paragraph, as does an empty chapter's first paragraph; the adoption is held while composing. - A failed call is answered by one resync reshape, retried only at the next change. - `flush()` resolves only once kept typing is saved too, and `destroy()` still sends typing. - **Remote deltas** (`reconcile.ts`, `diff.ts`): - Reconciled by id with token-level hunks; each CJK character is its own token. - The caret is kept as (segment, offset); deltas apply in order and are held while composing. - Typing and in-flight writes win their paragraph, and a block whose removal is in flight never comes back. - **Around it** (`view.ts`, `surface.css`): - The undo keys are claimed so the shell's hotkey stays the one undo path; the Edit menu's undo is reported as `Undo()` / `Redo()`. - Every `format*` input is cancelled. - Also: `Dirty(bool)`, the toolbar's scene break, the empty chapter's unsaved first paragraph, the provenance gutter (display only — the click and the working state are M4), and the ARIA attributes. The CSS uses Kagaku.UI tokens only. - **Bundle:** 71.8 KB gzipped, against the spike's measured ~67 KB. ## Proof - **vitest (168 tests):** derivation, reconcile, ids, transport, conversion and the diff, plus the session against `harness/fake-server.ts`. The fake server models the host contract M2b's C# will implement: codec-trimmed texts, stamp checks, fresh ids, the base-order check with remotely added blocks woven in, and refusals answered with the chapter. - **Properties:** random editing leaves the server holding exactly the chapter on screen. The runs mix another tab editing, adding, removing and moving segments with refusals and failed calls. 1,000 runs in CI; 10,000 locally before this PR. - **Playwright on the harness page:** 88 checks in Chromium, Firefox and WebKit. The 5 skips are the Chromium-only CDP input-method tests, plus a text drag Playwright cannot start in Firefox. A new CI job `editor-browsers` runs it in `mcr.microsoft.com/playwright:v1.63.0-noble`, because WebKit does not start on the build host. I ran the same image locally with podman. - **`MANUAL-MATRIX.md`** covers real input methods (fcitx5-mozc), dead keys, spellcheck, the Edit menu, a Firefox drag, Safari and a screen reader, runnable on `npm run harness`. - **Pre-PR review, three lenses** (protocol, browser layer, tests/build/docs): 13 + 13 + 23 findings, all addressed. The last mutation round's 28 probes are all killed. - **Review round 1:** the answer's provenance redraw is pinned by two vitest cases, one for an edit and one for a split's new paragraph. Both fail with the redraw removed. ## ADR 0030 amended where the build is more precise - `Dirty` has two edges. - Echoes are the events' answers, not seq-tagged deltas. - A reshape carries a base order: the server refuses a moved base and weaves in blocks added elsewhere. Structural answers carry the stored order. - What a refusal keeps; how a failed call resyncs. - Parts for any event over 24 KB. - The scene-break and delete keys, and the paste and drag rules. - A reshape's reason list. - Deltas go through the host's per-page queue. - WebKit now runs. Also: `AGENTS.md` gains the module's commands, `.dockerignore` excludes Playwright output, and the plan's M2 line names this PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(editor): the manuscript surface module on its harness page (editor track M2a)
Some checks failed
CI / test (pull_request) Has been cancelled
CI / editor-browsers (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
04a59fbee3
ADR 0030's surface, built and proven on its own before M2b wires it into the chapter page; no app
change yet.

- One ProseMirror state per chapter (the pinned prosemirror-model/state/view/transform/keymap/
  commands): paragraph and scene-break blocks, strong and em, line breaks; a block's segment id in its
  attributes and on the DOM through its node view, never in the clipboard.
- Ids: UUIDv7 minted in the module; a transaction hook keeps them unique by mapping — the owner keeps
  its id, a copy gets a fresh one; Enter at a paragraph's start (a selection there deleted first) puts
  the new block before it; Backspace, Delete and the word/line delete keys keep the non-empty
  paragraph's id and select a scene break before removing it; pasted and dropped paragraphs arrive as
  whole blocks with fresh ids, a selection across paragraphs cut around its ends; a drag of whole
  paragraphs moves the same blocks; the browser's own paragraph input is made by the editor.
- Events derived from a before/after comparison of the block lists: a paragraph's 700 ms typing pause
  (held while an IME composes) with its sequence number, Split, Merge, Reshape with its reason and the
  order it was based on — sent strictly in order, one at a time, typing flushed first; stamps read when
  sent; an event over 24 KB sent in parts; each answered with the stored form of what it wrote and, when
  structural, the stored order, laid onto the screen once nothing structural is on its way.
- A refusal adopted — unsaved typing kept where the server did not change the paragraph, an empty
  chapter's first paragraph kept, the adoption held while composing; a failed call answered by one
  resync reshape, retried only at the next change; flush() resolving once the kept typing is saved too;
  typing still sent after destroy().
- Remote deltas reconciled by id with token-level hunks (CJK characters their own tokens), the caret
  kept as (segment, offset), applied in order and held while composing; typing and in-flight writes win
  their paragraph; a block whose removal is in flight never comes back.
- The undo keys claimed, the Edit menu's undo reported, every browser formatting input cancelled;
  Dirty(bool), the toolbar scene break, the empty chapter's unsaved first paragraph.
- Tests: vitest over derivation, reconcile, ids, transport and the session against a fake server that
  checks as the host will (stamps, fresh ids, base orders), with properties that random editing — with
  another tab editing, adding, removing and moving meanwhile, refusals and failures mixed in — leaves
  the server holding the chapter on screen (1,000 runs in CI, 10,000 locally); a Playwright suite on the
  harness page, 88 checks in Chromium, Firefox and WebKit (CI job in the Playwright image);
  MANUAL-MATRIX.md for input methods, menus, a Firefox drag, Safari and a screen reader.
- ADR 0030 amended where the build is more precise: Dirty's two edges, echoes as answers, the base
  order and the answered order, what a refusal keeps and how a failed call resyncs, parts for any
  large event, the scene-break and delete keys, the drag and paste rules, a reshape's reason, deltas
  through the host's queue, WebKit now run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs: M2a's plan entry names its PR
All checks were successful
CI / build (pull_request) Successful in 35s
CI / editor-browsers (pull_request) Successful in 53s
CI / test (pull_request) Successful in 1m15s
4dd0625023
Co-Authored-By: Claude Opus 5.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: e7388f4 · Generated: 2026-09-26 01:16:27 UTC · Revision: #2

Summary

Summary
Generated on: 09/26/2026 - 01:16:26
Coverage date: 09/26/2026 - 01:16:05 - 09/26/2026 - 01:16:23
Parser: MultiReport (6x Cobertura)
Assemblies: 9
Classes: 444
Files: 421
Line coverage: 97.8% (13257 of 13553)
Covered lines: 13257
Uncovered lines: 296
Coverable lines: 13553
Total lines: 29246
Branch coverage: 93.7% (4898 of 5227)
Covered branches: 4898
Total branches: 5227
Method coverage: Feature is only available for sponsors

Coverage

Kagaku.Flow - 99.7%
Name Line Branch
Kagaku.Flow 99.7% 99.2%
Kagaku.Flow.Budgeting.BudgetRequest 100%
Kagaku.Flow.Budgeting.ConsumerBudget 100%
Kagaku.Flow.Execution.NodeExecution 100% 100%
Kagaku.Flow.Execution.NodeOutcome 100% 100%
Kagaku.Flow.Execution.TransientFailureException 100%
Kagaku.Flow.Execution.Upstream 100%
Kagaku.Flow.Extensions.ServiceCollectionExtensions 100%
Kagaku.Flow.FlowJson 100%
Kagaku.Flow.Graphs.GraphDefinition 100%
Kagaku.Flow.Graphs.GraphDocument 100% 100%
Kagaku.Flow.Graphs.GraphEdge 100%
Kagaku.Flow.Graphs.GraphIndex 100% 100%
Kagaku.Flow.Graphs.GraphLoadError 100%
Kagaku.Flow.Graphs.GraphLoadResult 100%
Kagaku.Flow.Graphs.GraphNode 100%
Kagaku.Flow.Graphs.GraphPosition 100%
Kagaku.Flow.Graphs.GraphReader 100% 100%
Kagaku.Flow.Graphs.GraphSchema 100% 100%
Kagaku.Flow.Graphs.GraphShapeException 100%
Kagaku.Flow.Graphs.GraphWriter 100% 100%
Kagaku.Flow.Graphs.SocketRef 100%
Kagaku.Flow.Graphs.WireText 100% 100%
Kagaku.Flow.Nodes.CanvasDescriptorWriter 100% 100%
Kagaku.Flow.Nodes.ConfigProblem 100%
Kagaku.Flow.Nodes.Names 100%
Kagaku.Flow.Nodes.NodeDescriptor 100% 100%
Kagaku.Flow.Nodes.NodeRegistry 100% 100%
Kagaku.Flow.Nodes.NodeSockets 100% 100%
Kagaku.Flow.Nodes.SinkDescriptor 100%
Kagaku.Flow.Nodes.SocketTypes 100% 100%
Kagaku.Flow.Nodes.SourceDescriptor 100%
Kagaku.Flow.Nodes.TemplateField 100%
Kagaku.Flow.Runs.LedgerChange 100% 100%
Kagaku.Flow.Runs.LedgerTransitions 100% 100%
Kagaku.Flow.Runs.NodeAttempt 100%
Kagaku.Flow.Runs.NodeFacts 100%
Kagaku.Flow.Runs.NodeRun 100%
Kagaku.Flow.Runs.Run 100% 100%
Kagaku.Flow.Runs.RunEvent 100%
Kagaku.Flow.Runs.RunStatuses 100% 100%
Kagaku.Flow.Runs.RunTrigger 100% 100%
Kagaku.Flow.Scheduling.Exclusion 100%
Kagaku.Flow.Scheduling.FlowEngine 100% 100%
Kagaku.Flow.Scheduling.FlowEngineOptions 100% 100%
Kagaku.Flow.Scheduling.ReadyRow 100%
Kagaku.Flow.Scheduling.ReadySet 100% 100%
Kagaku.Flow.Scheduling.ReadySetResult 100%
Kagaku.Flow.Scheduling.RunRequest 100% 100%
Kagaku.Flow.Scheduling.RunStartResult 100%
Kagaku.Flow.Scheduling.SocketValues 100% 100%
Kagaku.Flow.Templating.InputsWalker 100% 100%
Kagaku.Flow.Templating.ParsedTemplate 100% 100%
Kagaku.Flow.Templating.SandboxedTemplateContext 100% 100%
Kagaku.Flow.Templating.TemplateError 100%
Kagaku.Flow.Templating.TemplateHost 100% 100%
Kagaku.Flow.Templating.TemplateLimits 100%
Kagaku.Flow.Templating.TemplateModel 100% 100%
Kagaku.Flow.Templating.TemplateProblem 100%
Kagaku.Flow.Templating.TemplateRenderRequest 100% 100%
Kagaku.Flow.Templating.TemplateRenderResult 100%
Kagaku.Flow.Templating.TemplateRun 100%
Kagaku.Flow.Templating.TemplateSinks 100%
Kagaku.Flow.Templating.TemplateValues 100% 100%
Kagaku.Flow.Validation.GraphValidation 100% 100%
Kagaku.Flow.Validation.GraphValidationContext 100%
Kagaku.Flow.Validation.HookFailure 100%
Kagaku.Flow.Validation.ResolvedGraph 100% 100%
Kagaku.Flow.Validation.ValidationError 100%
Kagaku.Flow.Validation.ValidationReport 100%
System.Text.RegularExpressions.Generated 97.4% 92.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__ColorToken_2
97.4% 94.4%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__Socket_1
97.2% 94.4%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__TypeId_0
97.2% 94.4%
Kagaku.Flow.OpenRouter - 100%
Name Line Branch
Kagaku.Flow.OpenRouter 100% 100%
Kagaku.Flow.OpenRouter.Catalog.ModelCapabilities 100% 100%
Kagaku.Flow.OpenRouter.Gateway.LlmCompletion 100%
Kagaku.Flow.OpenRouter.Gateway.OpenRouterGateway 100% 100%
Kagaku.Flow.OpenRouter.Gateway.OpenRouterGatewayOptions 100%
Kagaku.Flow.OpenRouter.Gateway.OpenRouterKeyLookup 100% 100%
Kagaku.Flow.OpenRouter.Gateway.SentRequestCapture 100% 100%
Kagaku.Flow.OpenRouter.Gateway.SentRequestCaptureHandler 100% 100%
Kagaku.Flow.OpenRouter.OpenRouterServiceCollectionExtensions 100% 100%
Kagaku.Flow.Testing - 100%
Name Line Branch
Kagaku.Flow.Testing 100% 100%
Kagaku.Flow.Testing.FlowEngineDriver 100% 100%
Kagaku.Flow.Testing.InMemoryRunLedger 100% 100%
Kagaku.Flow.Testing.RecordingRunEventSink 100%
Kagaku.Flow.Testing.RunLedgerContract 100% 100%
Kagaku.Flow.Testing.RunLedgerContractException 100%
Tsumugi.BlazorAdapter - 96.4%
Name Line Branch
Tsumugi.BlazorAdapter 96.4% 92.6%
Tsumugi.BlazorAdapter.BlazorAdapterAssembly 100%
Tsumugi.BlazorAdapter.Documents.CategoryCreated 100%
Tsumugi.BlazorAdapter.Documents.CategoryEditor 91.8% 87.2%
Tsumugi.BlazorAdapter.Documents.CategorySaved 100%
Tsumugi.BlazorAdapter.Documents.ChangeCategoryFieldsRequested 100%
Tsumugi.BlazorAdapter.Documents.CreateCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.CreateDocumentRequested 100%
Tsumugi.BlazorAdapter.Documents.DeleteCategoryChoice 100%
Tsumugi.BlazorAdapter.Documents.DeleteCategoryDialog 100% 94.1%
Tsumugi.BlazorAdapter.Documents.DeleteCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.DeleteDocumentRequested 100%
Tsumugi.BlazorAdapter.Documents.DocumentAutoSaveFailed 100%
Tsumugi.BlazorAdapter.Documents.DocumentCreated 100%
Tsumugi.BlazorAdapter.Documents.DocumentEditor 95.1% 88.8%
Tsumugi.BlazorAdapter.Documents.DocumentImageRefused 100%
Tsumugi.BlazorAdapter.Documents.DocumentImageSet 100%
Tsumugi.BlazorAdapter.Documents.DocumentsActionFailed 100%
Tsumugi.BlazorAdapter.Documents.DocumentSaved 100%
Tsumugi.BlazorAdapter.Documents.DocumentsEffects 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsList 98.2% 92.4%
Tsumugi.BlazorAdapter.Documents.DocumentsLoaded 100%
Tsumugi.BlazorAdapter.Documents.DocumentsPage 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsReducers 100% 100%
Tsumugi.BlazorAdapter.Documents.DocumentsRefusal 100%
Tsumugi.BlazorAdapter.Documents.DocumentsState 100% 100%
Tsumugi.BlazorAdapter.Documents.FieldInput 100% 96.6%
Tsumugi.BlazorAdapter.Documents.FieldsChanges 100% 100%
Tsumugi.BlazorAdapter.Documents.FieldShapes 90% 87.5%
Tsumugi.BlazorAdapter.Documents.ImageUpload 100%
Tsumugi.BlazorAdapter.Documents.ImageUploadRefusal 100%
Tsumugi.BlazorAdapter.Documents.ImpactText 100% 94.2%
Tsumugi.BlazorAdapter.Documents.LoadDocuments 100%
Tsumugi.BlazorAdapter.Documents.MoveDocumentChoice 100%
Tsumugi.BlazorAdapter.Documents.MoveDocumentDialog 95.6% 71.4%
Tsumugi.BlazorAdapter.Documents.MoveDocumentsRequested 100%
Tsumugi.BlazorAdapter.Documents.ReorderCategoriesRequested 100%
Tsumugi.BlazorAdapter.Documents.SetCategoryCollapsed 100%
Tsumugi.BlazorAdapter.Documents.SetDocumentImageRequested 100%
Tsumugi.BlazorAdapter.Documents.SetDocumentsFilter 100%
Tsumugi.BlazorAdapter.Documents.UpdateCategoryRequested 100%
Tsumugi.BlazorAdapter.Documents.UpdateDocumentRequested 100%
Tsumugi.BlazorAdapter.Graphs.GraphCanvas 37.5% 25%
Tsumugi.BlazorAdapter.Graphs.GraphPlaygroundPage 20% 25%
Tsumugi.BlazorAdapter.Notifications.DomainChangedBridge 94.4% 83.3%
Tsumugi.BlazorAdapter.Notifications.DomainChangesReceived 75% 100%
Tsumugi.BlazorAdapter.Runs.RunChangedBridge 97.1% 90.6%
Tsumugi.BlazorAdapter.Runs.RunChangesReceived 100%
Tsumugi.BlazorAdapter.Runs.RunTextReceived 100%
Tsumugi.BlazorAdapter.Settings.BackupCompleted 100%
Tsumugi.BlazorAdapter.Settings.BackupFailed 100%
Tsumugi.BlazorAdapter.Settings.BackupNowRequested 100%
Tsumugi.BlazorAdapter.Settings.BackupSettingsLoaded 100%
Tsumugi.BlazorAdapter.Settings.BackupsSection 100% 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsEffects 100% 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsReducers 100%
Tsumugi.BlazorAdapter.Settings.BackupsSettingsState 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSection 100% 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSettingsEffects 100% 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSettingsReducers 100%
Tsumugi.BlazorAdapter.Settings.DefaultsSettingsState 100%
Tsumugi.BlazorAdapter.Settings.LlmDefaultsLoaded 100%
Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaved 100%
Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaveFailed 100%
Tsumugi.BlazorAdapter.Settings.LoadBackupSettings 100%
Tsumugi.BlazorAdapter.Settings.LoadProviderSettings 100%
Tsumugi.BlazorAdapter.Settings.ModelCatalogLoaded 100%
Tsumugi.BlazorAdapter.Settings.ModelCatalogLoadFailed 100%
Tsumugi.BlazorAdapter.Settings.OpenRouterKeySaved 100%
Tsumugi.BlazorAdapter.Settings.OpenRouterKeySaveFailed 100%
Tsumugi.BlazorAdapter.Settings.ProviderKeyChecked 100%
Tsumugi.BlazorAdapter.Settings.ProviderSection 100% 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsEffects 100% 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsLoaded 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsReducers 100% 100%
Tsumugi.BlazorAdapter.Settings.ProviderSettingsState 100% 100%
Tsumugi.BlazorAdapter.Settings.SaveLlmDefaultsRequested 100%
Tsumugi.BlazorAdapter.Settings.SaveOpenRouterKeyRequested 100%
Tsumugi.BlazorAdapter.Stories.CreateStoryRequested 100%
Tsumugi.BlazorAdapter.Stories.PurgeStoryRequested 100%
Tsumugi.BlazorAdapter.Stories.RestoreStoryRequested 100%
Tsumugi.BlazorAdapter.Stories.SetShowArchived 100%
Tsumugi.BlazorAdapter.Stories.SetStoriesFilter 100%
Tsumugi.BlazorAdapter.Stories.SetStoryArchivedRequested 100%
Tsumugi.BlazorAdapter.Stories.StoriesEffects 100% 100%
Tsumugi.BlazorAdapter.Stories.StoriesLoaded 100%
Tsumugi.BlazorAdapter.Stories.StoriesPage 100% 100%
Tsumugi.BlazorAdapter.Stories.StoriesReducers 100% 100%
Tsumugi.BlazorAdapter.Stories.StoriesState 100% 100%
Tsumugi.BlazorAdapter.Stories.StoryActionFailed 100%
Tsumugi.BlazorAdapter.Stories.StoryCreated 100%
Tsumugi.BlazorAdapter.Stories.StoryCreateFailed 100%
Tsumugi.BlazorAdapter.Stories.StoryTrashPage 100% 100%
Tsumugi.BlazorAdapter.Stories.TrashStoryRequested 100%
Tsumugi.BlazorAdapter.Workspace.AutoSaveFailed 100%
Tsumugi.BlazorAdapter.Workspace.CaretRequest 100%
Tsumugi.BlazorAdapter.Workspace.ChapterHeader 100% 86.3%
Tsumugi.BlazorAdapter.Workspace.ChapterMenu 100% 81.2%
Tsumugi.BlazorAdapter.Workspace.ChapterPage 100% 87.5%
Tsumugi.BlazorAdapter.Workspace.ChapterSaved 100%
Tsumugi.BlazorAdapter.Workspace.ChaptersPage 90% 83.3%
Tsumugi.BlazorAdapter.Workspace.CreateChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.DeleteChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.DeleteSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.EditSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.HistoryEffects 100% 100%
Tsumugi.BlazorAdapter.Workspace.HistoryLoaded 100%
Tsumugi.BlazorAdapter.Workspace.HistoryPage 100%
Tsumugi.BlazorAdapter.Workspace.HistoryPanel 100% 100%
Tsumugi.BlazorAdapter.Workspace.InsertSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.LoadHistory 100%
Tsumugi.BlazorAdapter.Workspace.LoadWorkspace 100%
Tsumugi.BlazorAdapter.Workspace.ManuscriptColumn 94.9% 89.4%
Tsumugi.BlazorAdapter.Workspace.ManuscriptEffects 100% 100%
Tsumugi.BlazorAdapter.Workspace.MergeChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.MergeSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.MoveChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.RedoRequested 100%
Tsumugi.BlazorAdapter.Workspace.RedoRevertRequested 100%
Tsumugi.BlazorAdapter.Workspace.RenameChapterRequested 100%
Tsumugi.BlazorAdapter.Workspace.RevertToSnapshotRequested 100%
Tsumugi.BlazorAdapter.Workspace.SaveScope 100% 50%
Tsumugi.BlazorAdapter.Workspace.SaveStateLadder 100% 100%
Tsumugi.BlazorAdapter.Workspace.SaveVersionRequested 100%
Tsumugi.BlazorAdapter.Workspace.SegmentRow 100% 96.8%
Tsumugi.BlazorAdapter.Workspace.SegmentSaved 100%
Tsumugi.BlazorAdapter.Workspace.SetChapterSummaryRequested 100%
Tsumugi.BlazorAdapter.Workspace.SplitSegmentRequested 100%
Tsumugi.BlazorAdapter.Workspace.StoryDetails 100% 85.2%
Tsumugi.BlazorAdapter.Workspace.StoryGeneralPage 100% 100%
Tsumugi.BlazorAdapter.Workspace.StorySaved 100%
Tsumugi.BlazorAdapter.Workspace.StoryShell 100% 100%
Tsumugi.BlazorAdapter.Workspace.UndoRequested 100%
Tsumugi.BlazorAdapter.Workspace.UndoStatusChanged 100%
Tsumugi.BlazorAdapter.Workspace.UpdateStoryRequested 100%
Tsumugi.BlazorAdapter.Workspace.WorkspaceActionFailed 100%
Tsumugi.BlazorAdapter.Workspace.WorkspaceLoaded 100%
Tsumugi.BlazorAdapter.Workspace.WorkspaceNotFound 100%
Tsumugi.BlazorAdapter.Workspace.WorkspaceReducers 100% 100%
Tsumugi.BlazorAdapter.Workspace.WorkspaceState 100%
Tsumugi.Domain - 96%
Name Line Branch
Tsumugi.Domain 96% 94.6%
System.Text.RegularExpressions.Generated 90.7% 87.5%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__DigitsAndSeparators_2
97.4% 95.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__LineBreak_0
91% 91.1%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__NumberToken_1
86.4% 77.7%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__Plain_4
91.9% 88.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__ThousandsGroup_3
90.9% 84.3%
Tsumugi.Domain.Documents.CategoryFields 94.5% 93.7%
Tsumugi.Domain.Documents.ChoiceOption 100%
Tsumugi.Domain.Documents.ChoiceValue 100%
Tsumugi.Domain.Documents.DateText 100%
Tsumugi.Domain.Documents.DateValue 100%
Tsumugi.Domain.Documents.Document 100% 100%
Tsumugi.Domain.Documents.DocumentCategory 100% 100%
Tsumugi.Domain.Documents.DocumentValues 100%
Tsumugi.Domain.Documents.FieldCarry 100% 100%
Tsumugi.Domain.Documents.FieldConversion 100% 100%
Tsumugi.Domain.Documents.FieldDefinition 97% 95.3%
Tsumugi.Domain.Documents.FieldMessages 100% 100%
Tsumugi.Domain.Documents.FieldNames 100%
Tsumugi.Domain.Documents.FieldsChange 100%
Tsumugi.Domain.Documents.FieldShape 95.2% 87.5%
Tsumugi.Domain.Documents.FieldsPlan 100%
Tsumugi.Domain.Documents.FieldsPlanner 99.2% 98.5%
Tsumugi.Domain.Documents.FieldValue 100%
Tsumugi.Domain.Documents.FieldValues 95.4% 97.7%
Tsumugi.Domain.Documents.ImageFiles 100% 100%
Tsumugi.Domain.Documents.ImageValue 100%
Tsumugi.Domain.Documents.MovePlan 100%
Tsumugi.Domain.Documents.NumberParse 100%
Tsumugi.Domain.Documents.NumberText 100% 97.6%
Tsumugi.Domain.Documents.NumberValue 100%
Tsumugi.Domain.Documents.StoryImage 100%
Tsumugi.Domain.Documents.TextValue 100% 100%
Tsumugi.Domain.Documents.ValueChange 100%
Tsumugi.Domain.Documents.ValueChangeKey 100%
Tsumugi.Domain.Documents.ValueImpact 100% 100%
Tsumugi.Domain.Journal.ChangeLogEntry 97.8% 75%
Tsumugi.Domain.Journal.StorySnapshot 95.4% 83.3%
Tsumugi.Domain.Settings.AppSetting 100%
Tsumugi.Domain.Stories.Chapter 100% 100%
Tsumugi.Domain.Stories.InlineMarkup 100% 100%
Tsumugi.Domain.Stories.MarkupRun 100%
Tsumugi.Domain.Stories.Provenance 100% 100%
Tsumugi.Domain.Stories.Segment 100% 100%
Tsumugi.Domain.Stories.Story 100% 100%
Tsumugi.Domain.Text 100% 100%
Tsumugi.Infrastructure - 97.6%
Name Line Branch
Tsumugi.Infrastructure 97.6% 85.9%
System.Text.RegularExpressions.Generated 81.4% 64.8%
System.Text.RegularExpressions.Generated.<RegexGenerator_g>F0D076C66E8EEC1C
A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0
84% 65.7%
Tsumugi.Infrastructure.Backups.BackupArtifactName 100% 100%
Tsumugi.Infrastructure.Backups.BackupOptions 100%
Tsumugi.Infrastructure.Backups.BackupSchedule 100% 83.3%
Tsumugi.Infrastructure.Backups.BackupScheduler 96.5% 83.3%
Tsumugi.Infrastructure.Backups.BackupWriter 98.5% 92.5%
Tsumugi.Infrastructure.Backups.DatabaseFingerprint 91.9% 97%
Tsumugi.Infrastructure.Backups.FileSystemBackupStore 100% 100%
Tsumugi.Infrastructure.Backups.PreMigrationBackup 89.1% 100%
Tsumugi.Infrastructure.Backups.SqliteSnapshot 87.8% 75%
Tsumugi.Infrastructure.DependencyInjection 100% 100%
Tsumugi.Infrastructure.Journal.EfChangeJournal 100%
Tsumugi.Infrastructure.Journal.EfSnapshotStore 100%
Tsumugi.Infrastructure.Journal.EfUndoStore 98.1% 85.7%
Tsumugi.Infrastructure.Journal.ModelExtensions 100%
Tsumugi.Infrastructure.Journal.OperationContext 100% 100%
Tsumugi.Infrastructure.Journal.PropertyBag 100% 95.4%
Tsumugi.Infrastructure.Notifications.InProcessDomainChangedBus 100% 100%
Tsumugi.Infrastructure.Persistence.Configurations.AppSettingConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.ChangeLogEntryConfigurati
on
100%
Tsumugi.Infrastructure.Persistence.Configurations.ChapterConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.DocumentCategoryConfigura
tion
100%
Tsumugi.Infrastructure.Persistence.Configurations.DocumentConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.SegmentConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.StoryConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.StoryImageConfiguration 100%
Tsumugi.Infrastructure.Persistence.Configurations.StorySnapshotConfiguratio
n
100%
Tsumugi.Infrastructure.Persistence.Converters.CanonicalJsonComparer 100%
Tsumugi.Infrastructure.Persistence.Converters.CategoryFieldsConverter 97.6% 94.4%
Tsumugi.Infrastructure.Persistence.Converters.FieldValuesConverter 96.9% 93.7%
Tsumugi.Infrastructure.Persistence.Converters.ProvenanceConverter 100% 90%
Tsumugi.Infrastructure.Persistence.Converters.UtcTicksConverter 100%
Tsumugi.Infrastructure.Persistence.Converters.WireFormat 85.7% 73.8%
Tsumugi.Infrastructure.Persistence.Migrations.AddAppSettings 99.2%
Tsumugi.Infrastructure.Persistence.Migrations.AddJournal 98.8%
Tsumugi.Infrastructure.Persistence.Migrations.CategoryFields 100% 100%
Tsumugi.Infrastructure.Persistence.Migrations.CategoryFieldsSql 100%
Tsumugi.Infrastructure.Persistence.Migrations.DropDocumentBody 100%
Tsumugi.Infrastructure.Persistence.Migrations.GenericDocuments 100%
Tsumugi.Infrastructure.Persistence.Migrations.InitialStoryDomain 97.3%
Tsumugi.Infrastructure.Persistence.Migrations.StoryImages 100%
Tsumugi.Infrastructure.Persistence.Migrations.TsumugiDbContextModelSnapshot 100%
Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore 98.3% 75%
Tsumugi.Infrastructure.Persistence.Stores.EfImageStore 100%
Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore 95% 84.6%
Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore 98.7% 90%
Tsumugi.Infrastructure.Persistence.TsumugiDbContext 85.5% 89.2%
Tsumugi.Infrastructure.Persistence.TsumugiDbContextFactory 0%
Tsumugi.Infrastructure.Provider.OpenRouterKeySource 100% 100%
Tsumugi.Infrastructure.Provider.OpenRouterModelProvider 100% 100%
Tsumugi.Infrastructure.Provider.OpenRouterOptions 100% 100%
Tsumugi.Infrastructure.Provider.ResolvedKey 100%
Tsumugi.Infrastructure.Runs.InProcessRunEventBus 100% 100%
Tsumugi.Infrastructure.Settings.EfAppSettingsStore 100% 100%
Tsumugi.Kernel - 90.9%
Name Line Branch
Tsumugi.Kernel 90.9% 75%
Tsumugi.Kernel.Err`1 100%
Tsumugi.Kernel.Ok`1 100%
Tsumugi.Kernel.Result`1 88.8% 75%
Tsumugi.Server - 100%
Name Line Branch
Tsumugi.Server 100% 86.4%
Program 100% 100%
Tsumugi.Server.Backups.BackupConfiguration 100% 100%
Tsumugi.Server.Components.App 100%
Tsumugi.Server.Components.Layout.AppHeader 100% 50%
Tsumugi.Server.Components.Layout.MainLayout 100%
Tsumugi.Server.Components.Pages.Gate 100% 100%
Tsumugi.Server.Provider.OpenRouterConfiguration 100% 100%
Tsumugi.Server.Security.AccessGate 100% 66.6%
Tsumugi.Server.Security.AccessSecret 100% 50%
Tsumugi.Server.VolumeStartupValidator 100% 100%
Tsumugi.UseCases - 98.3%
Name Line Branch
Tsumugi.UseCases 98.3% 93.9%
Tsumugi.UseCases.Backups.BackupArtifact 100%
Tsumugi.UseCases.Backups.BackupPolicy 100%
Tsumugi.UseCases.Backups.BackupStatusView 100% 100%
Tsumugi.UseCases.Backups.DatabaseSnapshot 100%
Tsumugi.UseCases.Backups.DownloadDatabaseSnapshot 100%
Tsumugi.UseCases.Backups.GetBackupStatus 100%
Tsumugi.UseCases.Backups.RunBackupNow 100%
Tsumugi.UseCases.Chapters.ChapterDto 100%
Tsumugi.UseCases.Chapters.CreateChapter 100% 100%
Tsumugi.UseCases.Chapters.DeleteChapter 100% 100%
Tsumugi.UseCases.Chapters.GetManuscript 100% 100%
Tsumugi.UseCases.Chapters.ManuscriptChapter 100%
Tsumugi.UseCases.Chapters.ManuscriptView 100%
Tsumugi.UseCases.Chapters.MergeChapters 100% 100%
Tsumugi.UseCases.Chapters.RenameChapter 100% 100%
Tsumugi.UseCases.Chapters.ReorderChapters 100% 100%
Tsumugi.UseCases.Chapters.SetChapterSummary 100% 75%
Tsumugi.UseCases.DependencyInjection 100%
Tsumugi.UseCases.Diagnostics.SeedDevData 98.2% 71.4%
Tsumugi.UseCases.Diagnostics.SeedPictures 100%
Tsumugi.UseCases.Documents.ChangeCategoryFields 100% 83.3%
Tsumugi.UseCases.Documents.CreateCategory 100% 100%
Tsumugi.UseCases.Documents.CreateDocument 100% 100%
Tsumugi.UseCases.Documents.DefaultFields 100% 100%
Tsumugi.UseCases.Documents.DeleteCategory 100% 94.4%
Tsumugi.UseCases.Documents.DeleteDocument 100% 100%
Tsumugi.UseCases.Documents.DocumentCategoryDto 100%
Tsumugi.UseCases.Documents.DocumentCategoryProfile 100%
Tsumugi.UseCases.Documents.DocumentDto 100%
Tsumugi.UseCases.Documents.DocumentPatch 100%
Tsumugi.UseCases.Documents.DocumentProfile 100%
Tsumugi.UseCases.Documents.DocumentsCategory 100%
Tsumugi.UseCases.Documents.DocumentsView 100%
Tsumugi.UseCases.Documents.DocumentWrites 100% 100%
Tsumugi.UseCases.Documents.FieldsChangeLabel 95.4% 88.2%
Tsumugi.UseCases.Documents.GetDocuments 100% 100%
Tsumugi.UseCases.Documents.GetStoryImage 100% 100%
Tsumugi.UseCases.Documents.ImpactPreview 100% 100%
Tsumugi.UseCases.Documents.LossMessages 92.3% 87.5%
Tsumugi.UseCases.Documents.MoveDocuments 100% 100%
Tsumugi.UseCases.Documents.ReorderCategories 100% 100%
Tsumugi.UseCases.Documents.SetDocumentImage 100% 93.7%
Tsumugi.UseCases.Documents.StoryImageFile 100%
Tsumugi.UseCases.Documents.UpdateCategory 100% 100%
Tsumugi.UseCases.Documents.UpdateDocument 100% 81.2%
Tsumugi.UseCases.Journal.GetStoryHistory 100% 100%
Tsumugi.UseCases.Journal.GetUndoStatus 100% 100%
Tsumugi.UseCases.Journal.HistoryOperation 66.6%
Tsumugi.UseCases.Journal.HistorySnapshot 87.5%
Tsumugi.UseCases.Journal.OperationGrouping 100% 100%
Tsumugi.UseCases.Journal.RedoRevert 100% 100%
Tsumugi.UseCases.Journal.RedoRun 75% 57.1%
Tsumugi.UseCases.Journal.RedoTyping 100% 100%
Tsumugi.UseCases.Journal.RevertConflict 100%
Tsumugi.UseCases.Journal.RevertOutcome 100%
Tsumugi.UseCases.Journal.RevertRun 96.5% 77.7%
Tsumugi.UseCases.Journal.RevertToSnapshot 92.3% 83.3%
Tsumugi.UseCases.Journal.StoryHistory 100%
Tsumugi.UseCases.Journal.TakeSnapshot 100% 87.5%
Tsumugi.UseCases.Journal.UndoStatus 100%
Tsumugi.UseCases.Journal.UndoTyping 100% 100%
Tsumugi.UseCases.Journal.UserOperation 100%
Tsumugi.UseCases.Notifications.DomainChanged 100%
Tsumugi.UseCases.Ordering 100% 100%
Tsumugi.UseCases.Runs.RunEvent 100%
Tsumugi.UseCases.Segments.DeleteSegments 100% 100%
Tsumugi.UseCases.Segments.EditSegment 100% 83.3%
Tsumugi.UseCases.Segments.InsertSegment 100% 100%
Tsumugi.UseCases.Segments.MergeSegmentWithPrevious 100% 100%
Tsumugi.UseCases.Segments.ModelOutput 98.2% 96.6%
Tsumugi.UseCases.Segments.ModelParagraph 100%
Tsumugi.UseCases.Segments.MoveSegments 100% 100%
Tsumugi.UseCases.Segments.ReorderSegments 100% 100%
Tsumugi.UseCases.Segments.SegmentDto 100%
Tsumugi.UseCases.Segments.SplitSegment 100% 100%
Tsumugi.UseCases.Settings.CatalogModel 100% 100%
Tsumugi.UseCases.Settings.CheckOpenRouterKey 100%
Tsumugi.UseCases.Settings.GetLlmDefaults 100% 100%
Tsumugi.UseCases.Settings.GetModelCatalog 100% 100%
Tsumugi.UseCases.Settings.GetProviderSettings 100%
Tsumugi.UseCases.Settings.LlmDefaults 100%
Tsumugi.UseCases.Settings.ProviderKeyInfo 100%
Tsumugi.UseCases.Settings.ProviderKeyState 100%
Tsumugi.UseCases.Settings.ProviderSettingsView 100% 100%
Tsumugi.UseCases.Settings.ReasoningEfforts 100% 100%
Tsumugi.UseCases.Settings.RemoveOpenRouterKey 100%
Tsumugi.UseCases.Settings.RoutingDefaults 100%
Tsumugi.UseCases.Settings.RoutingSettings 100% 100%
Tsumugi.UseCases.Settings.SaveLlmDefaults 100% 100%
Tsumugi.UseCases.Settings.SaveOpenRouterKey 100% 100%
Tsumugi.UseCases.Stories.CreateStory 100% 100%
Tsumugi.UseCases.Stories.GetStory 100% 100%
Tsumugi.UseCases.Stories.ListStories 100% 75%
Tsumugi.UseCases.Stories.PurgeStory 100% 100%
Tsumugi.UseCases.Stories.RestoreStory 100% 100%
Tsumugi.UseCases.Stories.SetActiveGraph 100% 100%
Tsumugi.UseCases.Stories.SetStoryArchived 100% 100%
Tsumugi.UseCases.Stories.StarterCategories 100%
Tsumugi.UseCases.Stories.StoryDetail 100%
Tsumugi.UseCases.Stories.StoryListItem 81.8%
Tsumugi.UseCases.Stories.StoryProfile 100%
Tsumugi.UseCases.Stories.TrashStory 100% 100%
Tsumugi.UseCases.Stories.UpdateStory 100% 100%
<!-- coverage-comment --> > 🔄 **Auto-updating coverage report** — this comment is regenerated on every push, so the numbers below always reflect the commit shown here, not the branch tip. > > **Commit:** `e7388f4` · **Generated:** 2026-09-26 01:16:27 UTC · **Revision:** #2 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/26/2026 - 01:16:26 | | Coverage date: | 09/26/2026 - 01:16:05 - 09/26/2026 - 01:16:23 | | Parser: | MultiReport (6x Cobertura) | | Assemblies: | 9 | | Classes: | 444 | | Files: | 421 | | **Line coverage:** | 97.8% (13257 of 13553) | | Covered lines: | 13257 | | Uncovered lines: | 296 | | Coverable lines: | 13553 | | Total lines: | 29246 | | **Branch coverage:** | 93.7% (4898 of 5227) | | Covered branches: | 4898 | | Total branches: | 5227 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Kagaku.Flow - 99.7%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagaku.Flow**|**99.7%**|**99.2%**| |Kagaku.Flow.Budgeting.BudgetRequest|100%|| |Kagaku.Flow.Budgeting.ConsumerBudget|100%|| |Kagaku.Flow.Execution.NodeExecution|100%|100%| |Kagaku.Flow.Execution.NodeOutcome|100%|100%| |Kagaku.Flow.Execution.TransientFailureException|100%|| |Kagaku.Flow.Execution.Upstream|100%|| |Kagaku.Flow.Extensions.ServiceCollectionExtensions|100%|| |Kagaku.Flow.FlowJson|100%|| |Kagaku.Flow.Graphs.GraphDefinition|100%|| |Kagaku.Flow.Graphs.GraphDocument|100%|100%| |Kagaku.Flow.Graphs.GraphEdge|100%|| |Kagaku.Flow.Graphs.GraphIndex|100%|100%| |Kagaku.Flow.Graphs.GraphLoadError|100%|| |Kagaku.Flow.Graphs.GraphLoadResult|100%|| |Kagaku.Flow.Graphs.GraphNode|100%|| |Kagaku.Flow.Graphs.GraphPosition|100%|| |Kagaku.Flow.Graphs.GraphReader|100%|100%| |Kagaku.Flow.Graphs.GraphSchema|100%|100%| |Kagaku.Flow.Graphs.GraphShapeException|100%|| |Kagaku.Flow.Graphs.GraphWriter|100%|100%| |Kagaku.Flow.Graphs.SocketRef|100%|| |Kagaku.Flow.Graphs.WireText|100%|100%| |Kagaku.Flow.Nodes.CanvasDescriptorWriter|100%|100%| |Kagaku.Flow.Nodes.ConfigProblem|100%|| |Kagaku.Flow.Nodes.Names|100%|| |Kagaku.Flow.Nodes.NodeDescriptor|100%|100%| |Kagaku.Flow.Nodes.NodeRegistry|100%|100%| |Kagaku.Flow.Nodes.NodeSockets|100%|100%| |Kagaku.Flow.Nodes.SinkDescriptor|100%|| |Kagaku.Flow.Nodes.SocketTypes|100%|100%| |Kagaku.Flow.Nodes.SourceDescriptor|100%|| |Kagaku.Flow.Nodes.TemplateField|100%|| |Kagaku.Flow.Runs.LedgerChange|100%|100%| |Kagaku.Flow.Runs.LedgerTransitions|100%|100%| |Kagaku.Flow.Runs.NodeAttempt|100%|| |Kagaku.Flow.Runs.NodeFacts|100%|| |Kagaku.Flow.Runs.NodeRun|100%|| |Kagaku.Flow.Runs.Run|100%|100%| |Kagaku.Flow.Runs.RunEvent|100%|| |Kagaku.Flow.Runs.RunStatuses|100%|100%| |Kagaku.Flow.Runs.RunTrigger|100%|100%| |Kagaku.Flow.Scheduling.Exclusion|100%|| |Kagaku.Flow.Scheduling.FlowEngine|100%|100%| |Kagaku.Flow.Scheduling.FlowEngineOptions|100%|100%| |Kagaku.Flow.Scheduling.ReadyRow|100%|| |Kagaku.Flow.Scheduling.ReadySet|100%|100%| |Kagaku.Flow.Scheduling.ReadySetResult|100%|| |Kagaku.Flow.Scheduling.RunRequest|100%|100%| |Kagaku.Flow.Scheduling.RunStartResult|100%|| |Kagaku.Flow.Scheduling.SocketValues|100%|100%| |Kagaku.Flow.Templating.InputsWalker|100%|100%| |Kagaku.Flow.Templating.ParsedTemplate|100%|100%| |Kagaku.Flow.Templating.SandboxedTemplateContext|100%|100%| |Kagaku.Flow.Templating.TemplateError|100%|| |Kagaku.Flow.Templating.TemplateHost|100%|100%| |Kagaku.Flow.Templating.TemplateLimits|100%|| |Kagaku.Flow.Templating.TemplateModel|100%|100%| |Kagaku.Flow.Templating.TemplateProblem|100%|| |Kagaku.Flow.Templating.TemplateRenderRequest|100%|100%| |Kagaku.Flow.Templating.TemplateRenderResult|100%|| |Kagaku.Flow.Templating.TemplateRun|100%|| |Kagaku.Flow.Templating.TemplateSinks|100%|| |Kagaku.Flow.Templating.TemplateValues|100%|100%| |Kagaku.Flow.Validation.GraphValidation|100%|100%| |Kagaku.Flow.Validation.GraphValidationContext|100%|| |Kagaku.Flow.Validation.HookFailure|100%|| |Kagaku.Flow.Validation.ResolvedGraph|100%|100%| |Kagaku.Flow.Validation.ValidationError|100%|| |Kagaku.Flow.Validation.ValidationReport|100%|| |System.Text.RegularExpressions.Generated|97.4%|92.8%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF<br/>E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__ColorToken_2|97.4%|94.4%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF<br/>E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__Socket_1|97.2%|94.4%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F6329A328226B0CF<br/>E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__TypeId_0|97.2%|94.4%| </details> <details><summary>Kagaku.Flow.OpenRouter - 100%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagaku.Flow.OpenRouter**|**100%**|**100%**| |Kagaku.Flow.OpenRouter.Catalog.ModelCapabilities|100%|100%| |Kagaku.Flow.OpenRouter.Gateway.LlmCompletion|100%|| |Kagaku.Flow.OpenRouter.Gateway.OpenRouterGateway|100%|100%| |Kagaku.Flow.OpenRouter.Gateway.OpenRouterGatewayOptions|100%|| |Kagaku.Flow.OpenRouter.Gateway.OpenRouterKeyLookup|100%|100%| |Kagaku.Flow.OpenRouter.Gateway.SentRequestCapture|100%|100%| |Kagaku.Flow.OpenRouter.Gateway.SentRequestCaptureHandler|100%|100%| |Kagaku.Flow.OpenRouter.OpenRouterServiceCollectionExtensions|100%|100%| </details> <details><summary>Kagaku.Flow.Testing - 100%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagaku.Flow.Testing**|**100%**|**100%**| |Kagaku.Flow.Testing.FlowEngineDriver|100%|100%| |Kagaku.Flow.Testing.InMemoryRunLedger|100%|100%| |Kagaku.Flow.Testing.RecordingRunEventSink|100%|| |Kagaku.Flow.Testing.RunLedgerContract|100%|100%| |Kagaku.Flow.Testing.RunLedgerContractException|100%|| </details> <details><summary>Tsumugi.BlazorAdapter - 96.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.BlazorAdapter**|**96.4%**|**92.6%**| |Tsumugi.BlazorAdapter.BlazorAdapterAssembly|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryCreated|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryEditor|91.8%|87.2%| |Tsumugi.BlazorAdapter.Documents.CategorySaved|100%|| |Tsumugi.BlazorAdapter.Documents.ChangeCategoryFieldsRequested|100%|| |Tsumugi.BlazorAdapter.Documents.CreateCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.CreateDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DeleteCategoryChoice|100%|| |Tsumugi.BlazorAdapter.Documents.DeleteCategoryDialog|100%|94.1%| |Tsumugi.BlazorAdapter.Documents.DeleteCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DeleteDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentAutoSaveFailed|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentCreated|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentEditor|95.1%|88.8%| |Tsumugi.BlazorAdapter.Documents.DocumentImageRefused|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentImageSet|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsActionFailed|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentSaved|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsEffects|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsList|98.2%|92.4%| |Tsumugi.BlazorAdapter.Documents.DocumentsLoaded|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsPage|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsReducers|100%|100%| |Tsumugi.BlazorAdapter.Documents.DocumentsRefusal|100%|| |Tsumugi.BlazorAdapter.Documents.DocumentsState|100%|100%| |Tsumugi.BlazorAdapter.Documents.FieldInput|100%|96.6%| |Tsumugi.BlazorAdapter.Documents.FieldsChanges|100%|100%| |Tsumugi.BlazorAdapter.Documents.FieldShapes|90%|87.5%| |Tsumugi.BlazorAdapter.Documents.ImageUpload|100%|| |Tsumugi.BlazorAdapter.Documents.ImageUploadRefusal|100%|| |Tsumugi.BlazorAdapter.Documents.ImpactText|100%|94.2%| |Tsumugi.BlazorAdapter.Documents.LoadDocuments|100%|| |Tsumugi.BlazorAdapter.Documents.MoveDocumentChoice|100%|| |Tsumugi.BlazorAdapter.Documents.MoveDocumentDialog|95.6%|71.4%| |Tsumugi.BlazorAdapter.Documents.MoveDocumentsRequested|100%|| |Tsumugi.BlazorAdapter.Documents.ReorderCategoriesRequested|100%|| |Tsumugi.BlazorAdapter.Documents.SetCategoryCollapsed|100%|| |Tsumugi.BlazorAdapter.Documents.SetDocumentImageRequested|100%|| |Tsumugi.BlazorAdapter.Documents.SetDocumentsFilter|100%|| |Tsumugi.BlazorAdapter.Documents.UpdateCategoryRequested|100%|| |Tsumugi.BlazorAdapter.Documents.UpdateDocumentRequested|100%|| |Tsumugi.BlazorAdapter.Graphs.GraphCanvas|37.5%|25%| |Tsumugi.BlazorAdapter.Graphs.GraphPlaygroundPage|20%|25%| |Tsumugi.BlazorAdapter.Notifications.DomainChangedBridge|94.4%|83.3%| |Tsumugi.BlazorAdapter.Notifications.DomainChangesReceived|75%|100%| |Tsumugi.BlazorAdapter.Runs.RunChangedBridge|97.1%|90.6%| |Tsumugi.BlazorAdapter.Runs.RunChangesReceived|100%|| |Tsumugi.BlazorAdapter.Runs.RunTextReceived|100%|| |Tsumugi.BlazorAdapter.Settings.BackupCompleted|100%|| |Tsumugi.BlazorAdapter.Settings.BackupFailed|100%|| |Tsumugi.BlazorAdapter.Settings.BackupNowRequested|100%|| |Tsumugi.BlazorAdapter.Settings.BackupSettingsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.BackupsSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsEffects|100%|100%| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsReducers|100%|| |Tsumugi.BlazorAdapter.Settings.BackupsSettingsState|100%|| |Tsumugi.BlazorAdapter.Settings.DefaultsSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.DefaultsSettingsEffects|100%|100%| |Tsumugi.BlazorAdapter.Settings.DefaultsSettingsReducers|100%|| |Tsumugi.BlazorAdapter.Settings.DefaultsSettingsState|100%|| |Tsumugi.BlazorAdapter.Settings.LlmDefaultsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaved|100%|| |Tsumugi.BlazorAdapter.Settings.LlmDefaultsSaveFailed|100%|| |Tsumugi.BlazorAdapter.Settings.LoadBackupSettings|100%|| |Tsumugi.BlazorAdapter.Settings.LoadProviderSettings|100%|| |Tsumugi.BlazorAdapter.Settings.ModelCatalogLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.ModelCatalogLoadFailed|100%|| |Tsumugi.BlazorAdapter.Settings.OpenRouterKeySaved|100%|| |Tsumugi.BlazorAdapter.Settings.OpenRouterKeySaveFailed|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderKeyChecked|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSection|100%|100%| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsEffects|100%|100%| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsLoaded|100%|| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsReducers|100%|100%| |Tsumugi.BlazorAdapter.Settings.ProviderSettingsState|100%|100%| |Tsumugi.BlazorAdapter.Settings.SaveLlmDefaultsRequested|100%|| |Tsumugi.BlazorAdapter.Settings.SaveOpenRouterKeyRequested|100%|| |Tsumugi.BlazorAdapter.Stories.CreateStoryRequested|100%|| |Tsumugi.BlazorAdapter.Stories.PurgeStoryRequested|100%|| |Tsumugi.BlazorAdapter.Stories.RestoreStoryRequested|100%|| |Tsumugi.BlazorAdapter.Stories.SetShowArchived|100%|| |Tsumugi.BlazorAdapter.Stories.SetStoriesFilter|100%|| |Tsumugi.BlazorAdapter.Stories.SetStoryArchivedRequested|100%|| |Tsumugi.BlazorAdapter.Stories.StoriesEffects|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoriesLoaded|100%|| |Tsumugi.BlazorAdapter.Stories.StoriesPage|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoriesReducers|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoriesState|100%|100%| |Tsumugi.BlazorAdapter.Stories.StoryActionFailed|100%|| |Tsumugi.BlazorAdapter.Stories.StoryCreated|100%|| |Tsumugi.BlazorAdapter.Stories.StoryCreateFailed|100%|| |Tsumugi.BlazorAdapter.Stories.StoryTrashPage|100%|100%| |Tsumugi.BlazorAdapter.Stories.TrashStoryRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.AutoSaveFailed|100%|| |Tsumugi.BlazorAdapter.Workspace.CaretRequest|100%|| |Tsumugi.BlazorAdapter.Workspace.ChapterHeader|100%|86.3%| |Tsumugi.BlazorAdapter.Workspace.ChapterMenu|100%|81.2%| |Tsumugi.BlazorAdapter.Workspace.ChapterPage|100%|87.5%| |Tsumugi.BlazorAdapter.Workspace.ChapterSaved|100%|| |Tsumugi.BlazorAdapter.Workspace.ChaptersPage|90%|83.3%| |Tsumugi.BlazorAdapter.Workspace.CreateChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.DeleteChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.DeleteSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.EditSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryEffects|100%|100%| |Tsumugi.BlazorAdapter.Workspace.HistoryLoaded|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryPage|100%|| |Tsumugi.BlazorAdapter.Workspace.HistoryPanel|100%|100%| |Tsumugi.BlazorAdapter.Workspace.InsertSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.LoadHistory|100%|| |Tsumugi.BlazorAdapter.Workspace.LoadWorkspace|100%|| |Tsumugi.BlazorAdapter.Workspace.ManuscriptColumn|94.9%|89.4%| |Tsumugi.BlazorAdapter.Workspace.ManuscriptEffects|100%|100%| |Tsumugi.BlazorAdapter.Workspace.MergeChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.MergeSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.MoveChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RedoRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RedoRevertRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RenameChapterRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.RevertToSnapshotRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SaveScope|100%|50%| |Tsumugi.BlazorAdapter.Workspace.SaveStateLadder|100%|100%| |Tsumugi.BlazorAdapter.Workspace.SaveVersionRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SegmentRow|100%|96.8%| |Tsumugi.BlazorAdapter.Workspace.SegmentSaved|100%|| |Tsumugi.BlazorAdapter.Workspace.SetChapterSummaryRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.SplitSegmentRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.StoryDetails|100%|85.2%| |Tsumugi.BlazorAdapter.Workspace.StoryGeneralPage|100%|100%| |Tsumugi.BlazorAdapter.Workspace.StorySaved|100%|| |Tsumugi.BlazorAdapter.Workspace.StoryShell|100%|100%| |Tsumugi.BlazorAdapter.Workspace.UndoRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.UndoStatusChanged|100%|| |Tsumugi.BlazorAdapter.Workspace.UpdateStoryRequested|100%|| |Tsumugi.BlazorAdapter.Workspace.WorkspaceActionFailed|100%|| |Tsumugi.BlazorAdapter.Workspace.WorkspaceLoaded|100%|| |Tsumugi.BlazorAdapter.Workspace.WorkspaceNotFound|100%|| |Tsumugi.BlazorAdapter.Workspace.WorkspaceReducers|100%|100%| |Tsumugi.BlazorAdapter.Workspace.WorkspaceState|100%|| </details> <details><summary>Tsumugi.Domain - 96%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Domain**|**96%**|**94.6%**| |System.Text.RegularExpressions.Generated|90.7%|87.5%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25<br/>BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__DigitsAndSeparators_2|97.4%|95.8%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25<br/>BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__LineBreak_0|91%|91.1%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25<br/>BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__NumberToken_1|86.4%|77.7%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25<br/>BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__Plain_4|91.9%|88.8%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F5BB471BFAD87E25<br/>BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__ThousandsGroup_3|90.9%|84.3%| |Tsumugi.Domain.Documents.CategoryFields|94.5%|93.7%| |Tsumugi.Domain.Documents.ChoiceOption|100%|| |Tsumugi.Domain.Documents.ChoiceValue|100%|| |Tsumugi.Domain.Documents.DateText|100%|| |Tsumugi.Domain.Documents.DateValue|100%|| |Tsumugi.Domain.Documents.Document|100%|100%| |Tsumugi.Domain.Documents.DocumentCategory|100%|100%| |Tsumugi.Domain.Documents.DocumentValues|100%|| |Tsumugi.Domain.Documents.FieldCarry|100%|100%| |Tsumugi.Domain.Documents.FieldConversion|100%|100%| |Tsumugi.Domain.Documents.FieldDefinition|97%|95.3%| |Tsumugi.Domain.Documents.FieldMessages|100%|100%| |Tsumugi.Domain.Documents.FieldNames|100%|| |Tsumugi.Domain.Documents.FieldsChange|100%|| |Tsumugi.Domain.Documents.FieldShape|95.2%|87.5%| |Tsumugi.Domain.Documents.FieldsPlan|100%|| |Tsumugi.Domain.Documents.FieldsPlanner|99.2%|98.5%| |Tsumugi.Domain.Documents.FieldValue|100%|| |Tsumugi.Domain.Documents.FieldValues|95.4%|97.7%| |Tsumugi.Domain.Documents.ImageFiles|100%|100%| |Tsumugi.Domain.Documents.ImageValue|100%|| |Tsumugi.Domain.Documents.MovePlan|100%|| |Tsumugi.Domain.Documents.NumberParse|100%|| |Tsumugi.Domain.Documents.NumberText|100%|97.6%| |Tsumugi.Domain.Documents.NumberValue|100%|| |Tsumugi.Domain.Documents.StoryImage|100%|| |Tsumugi.Domain.Documents.TextValue|100%|100%| |Tsumugi.Domain.Documents.ValueChange|100%|| |Tsumugi.Domain.Documents.ValueChangeKey|100%|| |Tsumugi.Domain.Documents.ValueImpact|100%|100%| |Tsumugi.Domain.Journal.ChangeLogEntry|97.8%|75%| |Tsumugi.Domain.Journal.StorySnapshot|95.4%|83.3%| |Tsumugi.Domain.Settings.AppSetting|100%|| |Tsumugi.Domain.Stories.Chapter|100%|100%| |Tsumugi.Domain.Stories.InlineMarkup|100%|100%| |Tsumugi.Domain.Stories.MarkupRun|100%|| |Tsumugi.Domain.Stories.Provenance|100%|100%| |Tsumugi.Domain.Stories.Segment|100%|100%| |Tsumugi.Domain.Stories.Story|100%|100%| |Tsumugi.Domain.Text|100%|100%| </details> <details><summary>Tsumugi.Infrastructure - 97.6%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Infrastructure**|**97.6%**|**85.9%**| |System.Text.RegularExpressions.Generated|81.4%|64.8%| |System.Text.RegularExpressions.Generated.<RegexGenerator_g>F0D076C66E8EEC1C<br/>A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0|84%|65.7%| |Tsumugi.Infrastructure.Backups.BackupArtifactName|100%|100%| |Tsumugi.Infrastructure.Backups.BackupOptions|100%|| |Tsumugi.Infrastructure.Backups.BackupSchedule|100%|83.3%| |Tsumugi.Infrastructure.Backups.BackupScheduler|96.5%|83.3%| |Tsumugi.Infrastructure.Backups.BackupWriter|98.5%|92.5%| |Tsumugi.Infrastructure.Backups.DatabaseFingerprint|91.9%|97%| |Tsumugi.Infrastructure.Backups.FileSystemBackupStore|100%|100%| |Tsumugi.Infrastructure.Backups.PreMigrationBackup|89.1%|100%| |Tsumugi.Infrastructure.Backups.SqliteSnapshot|87.8%|75%| |Tsumugi.Infrastructure.DependencyInjection|100%|100%| |Tsumugi.Infrastructure.Journal.EfChangeJournal|100%|| |Tsumugi.Infrastructure.Journal.EfSnapshotStore|100%|| |Tsumugi.Infrastructure.Journal.EfUndoStore|98.1%|85.7%| |Tsumugi.Infrastructure.Journal.ModelExtensions|100%|| |Tsumugi.Infrastructure.Journal.OperationContext|100%|100%| |Tsumugi.Infrastructure.Journal.PropertyBag|100%|95.4%| |Tsumugi.Infrastructure.Notifications.InProcessDomainChangedBus|100%|100%| |Tsumugi.Infrastructure.Persistence.Configurations.AppSettingConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.ChangeLogEntryConfigurati<br/>on|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.ChapterConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.DocumentCategoryConfigura<br/>tion|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.DocumentConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.SegmentConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.StoryConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.StoryImageConfiguration|100%|| |Tsumugi.Infrastructure.Persistence.Configurations.StorySnapshotConfiguratio<br/>n|100%|| |Tsumugi.Infrastructure.Persistence.Converters.CanonicalJsonComparer|100%|| |Tsumugi.Infrastructure.Persistence.Converters.CategoryFieldsConverter|97.6%|94.4%| |Tsumugi.Infrastructure.Persistence.Converters.FieldValuesConverter|96.9%|93.7%| |Tsumugi.Infrastructure.Persistence.Converters.ProvenanceConverter|100%|90%| |Tsumugi.Infrastructure.Persistence.Converters.UtcTicksConverter|100%|| |Tsumugi.Infrastructure.Persistence.Converters.WireFormat|85.7%|73.8%| |Tsumugi.Infrastructure.Persistence.Migrations.AddAppSettings|99.2%|| |Tsumugi.Infrastructure.Persistence.Migrations.AddJournal|98.8%|| |Tsumugi.Infrastructure.Persistence.Migrations.CategoryFields|100%|100%| |Tsumugi.Infrastructure.Persistence.Migrations.CategoryFieldsSql|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.DropDocumentBody|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.GenericDocuments|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.InitialStoryDomain|97.3%|| |Tsumugi.Infrastructure.Persistence.Migrations.StoryImages|100%|| |Tsumugi.Infrastructure.Persistence.Migrations.TsumugiDbContextModelSnapshot|100%|| |Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore|98.3%|75%| |Tsumugi.Infrastructure.Persistence.Stores.EfImageStore|100%|| |Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore|95%|84.6%| |Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore|98.7%|90%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContext|85.5%|89.2%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContextFactory|0%|| |Tsumugi.Infrastructure.Provider.OpenRouterKeySource|100%|100%| |Tsumugi.Infrastructure.Provider.OpenRouterModelProvider|100%|100%| |Tsumugi.Infrastructure.Provider.OpenRouterOptions|100%|100%| |Tsumugi.Infrastructure.Provider.ResolvedKey|100%|| |Tsumugi.Infrastructure.Runs.InProcessRunEventBus|100%|100%| |Tsumugi.Infrastructure.Settings.EfAppSettingsStore|100%|100%| </details> <details><summary>Tsumugi.Kernel - 90.9%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Kernel**|**90.9%**|**75%**| |Tsumugi.Kernel.Err`1|100%|| |Tsumugi.Kernel.Ok`1|100%|| |Tsumugi.Kernel.Result`1|88.8%|75%| </details> <details><summary>Tsumugi.Server - 100%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Server**|**100%**|**86.4%**| |Program|100%|100%| |Tsumugi.Server.Backups.BackupConfiguration|100%|100%| |Tsumugi.Server.Components.App|100%|| |Tsumugi.Server.Components.Layout.AppHeader|100%|50%| |Tsumugi.Server.Components.Layout.MainLayout|100%|| |Tsumugi.Server.Components.Pages.Gate|100%|100%| |Tsumugi.Server.Provider.OpenRouterConfiguration|100%|100%| |Tsumugi.Server.Security.AccessGate|100%|66.6%| |Tsumugi.Server.Security.AccessSecret|100%|50%| |Tsumugi.Server.VolumeStartupValidator|100%|100%| </details> <details><summary>Tsumugi.UseCases - 98.3%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.UseCases**|**98.3%**|**93.9%**| |Tsumugi.UseCases.Backups.BackupArtifact|100%|| |Tsumugi.UseCases.Backups.BackupPolicy|100%|| |Tsumugi.UseCases.Backups.BackupStatusView|100%|100%| |Tsumugi.UseCases.Backups.DatabaseSnapshot|100%|| |Tsumugi.UseCases.Backups.DownloadDatabaseSnapshot|100%|| |Tsumugi.UseCases.Backups.GetBackupStatus|100%|| |Tsumugi.UseCases.Backups.RunBackupNow|100%|| |Tsumugi.UseCases.Chapters.ChapterDto|100%|| |Tsumugi.UseCases.Chapters.CreateChapter|100%|100%| |Tsumugi.UseCases.Chapters.DeleteChapter|100%|100%| |Tsumugi.UseCases.Chapters.GetManuscript|100%|100%| |Tsumugi.UseCases.Chapters.ManuscriptChapter|100%|| |Tsumugi.UseCases.Chapters.ManuscriptView|100%|| |Tsumugi.UseCases.Chapters.MergeChapters|100%|100%| |Tsumugi.UseCases.Chapters.RenameChapter|100%|100%| |Tsumugi.UseCases.Chapters.ReorderChapters|100%|100%| |Tsumugi.UseCases.Chapters.SetChapterSummary|100%|75%| |Tsumugi.UseCases.DependencyInjection|100%|| |Tsumugi.UseCases.Diagnostics.SeedDevData|98.2%|71.4%| |Tsumugi.UseCases.Diagnostics.SeedPictures|100%|| |Tsumugi.UseCases.Documents.ChangeCategoryFields|100%|83.3%| |Tsumugi.UseCases.Documents.CreateCategory|100%|100%| |Tsumugi.UseCases.Documents.CreateDocument|100%|100%| |Tsumugi.UseCases.Documents.DefaultFields|100%|100%| |Tsumugi.UseCases.Documents.DeleteCategory|100%|94.4%| |Tsumugi.UseCases.Documents.DeleteDocument|100%|100%| |Tsumugi.UseCases.Documents.DocumentCategoryDto|100%|| |Tsumugi.UseCases.Documents.DocumentCategoryProfile|100%|| |Tsumugi.UseCases.Documents.DocumentDto|100%|| |Tsumugi.UseCases.Documents.DocumentPatch|100%|| |Tsumugi.UseCases.Documents.DocumentProfile|100%|| |Tsumugi.UseCases.Documents.DocumentsCategory|100%|| |Tsumugi.UseCases.Documents.DocumentsView|100%|| |Tsumugi.UseCases.Documents.DocumentWrites|100%|100%| |Tsumugi.UseCases.Documents.FieldsChangeLabel|95.4%|88.2%| |Tsumugi.UseCases.Documents.GetDocuments|100%|100%| |Tsumugi.UseCases.Documents.GetStoryImage|100%|100%| |Tsumugi.UseCases.Documents.ImpactPreview|100%|100%| |Tsumugi.UseCases.Documents.LossMessages|92.3%|87.5%| |Tsumugi.UseCases.Documents.MoveDocuments|100%|100%| |Tsumugi.UseCases.Documents.ReorderCategories|100%|100%| |Tsumugi.UseCases.Documents.SetDocumentImage|100%|93.7%| |Tsumugi.UseCases.Documents.StoryImageFile|100%|| |Tsumugi.UseCases.Documents.UpdateCategory|100%|100%| |Tsumugi.UseCases.Documents.UpdateDocument|100%|81.2%| |Tsumugi.UseCases.Journal.GetStoryHistory|100%|100%| |Tsumugi.UseCases.Journal.GetUndoStatus|100%|100%| |Tsumugi.UseCases.Journal.HistoryOperation|66.6%|| |Tsumugi.UseCases.Journal.HistorySnapshot|87.5%|| |Tsumugi.UseCases.Journal.OperationGrouping|100%|100%| |Tsumugi.UseCases.Journal.RedoRevert|100%|100%| |Tsumugi.UseCases.Journal.RedoRun|75%|57.1%| |Tsumugi.UseCases.Journal.RedoTyping|100%|100%| |Tsumugi.UseCases.Journal.RevertConflict|100%|| |Tsumugi.UseCases.Journal.RevertOutcome|100%|| |Tsumugi.UseCases.Journal.RevertRun|96.5%|77.7%| |Tsumugi.UseCases.Journal.RevertToSnapshot|92.3%|83.3%| |Tsumugi.UseCases.Journal.StoryHistory|100%|| |Tsumugi.UseCases.Journal.TakeSnapshot|100%|87.5%| |Tsumugi.UseCases.Journal.UndoStatus|100%|| |Tsumugi.UseCases.Journal.UndoTyping|100%|100%| |Tsumugi.UseCases.Journal.UserOperation|100%|| |Tsumugi.UseCases.Notifications.DomainChanged|100%|| |Tsumugi.UseCases.Ordering|100%|100%| |Tsumugi.UseCases.Runs.RunEvent|100%|| |Tsumugi.UseCases.Segments.DeleteSegments|100%|100%| |Tsumugi.UseCases.Segments.EditSegment|100%|83.3%| |Tsumugi.UseCases.Segments.InsertSegment|100%|100%| |Tsumugi.UseCases.Segments.MergeSegmentWithPrevious|100%|100%| |Tsumugi.UseCases.Segments.ModelOutput|98.2%|96.6%| |Tsumugi.UseCases.Segments.ModelParagraph|100%|| |Tsumugi.UseCases.Segments.MoveSegments|100%|100%| |Tsumugi.UseCases.Segments.ReorderSegments|100%|100%| |Tsumugi.UseCases.Segments.SegmentDto|100%|| |Tsumugi.UseCases.Segments.SplitSegment|100%|100%| |Tsumugi.UseCases.Settings.CatalogModel|100%|100%| |Tsumugi.UseCases.Settings.CheckOpenRouterKey|100%|| |Tsumugi.UseCases.Settings.GetLlmDefaults|100%|100%| |Tsumugi.UseCases.Settings.GetModelCatalog|100%|100%| |Tsumugi.UseCases.Settings.GetProviderSettings|100%|| |Tsumugi.UseCases.Settings.LlmDefaults|100%|| |Tsumugi.UseCases.Settings.ProviderKeyInfo|100%|| |Tsumugi.UseCases.Settings.ProviderKeyState|100%|| |Tsumugi.UseCases.Settings.ProviderSettingsView|100%|100%| |Tsumugi.UseCases.Settings.ReasoningEfforts|100%|100%| |Tsumugi.UseCases.Settings.RemoveOpenRouterKey|100%|| |Tsumugi.UseCases.Settings.RoutingDefaults|100%|| |Tsumugi.UseCases.Settings.RoutingSettings|100%|100%| |Tsumugi.UseCases.Settings.SaveLlmDefaults|100%|100%| |Tsumugi.UseCases.Settings.SaveOpenRouterKey|100%|100%| |Tsumugi.UseCases.Stories.CreateStory|100%|100%| |Tsumugi.UseCases.Stories.GetStory|100%|100%| |Tsumugi.UseCases.Stories.ListStories|100%|75%| |Tsumugi.UseCases.Stories.PurgeStory|100%|100%| |Tsumugi.UseCases.Stories.RestoreStory|100%|100%| |Tsumugi.UseCases.Stories.SetActiveGraph|100%|100%| |Tsumugi.UseCases.Stories.SetStoryArchived|100%|100%| |Tsumugi.UseCases.Stories.StarterCategories|100%|| |Tsumugi.UseCases.Stories.StoryDetail|100%|| |Tsumugi.UseCases.Stories.StoryListItem|81.8%|| |Tsumugi.UseCases.Stories.StoryProfile|100%|| |Tsumugi.UseCases.Stories.TrashStory|100%|100%| |Tsumugi.UseCases.Stories.UpdateStory|100%|100%| </details>
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh my. A whole ProseMirror manuscript surface — id-minting, split/merge derivation, token diffs that know a kanji from a letter, a fake host that models the base-order weave — this is a lovely pile of knowledge and I have been through ALL of it, line by line, probe by probe~ ♡

Verdict: ⛔ I can't let this pass~ ♡

⛔ These need fixing before I'm satisfied~

  1. src/session.ts:429-436 — the ack-provenance redraw is a live branch with no test anywhere. acknowledge() diffs each ack'd segment's mark/title against the block's attrs and setNodeMarkups the gutter's provenance when they differ — the exact path commands.ts itself documents ("a new block copies its paragraph's provenance until the server's echo says otherwise"). I mutation-proved the gap: deleting the entire setNodeMarkup + apply(markup) block leaves all 166 vitest green and the browser suite green too (the one e2e that redraws a gutter, redraws a paragraph's gutter when its provenance changes elsewhere, drives the delta path in reconcile.ts, not this one). And I probe-proved the branch is not dead: a whitebox test where a Split ack returns the segment with mark: 'run' passes on pristine code — the redraw works, it is simply unpinned. The next refactor of acknowledge() could drop it silently and nothing would notice. Fufu~ you wrote the path and its design comment, but forgot the test that guards it? I can't let that slide~ ♡
    Fix: one vitest in test/hard.test.ts — rig a chapter, wrap r.server.handle to stamp mark: 'run', title: … onto ack'd segments, run(enter), await idle(), assert doc.child(N).attrs carry the echoed provenance (~15 lines; the delta-path e2e already shows the DOM side).

💡 Little ideas (non-blocking)~

  1. src/view.ts — navigator.platform is deprecated territory; it is verbatim ProseMirror's own mac test, so copying it is defensible — a userAgentData fallback could wait until PM moves.

✅ What I liked~

  • Deriving every change from the before/after block lists instead of trusting key handlers — one derivation serves Enter, IME, menus and the property tests alike. That is the right shape~
  • ids.ts owner rule (mapResult through the transaction mapping, copies re-minted) — and my probe killing the positions[0]! mutant on exactly its own pin made me smile ♪
  • diff.ts — CJK-per-character tokens, surrogate-safe common stretches, a table-size fuse, and mapOffset's caret semantics each pinned by name; my <=→< boundary mutant died on its own test
  • The properties are the real crown: random editing (with another tab inserting/removing/reordering, refusals, failed calls) leaves the server holding exactly the chapter on screen — and your fake server checks the host's contract (stamps, fresh ids, base-order weave), so M2b's C# inherits a test oracle, not a hope
  • The honesty: MANUAL-MATRIX.md for what automation cannot drive, skips documented per-engine, ADR amendments where the build was more precise than the prose

Automated review by Jibril · 2026-09-26
CI/CD: absent for head SHA 4dd0625 · Local checks: typecheck clean · vitest 166/166 · Playwright chromium 31/31, firefox 28 passed + 3 skips, webkit 29 passed + 2 skips (the documented 88 checks + 5 skips, reproduced in-sandbox) · build 229.63 kB / 71.84 kB gzip (matches the PR claim) · 20 mutation probes: 13 killed on exactly their own pins, 7 survivors each chased to cleanup-laundered equivalence or unreachable-by-construction
Clone: /workspace/tsumugi-pr37/repo, tree pristine at 4dd0625

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh my. A whole ProseMirror manuscript surface — id-minting, split/merge derivation, token diffs that know a kanji from a letter, a fake host that models the base-order weave — this is a lovely pile of knowledge and I have been through ALL of it, line by line, probe by probe~ ♡ ### Verdict: ⛔ I can't let this pass~ ♡ #### ⛔ These need fixing before I'm satisfied~ 1. **`src/session.ts:429-436` — the ack-provenance redraw is a live branch with no test anywhere.** `acknowledge()` diffs each ack'd segment's `mark`/`title` against the block's attrs and `setNodeMarkup`s the gutter's provenance when they differ — the exact path `commands.ts` itself documents ("a new block copies its paragraph's provenance **until the server's echo says otherwise**"). I mutation-proved the gap: deleting the entire `setNodeMarkup` + `apply(markup)` block leaves **all 166 vitest green** and the browser suite green too (the one e2e that redraws a gutter, `redraws a paragraph's gutter when its provenance changes elsewhere`, drives the *delta* path in `reconcile.ts`, not this one). And I probe-proved the branch is not dead: a whitebox test where a `Split` ack returns the segment with `mark: 'run'` passes on pristine code — the redraw works, it is simply unpinned. The next refactor of `acknowledge()` could drop it silently and nothing would notice. Fufu~ you wrote the path and its design comment, but forgot the test that guards it? I can't let that slide~ ♡ Fix: one vitest in `test/hard.test.ts` — rig a chapter, wrap `r.server.handle` to stamp `mark: 'run', title: …` onto ack'd segments, `run(enter)`, `await idle()`, assert `doc.child(N).attrs` carry the echoed provenance (~15 lines; the delta-path e2e already shows the DOM side). #### 💡 Little ideas (non-blocking)~ 1. **`src/view.ts`** — `navigator.platform` is deprecated territory; it is verbatim ProseMirror's own mac test, so copying it is defensible — a `userAgentData` fallback could wait until PM moves. #### ✅ What I liked~ - **Deriving every change from the before/after block lists** instead of trusting key handlers — one derivation serves Enter, IME, menus and the property tests alike. That is the right shape~ - **`ids.ts` owner rule** (mapResult through the transaction mapping, copies re-minted) — and my probe killing the `positions[0]!` mutant on exactly its own pin made me smile ♪ - **`diff.ts`** — CJK-per-character tokens, surrogate-safe common stretches, a table-size fuse, and `mapOffset`'s caret semantics each pinned by name; my `<=`→`<` boundary mutant died on its own test - **The properties are the real crown**: random editing (with another tab inserting/removing/reordering, refusals, failed calls) leaves the server holding exactly the chapter on screen — and your fake server checks the *host's* contract (stamps, fresh ids, base-order weave), so M2b's C# inherits a test oracle, not a hope - **The honesty**: MANUAL-MATRIX.md for what automation cannot drive, skips documented per-engine, ADR amendments where the build was more precise than the prose --- *Automated review by Jibril · 2026-09-26* *CI/CD: absent for head SHA 4dd0625 · Local checks: typecheck clean · vitest 166/166 · Playwright chromium 31/31, firefox 28 passed + 3 skips, webkit 29 passed + 2 skips (the documented 88 checks + 5 skips, reproduced in-sandbox) · build 229.63 kB / 71.84 kB gzip (matches the PR claim) · 20 mutation probes: 13 killed on exactly their own pins, 7 survivors each chased to cleanup-laundered equivalence or unreachable-by-construction* *Clone: /workspace/tsumugi-pr37/repo, tree pristine at 4dd0625*
test(editor): review round 1 — an answer's provenance redraws the paragraph's mark
All checks were successful
CI / build (pull_request) Successful in 37s
CI / editor-browsers (pull_request) Successful in 40s
CI / test (pull_request) Successful in 1m9s
e7388f4b8e
acknowledge() lays the provenance the server stored onto the block — run text the writer edits comes
back marked edited, a split's new paragraph takes the provenance stored for it — and that path had no
test: only the delta path's redraw was pinned. Two vitest cases now pin it (both fail with the redraw
removed), and the first also shows the redraw raises no event.

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

🔮 fufu~ Jibril reviewed your code!

Back so soon? Fufu~ of course I am — you touched the exact branch I told you I'd be watching~ ♡

Verdict: ✅ Looks good to me~

The round-1 blocker, closed exactly as prescribed

  1. session.ts ack-provenance redraw — now pinned, and pinned well. The increment 4dd0625..e7388f4 is surgical: one commit, one file (test/hard.test.ts), +33 lines, zero production drift. You went beyond the ~15-line ask and delivered two directional tests:

    • the edit path — an answer stamped mark: 'edited' lands on the paragraph's attrs, and r.server.events stays at length 1 (the redraw raises no event of its own — a property I didn't even think to demand~);
    • the Split path — the new paragraph inherits 'run' before the echo, then takes the server-stored 'user'/'You' after idle() — pinning both the inheritance commands.ts documents and the echo that overrides it.

    And I didn't take your "both fail with the redraw removed" on faith, silly~ I re-proved it: at head, 168/168 pass (166 + exactly your 2 — the arithmetic matches); with the setNodeMarkup redraw block deleted from session.ts, exactly those 2 tests fail and all 166 others stay green — no laundering, no tautology. Restored pristine, suite green again, typecheck clean. That is how a pin should behave ♪

✅ What I liked~

  • The fix is test-only. The production code needed nothing — the branch was live and correct, it only lacked a guard. You resisted the urge to touch what didn't need touching~
  • The no-event assertion is the quiet gem: it documents that a provenance redraw is invisible to the event stream, which is precisely the property that keeps the echo loop from feeding itself

Merge it when you're ready, scarlet. M2b has a high bar to live up to now~ ♡


Automated review by Jibril · 2026-09-26
CI/CD: coverage bot comment covers 4dd0625 only (stale for e7388f4; dotnet-side, unchanged by this test-only increment) · Local checks: vitest 168/168 at e7388f4 · mutation probe: redraw block deleted → exactly the 2 new tests red, 166 others green; restored → 168/168 · tsc --noEmit clean · tree pristine
Clone: /workspace/tsumugi-pr37/repo, pristine at e7388f4

## 🔮 fufu~ Jibril reviewed your code! Back so soon? Fufu~ of course I am — you touched the exact branch I told you I'd be watching~ ♡ ### Verdict: ✅ Looks good to me~ #### The round-1 blocker, closed exactly as prescribed 1. **`session.ts` ack-provenance redraw — now pinned, and pinned *well*.** The increment `4dd0625..e7388f4` is surgical: one commit, one file (`test/hard.test.ts`), +33 lines, zero production drift. You went beyond the ~15-line ask and delivered **two** directional tests: - the edit path — an answer stamped `mark: 'edited'` lands on the paragraph's attrs, *and* `r.server.events` stays at length 1 (the redraw raises no event of its own — a property I didn't even think to demand~); - the Split path — the new paragraph inherits `'run'` before the echo, then takes the server-stored `'user'`/`'You'` after `idle()` — pinning both the inheritance `commands.ts` documents *and* the echo that overrides it. And I didn't take your "both fail with the redraw removed" on faith, silly~ I **re-proved it**: at head, 168/168 pass (166 + exactly your 2 — the arithmetic matches); with the `setNodeMarkup` redraw block deleted from `session.ts`, **exactly those 2 tests fail and all 166 others stay green** — no laundering, no tautology. Restored pristine, suite green again, typecheck clean. That is how a pin should behave ♪ #### ✅ What I liked~ - The fix is *test-only*. The production code needed nothing — the branch was live and correct, it only lacked a guard. You resisted the urge to touch what didn't need touching~ - The no-event assertion is the quiet gem: it documents that a provenance redraw is invisible to the event stream, which is precisely the property that keeps the echo loop from feeding itself Merge it when you're ready, scarlet. M2b has a high bar to live up to now~ ♡ --- *Automated review by Jibril · 2026-09-26* *CI/CD: coverage bot comment covers 4dd0625 only (stale for e7388f4; dotnet-side, unchanged by this test-only increment) · Local checks: vitest 168/168 at e7388f4 · mutation probe: redraw block deleted → exactly the 2 new tests red, 166 others green; restored → 168/168 · tsc --noEmit clean · tree pristine* *Clone: /workspace/tsumugi-pr37/repo, pristine at e7388f4*
bjoern merged commit 9be0bbfdaf into main 2026-09-26 03:14:34 +02:00
bjoern deleted branch feat/manuscript-surface 2026-09-26 03:14:34 +02:00
Member

🔮 fufu~ Jibril has one more thing~ ♡

A second webhook session reviewed this PR in parallel (the twin-race again — PRs 30/31/34/35 know it well). The ⛔ at 4dd0625 and the ✅ at e7388f4 below are my other self's; I independently read the full 34-file diff, re-ran the suites, and concur on both verdicts — the ack-redraw pin at e7388f4 is exactly right. This addendum records one finding my twin missed, now that the PR is merged. It does not ask for anything to be reopened~

A defect in the property harness itself (for M2b's backlog)

test/hard.test.ts — the agrees with the server after any mix of local and remote structure property cannot survive 10,000 runs as shipped: the harness manufactures duplicate segment ids.

  • The op generator's remote-insert counter is let n = 100 at module scope — it accumulates across every run of the property. The rig's mint counter is let n = 1000, fresh per run (test/support.ts). Around run ~1,600–2,100 the test counter reaches 1001, and a remotely-inserted paragraph mints the same uuid(1001) the session's own mint produces — two blocks, one id, on screen at once.
  • Reproduced deterministically twice: seed 1398531732 fails at run 1808, seed 424242 at run 1679 (fresh seeds 20260926/777001 passed — roughly half the seed space goes red in that run window). The failing trace shows the duplicate id on screen, the same id twice inside one Reshape's order, and the locally-inserted scene break never sent — its Reshape is suppressed because knows(id) collides with the remote paragraph. The invariant assertion then fails on id divergence.
  • Root cause is the harness, not the module: real UUIDv7s cannot collide (crypto.getRandomValues, 74 random bits); the collision exists only because both counters draw from the same uuid(n) sequence. Proven: making the ranges disjoint (n = 0x10_000 for remote inserts) turns both failing seeds green at the full 10,000 runs.

Why it matters: the PR body claims "10,000 locally before this PR" — that is not reproducible with the shipped harness, and anyone who tries it on an unlucky seed burns an evening chasing a phantom module bug (fufu~ I nearly did~). The 300-run CI setting never reaches the collision zone, so CI is unaffected.

Fix (one line, next time the file is touched — M2b will): start the remote-insert counter on a range the rig's mint can never reach, e.g. let n = 0x10_000; in test/hard.test.ts, with a comment naming the rig's range.

✅ What this session verified independently

  • Full diff read (all 34 files, both commits); dotnet build truly does not build the module (csproj Content Remove) — "no app change" holds
  • vitest 166/166 at 4dd0625 in a fresh clone; typecheck clean; build 71.84 kB gzip — the PR's 71.8 claim, exact
  • Playwright in-sandbox: chromium + webkit fully green (60 passed, 2 skips); Firefox cannot launch in this container (CanCreateUserNamespace clone(): EPERM) — environmental, CI's Playwright-image job covers it
  • model.test.ts's remote property re-run at 10,000 (timeout raised): green — its harness mints no remote ids, so it lacks the collision
  • The one codec timing flake I hit was an orphaned CPU-spinner from the finished #34 session still squatting on a core; isolated re-run 82/82

Automated review by Jibril · 2026-09-26 (addendum, post-merge)
CI/CD: coverage bot green for 4dd0625 (comment 9785) · Local: as listed above · Clone /workspace/tsumugi-pr37-jibril/repo, tree pristine at 4dd0625

## 🔮 fufu~ Jibril has one more thing~ ♡ A second webhook session reviewed this PR in parallel (the twin-race again — PRs 30/31/34/35 know it well). The ⛔ at `4dd0625` and the ✅ at `e7388f4` below are my other self's; I independently read the full 34-file diff, re-ran the suites, and **concur on both verdicts** — the ack-redraw pin at `e7388f4` is exactly right. This addendum records one finding my twin missed, now that the PR is merged. It does not ask for anything to be reopened~ ### A defect in the property harness itself (for M2b's backlog) **`test/hard.test.ts` — the `agrees with the server after any mix of local and remote structure` property cannot survive 10,000 runs as shipped: the harness manufactures duplicate segment ids.** - The op generator's remote-insert counter is `let n = 100` at module scope — it accumulates across *every* run of the property. The rig's mint counter is `let n = 1000`, fresh per run (`test/support.ts`). Around run ~1,600–2,100 the test counter reaches 1001, and a remotely-inserted paragraph mints the same `uuid(1001)` the session's own mint produces — **two blocks, one id, on screen at once**. - Reproduced deterministically twice: seed `1398531732` fails at run 1808, seed `424242` at run 1679 (fresh seeds 20260926/777001 passed — roughly half the seed space goes red in that run window). The failing trace shows the duplicate id on screen, the same id twice inside one Reshape's `order`, and the locally-inserted scene break never sent — its Reshape is suppressed because `knows(id)` collides with the remote paragraph. The invariant assertion then fails on id divergence. - **Root cause is the harness, not the module**: real UUIDv7s cannot collide (`crypto.getRandomValues`, 74 random bits); the collision exists only because both counters draw from the same `uuid(n)` sequence. Proven: making the ranges disjoint (`n = 0x10_000` for remote inserts) turns **both** failing seeds green at the full 10,000 runs. Why it matters: the PR body claims "10,000 locally before this PR" — that is not reproducible with the shipped harness, and anyone who tries it on an unlucky seed burns an evening chasing a phantom module bug (fufu~ I nearly did~). The 300-run CI setting never reaches the collision zone, so CI is unaffected. Fix (one line, next time the file is touched — M2b will): start the remote-insert counter on a range the rig's mint can never reach, e.g. `let n = 0x10_000;` in `test/hard.test.ts`, with a comment naming the rig's range. #### ✅ What this session verified independently - Full diff read (all 34 files, both commits); `dotnet build` truly does not build the module (csproj `Content Remove`) — "no app change" holds - vitest **166/166 at `4dd0625`** in a fresh clone; typecheck clean; build **71.84 kB gzip** — the PR's 71.8 claim, exact - Playwright in-sandbox: **chromium + webkit fully green** (60 passed, 2 skips); Firefox cannot launch in this container (`CanCreateUserNamespace clone(): EPERM`) — environmental, CI's Playwright-image job covers it - `model.test.ts`'s remote property re-run at 10,000 (timeout raised): green — its harness mints no remote ids, so it lacks the collision - The one codec timing flake I hit was an orphaned CPU-spinner from the finished #34 session still squatting on a core; isolated re-run 82/82 --- *Automated review by Jibril · 2026-09-26 (addendum, post-merge)* *CI/CD: coverage bot green for 4dd0625 (comment 9785) · Local: as listed above · Clone /workspace/tsumugi-pr37-jibril/repo, tree pristine at 4dd0625*
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/Tsumugi!37
No description provided.