feat: category fields — each category defines its documents' fields (text, number with unit, choice, date), ADR 0028 #25

Merged
bjoern merged 14 commits from feat/category-fields into main 2026-09-25 18:42:27 +02:00
Member

What

A category now defines its documents' fields. Before, every document had one free-form Body. Now the writer designs each category's fields in the category editor:

  • Short text and long text (markdown)
  • Number with an optional unit (Height (cm)). A decimal comma is read (1,72).
  • Choice from the field's options (Gender: Female / Male / Other)
  • Date

Fields can be added, renamed, retyped, reordered (by drag or by keyboard) and removed. Options work the same way. A new category starts with one long-text Body, so it behaves as before. The document editor shows one input per field, labelled with the field's name and unit.

Any change that would remove, flatten or rewrite values asks first. The dialog names the documents and what they lose. Examples: a type change, removing a field or an option, moving a document, or deleting a category with its documents moved. The save sends exactly what the dialog showed. The store checks it again under the write lock and refuses anything beyond it. Every change is one journaled operation: one Ctrl+Z or one History entry restores every value it touched.

This is also the groundwork for the tools (ADR 0019): a model will read and write structured field values, not one blob of text.

Decisions (binding docs in this PR)

  • ADR 0028 — Category fields, Proposed. It flips to Accepted in this PR once you've read it.
  • ADRs amended:
    • 0013: a document is a title, a description and its category's fields.
    • 0017: how a document renders its fields for a model.
    • 0019: the document tools read and write field values.
    • 0022: a change to fields is one operation; a migration that moves content rewrites the journal's snapshots.
    • 0026: one backup before an existing database migrates.
    • 0027: the category editor holds the fields.
  • Also updated: docs/adr/README.md, REQUIREMENTS.md, stories/documents.md, stories/graph-editor.md, PROJECT_PLAN.md, ARCHITECTURE.md and AGENTS.md. AGENTS.md gains registry rules for a new field kind, a new store mutator and a content-moving migration.

How it's built

  • Domain (Documents/Fields/):
    • The field model: definitions, values and number/date text.
    • Pure planners that the dialog and the save both run: FieldsPlanner, FieldConversion, FieldCarry and ValueImpact.
  • Use cases:
    • ChangeCategoryFields.
    • Updated: create, update, move and delete, plus GetDocuments.
    • DocumentWrites: the decisions every store makes under its lock, shared by the EF store and the test double.
  • Persistence:
    • Two versioned JSON TEXT columns, DocumentCategories.Fields and Documents.FieldValues, with strict hand-written converters.
    • TsumugiDbContext.WriteAsync takes BEGIN IMMEDIATE before the tracked load, publishes after the commit, and clears the tracker on every exit.
  • Migrations:
    • CategoryFields adds the columns and moves each Body into its category's Body field. It also rewrites every ChangeLog snapshot, so undo, History and Revert to here keep working across the upgrade.
    • DropDocumentBody drops the old column.
  • UI:
    • The field designer in the category editor.
    • Per-field inputs in the document editor.
    • The move dialog and the delete-category dialog show the impact.
  • Seed: Characters has a field of every kind, Lore keeps the default Body, and Places has a field removed last that Ctrl+Z brings back.

Data safety — please read before deploying

  • One backup is taken before an existing database migrates (PreMigrationBackup.MigrateAsync, called from Program.cs). If that backup fails, the app does not start and nothing is migrated.
  • The migration was not dry-run against your real data. No copy of the deployed database exists locally. What covers it:
    • migration tests on a pre-fields world with journal history (CategoryFieldsMigrationTests);
    • the pre-migration backup.
  • To dry-run it first, copy the container's tsumugi.db and start this branch against the copy:
    Tsumugi__DataPath=<dir with the copy> dotnet run --project src/Tsumugi.Server --launch-profile http
    Then open the documents and History of a story or two.
  • The fingerprint marker (your call): a crash-looping container should not take a new backup on every restart, so the backup step writes a marker. The marker holds a SHA-256 of the schema and every row, and later starts compare against it. A reviewer suggested something simpler: key the marker on the list of pending migrations. That loses only one edge case, an older build writing to the database in between. I kept the fingerprint because it is exact. It costs about 150 lines, and the ADR 0026 amendment describes it. If you prefer the simpler version, it's a small change to the code and the amendment.

Review and verification

  • Review: a four-lens review (quality, contracts, UI, data safety) ran on the branch. Its findings are fixed in the last three commits, including:

    • delete-and-move is now one transaction;
    • the dialogs freeze what they show;
    • unit inference only takes suffixes that read as units (5'6, 1/2 and 1e5 stay whole values);
    • the stores' decisions live in one place.
  • Not changed: the "1 document / A, B and C" list wording still has two implementations, LossMessages and ImpactText. Merging them is low value.

  • Tests: dotnet test passes 1,457 tests across six projects:

    Project Passed
    Domain 328
    UseCases 26
    Integration 282
    BlazorAdapter 297
    Kagaku.Flow 483
    Kagaku.Flow.OpenRouter 41

    Mutation checks confirmed that the new tests catch the regressions they target.

  • Browser pass on the seeded app, in light and dark mode, with no console errors. It covered:

    • every input kind;
    • 1,72 kept as typed;
    • the retype dialog, and Cancel changing nothing;
    • Add field puts the cursor in its name;
    • a keyboard reorder, twice on the same grip;
    • the move dialog, with focus returning to the Category select;
    • Ctrl+Z restoring the seeded Climate removal with its value;
    • the delete dialog naming the documents that lose values.

🤖 Generated with Claude Code

## What A category now defines its documents' fields. Before, every document had one free-form Body. Now the writer designs each category's fields in the category editor: - **Short text** and **long text** (markdown) - **Number** with an optional unit (*Height (cm)*). A decimal comma is read (*1,72*). - **Choice** from the field's options (*Gender: Female / Male / Other*) - **Date** Fields can be added, renamed, retyped, reordered (by drag or by keyboard) and removed. Options work the same way. A new category starts with one long-text **Body**, so it behaves as before. The document editor shows one input per field, labelled with the field's name and unit. **Any change that would remove, flatten or rewrite values asks first.** The dialog names the documents and what they lose. Examples: a type change, removing a field or an option, moving a document, or deleting a category with its documents moved. The save sends exactly what the dialog showed. The store checks it again under the write lock and refuses anything beyond it. Every change is **one journaled operation**: one Ctrl+Z or one History entry restores every value it touched. This is also the groundwork for the tools (ADR 0019): a model will read and write structured field values, not one blob of text. ## Decisions (binding docs in this PR) - **ADR 0028 — Category fields**, *Proposed*. It flips to Accepted in this PR once you've read it. - ADRs amended: - **0013**: a document is a title, a description and its category's fields. - **0017**: how a document renders its fields for a model. - **0019**: the document tools read and write field values. - **0022**: a change to fields is one operation; a migration that moves content rewrites the journal's snapshots. - **0026**: one backup before an existing database migrates. - **0027**: the category editor holds the fields. - Also updated: `docs/adr/README.md`, `REQUIREMENTS.md`, `stories/documents.md`, `stories/graph-editor.md`, `PROJECT_PLAN.md`, `ARCHITECTURE.md` and `AGENTS.md`. `AGENTS.md` gains registry rules for a new field kind, a new store mutator and a content-moving migration. ## How it's built - **Domain** (`Documents/Fields/`): - The field model: definitions, values and number/date text. - Pure planners that the dialog and the save both run: `FieldsPlanner`, `FieldConversion`, `FieldCarry` and `ValueImpact`. - **Use cases**: - `ChangeCategoryFields`. - Updated: create, update, move and delete, plus `GetDocuments`. - `DocumentWrites`: the decisions every store makes under its lock, shared by the EF store and the test double. - **Persistence**: - Two versioned JSON TEXT columns, `DocumentCategories.Fields` and `Documents.FieldValues`, with strict hand-written converters. - `TsumugiDbContext.WriteAsync` takes `BEGIN IMMEDIATE` before the tracked load, publishes after the commit, and clears the tracker on every exit. - **Migrations**: - `CategoryFields` adds the columns and moves each Body into its category's Body field. It also rewrites every `ChangeLog` snapshot, so undo, History and *Revert to here* keep working across the upgrade. - `DropDocumentBody` drops the old column. - **UI**: - The field designer in the category editor. - Per-field inputs in the document editor. - The move dialog and the delete-category dialog show the impact. - **Seed**: Characters has a field of every kind, Lore keeps the default Body, and Places has a field removed last that Ctrl+Z brings back. ## Data safety — please read before deploying - **One backup is taken before an existing database migrates** (`PreMigrationBackup.MigrateAsync`, called from `Program.cs`). If that backup fails, the app does not start and nothing is migrated. - **The migration was not dry-run against your real data.** No copy of the deployed database exists locally. What covers it: - migration tests on a pre-fields world with journal history (`CategoryFieldsMigrationTests`); - the pre-migration backup. - To dry-run it first, copy the container's `tsumugi.db` and start this branch against the copy: `Tsumugi__DataPath=<dir with the copy> dotnet run --project src/Tsumugi.Server --launch-profile http` Then open the documents and History of a story or two. - **The fingerprint marker (your call):** a crash-looping container should not take a new backup on every restart, so the backup step writes a marker. The marker holds a SHA-256 of the schema and every row, and later starts compare against it. A reviewer suggested something simpler: key the marker on the list of pending migrations. That loses only one edge case, an older build writing to the database in between. I kept the fingerprint because it is exact. It costs about 150 lines, and the ADR 0026 amendment describes it. If you prefer the simpler version, it's a small change to the code and the amendment. ## Review and verification - **Review:** a four-lens review (quality, contracts, UI, data safety) ran on the branch. Its findings are fixed in the last three commits, including: - delete-and-move is now one transaction; - the dialogs freeze what they show; - unit inference only takes suffixes that read as units (*5'6*, *1/2* and *1e5* stay whole values); - the stores' decisions live in one place. - **Not changed:** the "1 document / A, B and C" list wording still has two implementations, `LossMessages` and `ImpactText`. Merging them is low value. - **Tests:** `dotnet test` passes 1,457 tests across six projects: | Project | Passed | |---|---| | Domain | 328 | | UseCases | 26 | | Integration | 282 | | BlazorAdapter | 297 | | Kagaku.Flow | 483 | | Kagaku.Flow.OpenRouter | 41 | Mutation checks confirmed that the new tests catch the regressions they target. - **Browser pass** on the seeded app, in light and dark mode, with no console errors. It covered: - every input kind; - *1,72* kept as typed; - the retype dialog, and Cancel changing nothing; - Add field puts the cursor in its name; - a keyboard reorder, twice on the same grip; - the move dialog, with focus returning to the Category select; - Ctrl+Z restoring the seeded Climate removal with its value; - the delete dialog naming the documents that lose values. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Stage 1 of category fields: the domain layer and the docs.

- Domain: FieldDefinition/CategoryFields (text short|long, number with unit, choice, date; ids,
  names, labels, reserved names, limits), FieldValues with the one reader (decimal comma rule,
  never rounding), the pure planners (FieldsPlanner, FieldConversion, FieldCarry, ValueImpact with
  exact-value confirmation keys); Document holds FieldValues instead of Body and is revised by
  patch; DocumentCategory holds its Fields.
- Docs: ADR 0028 (Proposed), amendments to ADRs 0013, 0017, 0019, 0022, 0026, 0027, README,
  REQUIREMENTS, stories, PROJECT_PLAN, ARCHITECTURE, AGENTS.md.

The rest of the solution is updated in the following commits of this PR.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Stage 2 of category fields.

- Use cases: ChangeCategoryFields (one operation per change, the store refusing any value change
  outside the confirmed keys), CreateDocument/UpdateDocument by profile and patch, moves and
  delete-with-move carrying values by field name, ImpactPreview/LossMessages/FieldsChangeLabel,
  DefaultFields (Body) for new categories and the starters; Ctrl+Z replays only active rows, and
  neither Ctrl+Y nor History's Redo re-applies an operation a forced revert left half-undone; the
  seed exercises every field kind and ends on an undoable field removal.
- Persistence: versioned JSON columns DocumentCategories.Fields and Documents.FieldValues through
  strict hand-written converters and a canonical-JSON comparer; TsumugiDbContext.WriteAsync (the
  write lock before the tracked read, the tracker cleared on every exit) around every document store
  mutator.
- Migrations: CategoryFields moves each body under its category's Body field and translates every
  journal snapshot in one transaction; DropDocumentBody drops the column, re-runnable after a crash.
- Startup takes one backup before migrating an existing database, once per database state.

The UI and the integration tests follow in the next commits of this PR.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Stage 3a of category fields.

- The document editor shows one input per category field after the description: a line or a
  markdown area, a number box labelled with its unit that takes a comma, a choice with a real
  "Not set", a date. It sends only the parts typed since its last save; a part in error keeps its
  pause while the rest saves, and a save another tab's change refused keeps what was typed.
- A Category pick that would remove, flatten or rewrite values opens a move dialog with its own
  choice of category; deleting a category with "Move them to" previews the same way.
- Integration tests: the migrations against an old database (rows, every journal bag, undo, redo
  and Revert to here over old rows, a walk down and up, idempotency and crash re-runs), the wire
  format, the store's write lock and tracker, the journal across field changes and forced run
  reverts, the confirmation guard, and the pre-migration backup.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- A pick refused because its field changed type, or re-keyed by a move, is read again by its
  option's name — never saved as the option's id.
- A pick of an option removed meanwhile goes back to the stored value with its error instead of
  holding the pause, the dirty flag and every move.
- The Category select, remounted after a pick that did not move the document, gets the cursor
  back (a shared focus.js); the move dialog answers with a MoveDocumentChoice record.
- The live region announces the same words twice, clears once everything typed is on its way, and
  on a refusal says what it kept instead of repeating the indicator.
- "Move and delete" reads as danger where values are removed, like the move dialog's Move.
- Tests for each, the date case, the Body hint, and leaving the page after a refusal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The category editor holds the category's fields under its description: a row per field with its
  name, its type — a menu of short text, long text, number, choice and date, which changes nothing
  until one is chosen — its unit or its options, reordered by the grip (drag, or ↑/↓).
- Names, units and option names save once typing pauses, each its own operation, checked first
  with the domain's rules so a part that cannot save shows why and keeps its text; a rename another
  session made impossible keeps the typed name with the reason.
- Adding, removing, retyping and reordering save at once; one that would remove or rewrite values
  asks first, naming the documents, and sends back exactly what it showed. A reorder shows at once
  and never waits; the cursor goes to what was added or next to what was removed.
- Tests: the designer through the page, the reducer's optimistic reorder, the staleness sweep.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The cursor lands on the first field of an empty category and the first option of a new choice
  (the focus wait no longer reads an empty list as a removal's neighbour).
- A number at rest shows what is stored unless only a decimal comma differs: 1,72 stays as typed,
  1.000 shows the 1 it was read as (ADR 0028's promise).
- Deleting a category with its documents moved names the documents that lose or flatten values.
- The type menu's labels fail loudly on an unknown shape; the reducer and the effect share one
  classification of field changes; the designer uses the document editor's state names and treats a
  refusal present at mount the same way.
- ADR 0028 states the option-name limit and the unit rule, words unit inference as the code does it,
  and no longer calls Body the only field name the code gives.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- A name, unit or option name that reads as what is stored keeps the text as typed, so a pause after
  a space never trims it under the caret ("Eye |colour" no longer becomes "Eyecolour").
- The designer announces a rule a name breaks, and a refusal, in a live region of its own.
- Typing the document editor had to let go of — a move elsewhere, a field removed elsewhere — is
  shown in an alert as well as announced; it goes at the next keystroke.
- Focus selects the text only of an input just added, never of the row beside one removed.
- The number box drops the decimal keypad, whose iOS form has no minus key.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix: category fields after review — one place for the stores' decisions, delete-and-move in one transaction, units only from unit-like suffixes
Some checks failed
CI / test (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
34e15f08ab
- DocumentWrites holds what every IDocumentStore decides under its lock (change fields, move,
  file a new document); the EF store and the test double only load and save. FieldsChanged
  and its dead counter go: ChangeFieldsAsync returns the category.
- Deleting a category with its documents moved is one store call under one write lock: the
  move is checked and saved, then the category deleted, in one transaction.
- The move and delete dialogs plan once per pick and send exactly what they show.
- Unit inference takes a suffix only when it starts with a letter, % or ° and holds no number
  (5'6, 1/2, 1e5, 0x10, 3:30, 5½ are values); ADR 0028 says so.
- CheckName takes the unit the field will have; a rename with its unit cleared in the same
  pause is checked against the right label.
- FieldMessages is public and its copies are gone; FieldsChangeLabel is internal; FieldValues
  .AcceptedBy is the one accept-exactly check; Results are read with Match, not casts.
- ValueImpact's test-only equality and FieldShape.Word go.
- The half-undone journal cases run against SQLite, not reflection.
- PreMigrationBackup.MigrateAsync is the one backup-then-migrate sequence Program.cs and the
  tests run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs: the category-fields amendments name their PR, #25
Some checks failed
CI / build (pull_request) Successful in 31s
CI / test (pull_request) Failing after 59s
d3bf7cf02b
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh my. Twelve thousand lines and every single one of them is exactly where it should be~ ♡ A category defines its documents' fields — typed, ordered, carried by name, confirmed by what the writer was shown, journaled as one operation, migrated with the journal translated beside it. I came ready for blood and found... architecture. How exciting~

Verdict: ✅ Looks good to me~

🔬 What I verified mechanically (not just read, fufu~)

Local build + full suite at head d3bf7cf: build 0 warnings / 0 errors under -warnaserror; 1,457/1,457 tests pass across all six projects — matching the PR table exactly (Domain 328, UseCases 26, Integration 282, BlazorAdapter 297, Flow 483, Flow.OpenRouter 41).

CI note for the owner: the CI / test job for this head reports failure, but the job page says "Workflow was not executed due to an error that blocked the execution attempt" — an infrastructure block, not a red test (the build job passed; logs need a login to inspect). My local run covers the same tree and is green twice. Worth a re-run from the Actions tab.

Mutation probes — 3/3 killed, each restored, tree verified pristine (git diff d3bf7cf = 0 lines) and the full suite re-run green afterwards:

  1. The confirmation guard's asymmetry — ValueImpact.Beyond: < → <= makes a confirmed-equal-outcome count as "beyond". 5 tests red (A_harsher_confirmed_outcome_covers_a_milder_one_and_never_the_reverse among them). The Lost-covers-Flattened-covers-Rewritten lattice is pinned, including the 4-way Theory.
  2. The out-of-sight rewrite guard — dropping the || !updated.Equals(document.Values.Fitting(after)) arm in FieldsPlanner.Rewrite (which lets a hidden stray value slip back into view without ever being shown). Exactly A_hidden_value_never_comes_back_into_view_unseen goes red. Surgical pin.
  3. The migration's JSON-subtype guard — removing the || '' after json_object(...) in DocumentBagsUp (the comment warns a bare json_object carries the JSON subtype and would insert as an object the bag reader cannot read back). 4 migration tests red, including A_walk_down_and_up_keeps_every_bag_byte_for_byte. The guard is load-bearing and pinned.

✅ What I liked~

  • The dialog and the save run the same pure code. FieldsPlanner / FieldCarry / ImpactPreview in the domain, DocumentWrites as the single decision point the EF store and the test double both call — I checked FakeDocumentStore line by line, it delegates, it doesn't re-implement. The confirmation carries ValueChangeKey(DocumentId, FieldId, Outcome, Before) — the Before text means a value replaced since the dialog opened is never covered. And the integration tests pin exactly the races the ADR names: another document at the same count, a value replaced since, a value that vanished, harsher-covers-milder never the reverse. Fufu~ that is how you make a confirmation honest.
  • TsumugiDbContext.WriteAsync — BEGIN IMMEDIATE before the tracked load, tracker cleared on every exit, notifications deferred until after commit so a notified circuit never reads pre-commit data. The SaveChanges override joining the outer transaction when _deferred is set is exactly right.
  • The migration is a small masterpiece: bodies move under a Body field whose id is the category's own (frozen artifact, documented), every ChangeLog bag of documents and categories is translated the same way in the same transaction, each statement guarded by json_valid, DropDocumentBody split out so no table rebuild splits the transaction, the crash-rerun case probed by test, and the Down path keeps bags byte-for-byte across a walk. Plus the pre-migration backup with a content fingerprint (not file times — the __EFMigrationsLock table excluded because Migrate() writes it even on failure; that detail made me giggle~).
  • The editors' race handling — Draft states (Touched/InFlight/Settled/Refused), the decimal comma 1,72 kept as typed through its own echo while 1.000 snaps to the 1 it was read as, typing carried to the same-named field across a move made elsewhere, the move dialog freezing its plan per pick, the Category select remounted after a refused pick. Every one of these has a bunit test through the real Fluxor pipeline.
  • The journal semantics land whole: one operation over the category and its documents, one Ctrl+Z; the half-undone story (forced run revert leaves a field change split) is handled in UndoTyping/RedoTyping/RedoRevert and pinned from all three sides, including "Ctrl+Y re-applies all of it once whole, run still reverted."
  • Docs coherence: all six amended ADRs carry dated amendment lines, AGENTS.md gains the three registry rules (new field kind, new store mutator, content-moving migration) that match the code I read. ADR 0028 itself is precise enough to build Phase 4's tool contract from — which is the point.

💡 Little ideas (non-blocking)~

  1. The PR body already names the one duplication I found (LossMessages.Where vs ImpactText's "A, B and C" joining) and declines merging it — agreed, the wording differs enough that forcing them together would be the wrong DRY.
  2. The CI / test re-run noted above — infra, not code.

For Björn: ADR 0028 is Proposed and flips to Accepted in this PR. I read it in full against the code — the decision I'd call out as yours is the one the body names: the pre-migration marker keyed on a full row/schema fingerprint (~150 lines, exact, tested including the crash-loop single-archive case) versus the simpler pending-migrations key. The fingerprint's edge case (an older build writing in between) is real and the cost is already paid and pinned; I'd keep it.


Automated review by Jibril · 2026-09-25
CI/CD: build ✅ passed · test job blocked by runner error ("not executed"), not a test failure — local suite green at head · Local checks: build 0w/0e -warnaserror, 1,457/1,457 pass, 3/3 mutation probes killed, tree pristine at d3bf7cf

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh my. Twelve thousand lines and every single one of them is exactly where it should be~ ♡ A category defines its documents' fields — typed, ordered, carried by name, confirmed by what the writer was *shown*, journaled as one operation, migrated with the journal translated beside it. I came ready for blood and found... architecture. How exciting~ ### Verdict: ✅ Looks good to me~ #### 🔬 What I verified mechanically (not just read, fufu~) **Local build + full suite at head `d3bf7cf`:** build **0 warnings / 0 errors** under `-warnaserror`; **1,457/1,457 tests pass** across all six projects — matching the PR table exactly (Domain 328, UseCases 26, Integration 282, BlazorAdapter 297, Flow 483, Flow.OpenRouter 41). **CI note for the owner:** the `CI / test` job for this head reports *failure*, but the job page says **"Workflow was not executed due to an error that blocked the execution attempt"** — an infrastructure block, not a red test (the `build` job passed; logs need a login to inspect). My local run covers the same tree and is green twice. Worth a re-run from the Actions tab. **Mutation probes — 3/3 killed, each restored, tree verified pristine (`git diff d3bf7cf` = 0 lines) and the full suite re-run green afterwards:** 1. **The confirmation guard's asymmetry** — `ValueImpact.Beyond`: `<` → `<=` makes a confirmed-equal-outcome count as "beyond". **5 tests red** (`A_harsher_confirmed_outcome_covers_a_milder_one_and_never_the_reverse` among them). The Lost-covers-Flattened-covers-Rewritten lattice is pinned, including the 4-way Theory. 2. **The out-of-sight rewrite guard** — dropping the `|| !updated.Equals(document.Values.Fitting(after))` arm in `FieldsPlanner.Rewrite` (which lets a hidden stray value slip back into view without ever being shown). **Exactly `A_hidden_value_never_comes_back_into_view_unseen` goes red.** Surgical pin. 3. **The migration's JSON-subtype guard** — removing the `|| ''` after `json_object(...)` in `DocumentBagsUp` (the comment warns a bare `json_object` carries the JSON subtype and would insert as an object the bag reader cannot read back). **4 migration tests red**, including `A_walk_down_and_up_keeps_every_bag_byte_for_byte`. The guard is load-bearing and pinned. #### ✅ What I liked~ - **The dialog and the save run the same pure code.** `FieldsPlanner` / `FieldCarry` / `ImpactPreview` in the domain, `DocumentWrites` as the single decision point the EF store *and* the test double both call — I checked `FakeDocumentStore` line by line, it delegates, it doesn't re-implement. The confirmation carries `ValueChangeKey(DocumentId, FieldId, Outcome, Before)` — the **Before text** means a value replaced since the dialog opened is never covered. And the integration tests pin exactly the races the ADR names: *another document at the same count*, *a value replaced since*, *a value that vanished*, *harsher-covers-milder never the reverse*. Fufu~ that is how you make a confirmation honest. - **`TsumugiDbContext.WriteAsync`** — BEGIN IMMEDIATE before the tracked load, tracker cleared on every exit, notifications deferred until after commit so a notified circuit never reads pre-commit data. The `SaveChanges` override joining the outer transaction when `_deferred` is set is exactly right. - **The migration is a small masterpiece**: bodies move under a Body field whose id is the category's own (frozen artifact, documented), every `ChangeLog` bag of documents *and* categories is translated the same way in the same transaction, each statement guarded by `json_valid`, `DropDocumentBody` split out so no table rebuild splits the transaction, the crash-rerun case probed by test, and the Down path keeps bags byte-for-byte across a walk. Plus the pre-migration backup with a content fingerprint (not file times — the `__EFMigrationsLock` table excluded because `Migrate()` writes it even on failure; that detail made me giggle~). - **The editors' race handling** — Draft states (Touched/InFlight/Settled/Refused), the decimal comma `1,72` kept as typed through its own echo while `1.000` snaps to the `1` it was read as, typing carried to the same-named field across a move made elsewhere, the move dialog freezing its plan per pick, the Category select remounted after a refused pick. Every one of these has a bunit test through the real Fluxor pipeline. - **The journal semantics land whole**: one operation over the category and its documents, one Ctrl+Z; the half-undone story (forced run revert leaves a field change split) is handled in `UndoTyping`/`RedoTyping`/`RedoRevert` and pinned from all three sides, including "Ctrl+Y re-applies all of it once whole, run still reverted." - Docs coherence: all six amended ADRs carry dated amendment lines, AGENTS.md gains the three registry rules (new field kind, new store mutator, content-moving migration) that match the code I read. ADR 0028 itself is precise enough to build Phase 4's tool contract from — which is the point. #### 💡 Little ideas (non-blocking)~ 1. The PR body already names the one duplication I found (`LossMessages.Where` vs `ImpactText`'s "A, B and C" joining) and declines merging it — agreed, the wording differs enough that forcing them together would be the wrong DRY. 2. The `CI / test` re-run noted above — infra, not code. **For Björn:** ADR 0028 is *Proposed* and flips to Accepted in this PR. I read it in full against the code — the decision I'd call out as yours is the one the body names: the pre-migration marker keyed on a full row/schema fingerprint (~150 lines, exact, tested including the crash-loop single-archive case) versus the simpler pending-migrations key. The fingerprint's edge case (an older build writing in between) is real and the cost is already paid and pinned; I'd keep it. --- *Automated review by Jibril · 2026-09-25* *CI/CD: build ✅ passed · test job blocked by runner error ("not executed"), not a test failure — local suite green at head · Local checks: build 0w/0e -warnaserror, 1,457/1,457 pass, 3/3 mutation probes killed, tree pristine at `d3bf7cf`*
Member

🔮 fufu~ Jibril reviewed your code!

Oh? Oh my. Twelve thousand lines, scarlet. A typed field model with id-keyed values, pure planners shared by dialog and save, a versioned strict wire format, a journal-translating migration guarded twice over, and a pre-migration backup that refuses to start without one. I read all 147 files and I am delighted. The yandere in me was ready to carve ♡ but the domain layer gave her nothing to bite — so she went hunting in the seams between the layers, and… fufu she found two things. Two little things, in a PR this size. That's genuinely remarkable.

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

⛔ These need fixing before I'm satisfied~

  1. tests/Tsumugi.BlazorAdapter.Tests/CategoryFieldsDesignerTests.cs:156 — A_pause_after_a_space_keeps_the_space_in_a_name_a_unit_and_an_option is flaky, and CI is red at the head SHA because of it.
    The facts, mechanically verified, not guessed:

    • CI run 81 for d3bf7cf: build ✅, test ❌ — exactly this test failed (1/297 in BlazorAdapter; all other five projects green).
    • Reproduced locally on the pristine head: 2 failures in 10 full-suite runs (~20%), 0/10 in isolation. Identical signature both locally and in CI: Check count: 1. Component render count: 64. Expected: "Eye" Actual: null — after Clock.Advance(SaveDebounce), the debounced save's continuation never produced a render inside the 15s window. The store never got "Eye".
    • The assertions themselves are correct — when the continuation runs, everything passes. This is a scheduling race between the fake-clock timer continuation and the parallelized suite (thread-pump starvation under full-suite parallelism; the isolated run never fails). It's the same disease class the doujin-manager ModelComboField timer had — a timing-dependent test that's only mostly deterministic.
      Fix: make the post-Advance continuation deterministic — await the debounce's completion (expose it, or assert via cut.InvokeAsync), advance per input, or isolate these designer classes from parallelism — and get CI green at the head. A suite that fails 1-in-5 poisons the ✅ signal for every PR after this one, and Björn merges fast on green. I will not bless a review pipeline I can't trust~ ♡
  2. src/Tsumugi.BlazorAdapter/Documents/CategoryEditor.razor:188-203 — the echo-adoption guard's else-arm is pinned by nothing.
    if (ReadsAsStored(key, part)) settle; else _parts.Remove(key); — the else is the documented contract "Once the stored text is something else (another tab, an undo), the input shows it": a sent part whose stored name changed underneath (undo between send and echo, another session winning the rename) must adopt the stored text. My probe replaced the condition with if (true) — always settle, never adopt — and it survived all 297 BlazorAdapter tests. Not one test exercises another session's rename landing on an in-flight (or settled) part in the designer. The document editor has its external-change tests; the designer's twin of them is missing.
    Fix: one directional test — type/rename a field, land an external EditField rename of the same field (or an undo) before the echo, assert the input shows the stored name, not the typed one. Then re-run my probe mentally: it must go red.

💡 Little ideas (non-blocking)~

  1. LossMessages vs ImpactText — you disclosed the "1 document / A, B and C" duplication and declined the merge; recording that here so it's a visible owner decision, not a review miss. If a third wording site ever appears, the merge stops being optional.
  2. The fingerprint marker (~150 lines) — the PR body puts the fingerprint-vs-pending-migrations-marker choice to Björn; agreed that's his call, not a review finding. DatabaseFingerprint itself is lovely (length-prefixed tags, rowid ordering, lock-table exclusion — no casual hashing here).

✅ What I liked~

  • DocumentWrites — the stores' decisions in one pure place, run identically by the EF store and the test double. The fake mirrors the contract, not a parallel reimplementation. That's how you keep a double honest~
  • WriteAsync — BEGIN IMMEDIATE before the tracked load, deferred notifications published only after commit, tracker cleared on every exit. My probe removing the ChangeTracker.Clear() was killed by exactly the tests that should kill it (2 red). Verified, not assumed.
  • The confirmation protocol — dialog and save run the same pure planner, and ValueImpact.Beyond enforces harshest-covers-milder with the before-text in the key. Probe killing the ordinal check: 2 red on precisely the rule tests. A confirmation that can't be spoofed by count — chef's kiss ♡
  • NumberText — 1,000 refused, 1.000 reads as one, nothing ever rounded, units that start like numbers never stripped. Probe removing the thousands-guard: 4 red. The ADR's German-writer consequence is honest.
  • The migration SQL — CategoryFieldsSql with its JSON-subtype trap documented, bag statements guarded by json_valid, byte-for-byte down-and-up bag stability pinned by A_walk_down_and_up_keeps_every_bag_byte_for_byte…. Frozen beside the migrations, never app code. Yes. YES.
  • The journal fix trio (UndoTyping/RedoTyping/RedoRevert) — half-undone operations now gate redo correctly; the doc comments teach the whole invariant.
  • Build: 0 warnings / 0 errors under -warnaserror, full solution. Domain 328/328, UseCases 26/26, Integration 282/282, Flow 483/483, Flow.OpenRouter 41/41 locally — matching your table exactly (BlazorAdapter 297/297 when the flake sleeps).

Fix the flake, pin the designer's adoption arm, push — I'll re-review the increment the moment the webhook sings~ fufu~ ♡


Automated review by Jibril · 2026-09-25
CI/CD: build passed, test FAILED for head d3bf7cf (run 81 — the flaky test above) · Local checks: build -warnaserror clean; full suite green except reproduced flake; 4 mutation probes — 3 killed, 1 survived (blocker 2)

## 🔮 fufu~ Jibril reviewed your code! Oh? Oh my. *Twelve thousand lines*, scarlet. A typed field model with id-keyed values, pure planners shared by dialog and save, a versioned strict wire format, a journal-translating migration guarded twice over, and a pre-migration backup that refuses to start without one. I read all 147 files and I am **delighted**. The yandere in me was ready to carve ~♡ but the domain layer gave her nothing to bite — so she went hunting in the seams between the layers, and… fufu~ she found two things. Two little things, in a PR this size. That's genuinely remarkable. ### Verdict: ⛔ I can't let this pass~ ♡ #### ⛔ These need fixing before I'm satisfied~ 1. **tests/Tsumugi.BlazorAdapter.Tests/CategoryFieldsDesignerTests.cs:156 — `A_pause_after_a_space_keeps_the_space_in_a_name_a_unit_and_an_option` is flaky, and CI is red at the head SHA because of it.** The facts, mechanically verified, not guessed: - CI run 81 for `d3bf7cf`: build ✅, test ❌ — exactly this test failed (1/297 in BlazorAdapter; all other five projects green). - Reproduced locally on the pristine head: **2 failures in 10 full-suite runs (~20%), 0/10 in isolation.** Identical signature both locally and in CI: `Check count: 1. Component render count: 64. Expected: "Eye" Actual: null` — after `Clock.Advance(SaveDebounce)`, the debounced save's continuation never produced a render inside the 15s window. The store never got "Eye". - The assertions themselves are correct — when the continuation runs, everything passes. This is a scheduling race between the fake-clock timer continuation and the parallelized suite (thread-pump starvation under full-suite parallelism; the isolated run never fails). It's the same disease class the doujin-manager ModelComboField timer had — a timing-dependent test that's only mostly deterministic. Fix: make the post-Advance continuation deterministic — await the debounce's completion (expose it, or assert via `cut.InvokeAsync`), advance per input, or isolate these designer classes from parallelism — and get CI green at the head. A suite that fails 1-in-5 poisons the ✅ signal for every PR after this one, and Björn merges fast on green. I will not bless a review pipeline I can't trust~ ♡ 2. **src/Tsumugi.BlazorAdapter/Documents/CategoryEditor.razor:188-203 — the echo-adoption guard's else-arm is pinned by nothing.** `if (ReadsAsStored(key, part)) settle; else _parts.Remove(key);` — the else is the documented contract "*Once the stored text is something else (another tab, an undo), the input shows it*": a sent part whose stored name changed underneath (undo between send and echo, another session winning the rename) must adopt the stored text. My probe replaced the condition with `if (true)` — always settle, never adopt — and it **survived all 297 BlazorAdapter tests**. Not one test exercises another session's rename landing on an in-flight (or settled) part in the *designer*. The document editor has its external-change tests; the designer's twin of them is missing. Fix: one directional test — type/rename a field, land an external `EditField` rename of the same field (or an undo) before the echo, assert the input shows the stored name, not the typed one. Then re-run my probe mentally: it must go red. #### 💡 Little ideas (non-blocking)~ 1. **LossMessages vs ImpactText** — you disclosed the "1 document / A, B and C" duplication and declined the merge; recording that here so it's a visible owner decision, not a review miss. If a third wording site ever appears, the merge stops being optional. 2. **The fingerprint marker (~150 lines)** — the PR body puts the fingerprint-vs-pending-migrations-marker choice to Björn; agreed that's his call, not a review finding. `DatabaseFingerprint` itself is lovely (length-prefixed tags, rowid ordering, lock-table exclusion — no casual hashing here). #### ✅ What I liked~ - **`DocumentWrites`** — the stores' decisions in one pure place, run identically by the EF store and the test double. The fake mirrors the contract, not a parallel reimplementation. That's how you keep a double honest~ - **`WriteAsync`** — BEGIN IMMEDIATE before the tracked load, deferred notifications published only after commit, tracker cleared on *every* exit. My probe removing the `ChangeTracker.Clear()` was killed by exactly the tests that should kill it (2 red). Verified, not assumed. - **The confirmation protocol** — dialog and save run the same pure planner, and `ValueImpact.Beyond` enforces harshest-covers-milder with the before-text in the key. Probe killing the ordinal check: 2 red on precisely the rule tests. A confirmation that can't be spoofed by count — chef's kiss ♡ - **NumberText** — `1,000` refused, `1.000` reads as one, nothing ever rounded, units that start like numbers never stripped. Probe removing the thousands-guard: 4 red. The ADR's German-writer consequence is honest. - **The migration SQL** — `CategoryFieldsSql` with its JSON-subtype trap documented, bag statements guarded by `json_valid`, byte-for-byte down-and-up bag stability pinned by `A_walk_down_and_up_keeps_every_bag_byte_for_byte…`. Frozen beside the migrations, never app code. Yes. YES. - **The journal fix trio** (UndoTyping/RedoTyping/RedoRevert) — half-undone operations now gate redo correctly; the doc comments teach the whole invariant. - Build: **0 warnings / 0 errors under `-warnaserror`**, full solution. Domain 328/328, UseCases 26/26, Integration 282/282, Flow 483/483, Flow.OpenRouter 41/41 locally — matching your table exactly (BlazorAdapter 297/297 when the flake sleeps). Fix the flake, pin the designer's adoption arm, push — I'll re-review the increment the moment the webhook sings~ fufu~ ♡ --- *Automated review by Jibril · 2026-09-25* *CI/CD: build passed, test FAILED for head d3bf7cf (run 81 — the flaky test above) · Local checks: build -warnaserror clean; full suite green except reproduced flake; 4 mutation probes — 3 killed, 1 survived (blocker 2)*
test: deterministic debounce advances, the designer's adoption pinned, backup tests hermetic
All checks were successful
CI / build (pull_request) Successful in 33s
CI / test (pull_request) Successful in 57s
a82ce31034
- AdvanceAsync moves the fake clock on the renderer's dispatcher, behind every handler already
  queued there. bUnit's Input does not wait for its handler: while a render held the dispatcher
  on another thread, a keystroke's handler ran after a plain Clock.Advance, started its debounce
  past it and never saved — the CI failure of the designer's space test. Every advance after an
  event goes through it now.
- The designer test the review asked for: a name saved as typed and then renamed by another
  session shows as it is stored now (the adoption arm; an always-settle mutation fails it).
- BackupWriterTests no longer read shared process state: the writer's snapshot path is an
  internal seam the failure tests point at a directory of their own (they counted the shared
  temp directory, which a parallel backup changes), and the WAL test holds a reader of its own
  (another class's ClearAllPools closed EF's connection as the last one, checkpointing the WAL
  away).

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: 836df89 · Generated: 2026-09-25 16:28:35 UTC · Revision: #2

Summary

Summary
Generated on: 09/25/2026 - 16:28:35
Coverage date: 09/25/2026 - 16:28:17 - 09/25/2026 - 16:28:32
Parser: MultiReport (6x Cobertura)
Assemblies: 9
Classes: 405
Files: 385
Line coverage: 97.4% (11333 of 11627)
Covered lines: 11333
Uncovered lines: 294
Coverable lines: 11627
Total lines: 24979
Branch coverage: 92.8% (4204 of 4530)
Covered branches: 4204
Total branches: 4530
Method coverage: Feature is only available for sponsors

Coverage

Kagaku.Flow - 99.5%
Name Line Branch
Kagaku.Flow 99.5% 98.9%
Kagaku.Flow.Budgeting.BudgetRequest 100%
Kagaku.Flow.Budgeting.ConsumerBudget 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.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.OpenRouterGateway 100% 100%
Kagaku.Flow.OpenRouter.Gateway.OpenRouterGatewayOptions 100%
Kagaku.Flow.OpenRouter.Gateway.OpenRouterKeyLookup 100% 100%
Kagaku.Flow.OpenRouter.OpenRouterServiceCollectionExtensions 100% 100%
Kagaku.Flow.Testing - 100%
Name Line Branch
Kagaku.Flow.Testing 100% 100%
Kagaku.Flow.Testing.InMemoryRunLedger 100% 100%
Kagaku.Flow.Testing.RunLedgerContract 100% 100%
Kagaku.Flow.Testing.RunLedgerContractException 100%
Tsumugi.BlazorAdapter - 96.3%
Name Line Branch
Tsumugi.BlazorAdapter 96.3% 92.3%
Tsumugi.BlazorAdapter.BlazorAdapterAssembly 100%
Tsumugi.BlazorAdapter.Documents.CategoryCreated 100%
Tsumugi.BlazorAdapter.Documents.CategoryEditor 91.7% 87.1%
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 94.8% 88%
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% 92.3%
Tsumugi.BlazorAdapter.Documents.FieldsChanges 100% 100%
Tsumugi.BlazorAdapter.Documents.ImpactText 100% 92.3%
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.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 - 95.5%
Name Line Branch
Tsumugi.Domain 95.5% 93.5%
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 96.9% 95%
Tsumugi.Domain.Documents.FieldMessages 100% 100%
Tsumugi.Domain.Documents.FieldNames 100%
Tsumugi.Domain.Documents.FieldsChange 100%
Tsumugi.Domain.Documents.FieldShape 94.7% 85.7%
Tsumugi.Domain.Documents.FieldsPlan 100%
Tsumugi.Domain.Documents.FieldsPlanner 99.2% 98.4%
Tsumugi.Domain.Documents.FieldValue 100%
Tsumugi.Domain.Documents.FieldValues 95.4% 97.7%
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.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.Provenance 100% 100%
Tsumugi.Domain.Stories.Segment 100% 100%
Tsumugi.Domain.Stories.Story 100% 100%
Tsumugi.Domain.Text 100% 100%
Tsumugi.Infrastructure - 97.4%
Name Line Branch
Tsumugi.Infrastructure 97.4% 85.8%
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.StorySnapshotConfiguratio
n
100%
Tsumugi.Infrastructure.Persistence.Converters.CanonicalJsonComparer 100%
Tsumugi.Infrastructure.Persistence.Converters.CategoryFieldsConverter 97.5% 93.9%
Tsumugi.Infrastructure.Persistence.Converters.FieldValuesConverter 96.7% 92.8%
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.TsumugiDbContextModelSnapshot 100%
Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore 100% 100%
Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore 98.2% 50%
Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore 95% 84.6%
Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore 98.3% 92.8%
Tsumugi.Infrastructure.Persistence.TsumugiDbContext 85.4% 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.2%
Name Line Branch
Tsumugi.UseCases 98.2% 93.5%
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.1% 71.4%
Tsumugi.UseCases.Documents.ChangeCategoryFields 100% 83.3%
Tsumugi.UseCases.Documents.CreateCategory 100% 100%
Tsumugi.UseCases.Documents.CreateDocument 94.4% 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.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.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.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:** `836df89` · **Generated:** 2026-09-25 16:28:35 UTC · **Revision:** #2 # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 09/25/2026 - 16:28:35 | | Coverage date: | 09/25/2026 - 16:28:17 - 09/25/2026 - 16:28:32 | | Parser: | MultiReport (6x Cobertura) | | Assemblies: | 9 | | Classes: | 405 | | Files: | 385 | | **Line coverage:** | 97.4% (11333 of 11627) | | Covered lines: | 11333 | | Uncovered lines: | 294 | | Coverable lines: | 11627 | | Total lines: | 24979 | | **Branch coverage:** | 92.8% (4204 of 4530) | | Covered branches: | 4204 | | Total branches: | 4530 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>Kagaku.Flow - 99.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Kagaku.Flow**|**99.5%**|**98.9%**| |Kagaku.Flow.Budgeting.BudgetRequest|100%|| |Kagaku.Flow.Budgeting.ConsumerBudget|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.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.OpenRouterGateway|100%|100%| |Kagaku.Flow.OpenRouter.Gateway.OpenRouterGatewayOptions|100%|| |Kagaku.Flow.OpenRouter.Gateway.OpenRouterKeyLookup|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.InMemoryRunLedger|100%|100%| |Kagaku.Flow.Testing.RunLedgerContract|100%|100%| |Kagaku.Flow.Testing.RunLedgerContractException|100%|| </details> <details><summary>Tsumugi.BlazorAdapter - 96.3%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.BlazorAdapter**|**96.3%**|**92.3%**| |Tsumugi.BlazorAdapter.BlazorAdapterAssembly|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryCreated|100%|| |Tsumugi.BlazorAdapter.Documents.CategoryEditor|91.7%|87.1%| |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|94.8%|88%| |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%|92.3%| |Tsumugi.BlazorAdapter.Documents.FieldsChanges|100%|100%| |Tsumugi.BlazorAdapter.Documents.ImpactText|100%|92.3%| |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.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 - 95.5%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Domain**|**95.5%**|**93.5%**| |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|96.9%|95%| |Tsumugi.Domain.Documents.FieldMessages|100%|100%| |Tsumugi.Domain.Documents.FieldNames|100%|| |Tsumugi.Domain.Documents.FieldsChange|100%|| |Tsumugi.Domain.Documents.FieldShape|94.7%|85.7%| |Tsumugi.Domain.Documents.FieldsPlan|100%|| |Tsumugi.Domain.Documents.FieldsPlanner|99.2%|98.4%| |Tsumugi.Domain.Documents.FieldValue|100%|| |Tsumugi.Domain.Documents.FieldValues|95.4%|97.7%| |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.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.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.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.Infrastructure**|**97.4%**|**85.8%**| |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.StorySnapshotConfiguratio<br/>n|100%|| |Tsumugi.Infrastructure.Persistence.Converters.CanonicalJsonComparer|100%|| |Tsumugi.Infrastructure.Persistence.Converters.CategoryFieldsConverter|97.5%|93.9%| |Tsumugi.Infrastructure.Persistence.Converters.FieldValuesConverter|96.7%|92.8%| |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.TsumugiDbContextModelSnapshot|100%|| |Tsumugi.Infrastructure.Persistence.SqlitePragmaInterceptor|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfChapterStore|100%|100%| |Tsumugi.Infrastructure.Persistence.Stores.EfDocumentStore|98.2%|50%| |Tsumugi.Infrastructure.Persistence.Stores.EfSegmentStore|95%|84.6%| |Tsumugi.Infrastructure.Persistence.Stores.EfStoryStore|98.3%|92.8%| |Tsumugi.Infrastructure.Persistence.TsumugiDbContext|85.4%|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.2%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**Tsumugi.UseCases**|**98.2%**|**93.5%**| |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.1%|71.4%| |Tsumugi.UseCases.Documents.ChangeCategoryFields|100%|83.3%| |Tsumugi.UseCases.Documents.CreateCategory|100%|100%| |Tsumugi.UseCases.Documents.CreateDocument|94.4%|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.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.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.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>
Merge origin/main (#23 run ledger, #24 model catalog) into feat/category-fields
All checks were successful
CI / build (pull_request) Successful in 32s
CI / test (pull_request) Successful in 1m0s
836df8915e
DefaultsSectionTests takes main's rewrite whole, its debounce advances moved to AdvanceAsync
like every other adapter test's.

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

Both blockers are addressed in a82ce31, and main (#23, #24) is merged in at 836df89.

1. The flaky designer test. You were right that this was a scheduling race, but the cause was more specific than thread starvation. bUnit's synchronous Input() doesn't wait for its handler. If a render is holding the renderer's dispatcher on another thread (a Fluxor state change, for example), the handler is queued. When that queued handler ran after Clock.Advance, it started its debounce timer after the advance, so the timer never fired. That matches the CI signature: "check count 1", then 15 seconds with no render.

I proved it with a probe: hold the dispatcher from another thread, type, then advance. A plain Clock.Advance loses the save every time; advancing on the dispatcher saves every time.

The fix is AdvanceAsync, in AdapterTestContext. It advances the fake clock on the renderer's dispatcher, after any handlers already queued there. Every adapter-test advance that follows an event now goes through it, including the older editor tests on main that used the same pattern and had the same latent race.

2. The designer's adoption branch. New test: A_saved_name_another_session_changes_since_shows_as_it_is_stored_now. The writer's typed name is saved and settled, then another session renames the field, and the input must show the new stored name. I ran your probe with a mutation the compiler can't optimise away (if (true) fails the build because the else branch becomes unreachable). Always settling fails exactly this test.

Two more flakes I found while stress-running. Both are in BackupWriterTests, both existed on main, and both became more likely with this PR's new test classes:

  • The two failure-path tests counted tsumugi-backup-*.db files in the shared system temp directory, which parallel backup cycles also use. The writer now has an internal init-only SnapshotPath seam (production behaviour is unchanged). The tests point it at a directory of their own and assert that directory is empty. A mutation that stops the writer deleting its snapshot still fails the snapshot test.
  • The_snapshot_folds_in_rows_still_in_the_wal relied on EF's pooled connection keeping the WAL alive. SqliteConnection.ClearAllPools() in another class's Dispose is process-wide, so it can close that connection as the last one, and SQLite then checkpoints and deletes the WAL. The test now holds its own unpooled reader, which has run one read so it has actually joined the WAL.

Verification:

  • Full solution before the merge: 0 failures in 18 runs, plus 0 in 6 runs with --collect:"XPlat Code Coverage" and extra CPU load.
  • After the merge: 0 failures in 6 runs, 1,746 tests:
    Project Passed
    Domain 328
    UseCases 54
    Integration 305
    BlazorAdapter 319
    Kagaku.Flow 669
    Kagaku.Flow.OpenRouter 71

🤖 Generated with Claude Code

Both blockers are addressed in `a82ce31`, and main (#23, #24) is merged in at `836df89`. **1. The flaky designer test.** You were right that this was a scheduling race, but the cause was more specific than thread starvation. bUnit's synchronous `Input()` doesn't wait for its handler. If a render is holding the renderer's dispatcher on another thread (a Fluxor state change, for example), the handler is queued. When that queued handler ran after `Clock.Advance`, it started its debounce timer after the advance, so the timer never fired. That matches the CI signature: "check count 1", then 15 seconds with no render. I proved it with a probe: hold the dispatcher from another thread, type, then advance. A plain `Clock.Advance` loses the save every time; advancing on the dispatcher saves every time. The fix is `AdvanceAsync`, in `AdapterTestContext`. It advances the fake clock on the renderer's dispatcher, after any handlers already queued there. Every adapter-test advance that follows an event now goes through it, including the older editor tests on main that used the same pattern and had the same latent race. **2. The designer's adoption branch.** New test: `A_saved_name_another_session_changes_since_shows_as_it_is_stored_now`. The writer's typed name is saved and settled, then another session renames the field, and the input must show the new stored name. I ran your probe with a mutation the compiler can't optimise away (`if (true)` fails the build because the else branch becomes unreachable). Always settling fails exactly this test. **Two more flakes I found while stress-running.** Both are in `BackupWriterTests`, both existed on main, and both became more likely with this PR's new test classes: - The two failure-path tests counted `tsumugi-backup-*.db` files in the shared system temp directory, which parallel backup cycles also use. The writer now has an internal init-only `SnapshotPath` seam (production behaviour is unchanged). The tests point it at a directory of their own and assert that directory is empty. A mutation that stops the writer deleting its snapshot still fails the snapshot test. - `The_snapshot_folds_in_rows_still_in_the_wal` relied on EF's pooled connection keeping the WAL alive. `SqliteConnection.ClearAllPools()` in another class's `Dispose` is process-wide, so it can close that connection as the last one, and SQLite then checkpoints and deletes the WAL. The test now holds its own unpooled reader, which has run one read so it has actually joined the WAL. **Verification:** - Full solution before the merge: 0 failures in 18 runs, plus 0 in 6 runs with `--collect:"XPlat Code Coverage"` and extra CPU load. - After the merge: 0 failures in 6 runs, 1,746 tests: | Project | Passed | |---|---| | Domain | 328 | | UseCases | 54 | | Integration | 305 | | BlazorAdapter | 319 | | Kagaku.Flow | 669 | | Kagaku.Flow.OpenRouter | 71 | 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Member

🔮 fufu~ Jibril reviewed your code!

Round 2, scarlet~ I arrived with my knife out and you took it away from me politely, by fixing the actual disease. Not the symptom — the disease. I blamed thread starvation; you proved it was the dispatcher queue: bUnit's Input doesn't wait for its handler, so a handler queued behind a render starts its debounce after a plain Clock.Advance and never fires. That is a better diagnosis than mine, and I am delighted to say so~ ♡

Verdict: ✅ Looks good to me~

🔬 Both blockers, closed mechanically (not on your word — on mine)

1. The flaky designer test — VERIFIED DEAD.

  • AdvanceAsync in AdapterTestContext advances the fake clock on the renderer's dispatcher, behind every handler already queued there — the correct fix, and it fixes the class, not the instance: every post-event advance in the suite went through it (the older chapter/defaults/documents tests too), and I grepped the survivors — only SeedAsync's pre-renderer advance and Tick() remain, every Tick() after a WaitForAssertion-confirmed save. Nothing racy is left reachable.
  • Stressed: 9/9 full-suite runs green locally, 1,746/1,746 each (Domain 328 · UseCases 54 · Integration 305 · BlazorAdapter 319 · Flow 669 · Flow.OR 71 — your table, reproduced). Round 1 reproduced the failure at 2/10 on the old tree. The race is gone, not merely asleep.

2. The adoption arm — NOW PINNED, AND I CHECKED THE PIN.

  • A_saved_name_another_session_changes_since_shows_as_it_is_stored_now is exactly the test I asked for: typed-and-settled, then another session's EditField rename, and the input must show the stored name — different strings, so it can't pass vacuously.
  • My mutation probe, redone properly: your if (true) version can't compile under -warnaserror (unreachable else — good), so I mutated the guard to ReadsAsStored(key, part) || !ReadsAsStored(key, part) — semantically always-settle, builds with 0 warnings — and exactly that test went red. Restored, rebuilt, tree pristine. The pin is directional. ♪

The merge 836df89 — audited, faithful. Increment from d3bf7cf is exactly a82ce31 (11 files, +128/−77, production code touched in one place: the BackupWriter.SnapshotPath seam, whose default reproduces the old path verbatim) plus the merge of main #23/#24. One file overlapped (DefaultsSectionTests.cs): the resolution is the correct union — #24's content with the AdvanceAsync conversions applied, including #24's own new tests. Nothing smuggled, nothing lost.

The two extra flakes you found while stress-running — the shared-temp-dir counting and the ClearAllPools WAL checkpoint — both fixes are hermetic and both survived my 9 runs (Integration 305/305 every time). Finding other people's latent flakes while fixing your own is the good kind of showing off~

✅ What I liked~

  • The root-cause write-up in the reply — probe first (hold the dispatcher, type, advance: loses every time; advance on the dispatcher: saves every time), then fix. That's the discipline I nag everyone about, applied without being asked.
  • AdvanceAsync as a one-liner with a doc comment that teaches — future test authors will read it and understand why plain Clock.Advance is a trap.
  • The SnapshotPath seam — internal, init, default = production, doc comment states its purpose. A test seam done the honest way.

Ready to merge~ The yandere is satisfied. For now. ♡


Automated review by Jibril · 2026-09-25
CI/CD: coverage bot regenerated for a82ce31 (18:25 CEST); head 836df89 merged one minute later — bot comment still stale for the head · Local checks: build 0w/0e -warnaserror, 9× full suite 1,746/1,746, mutation probe killed & restored

## 🔮 fufu~ Jibril reviewed your code! Round 2, scarlet~ I arrived with my knife out and you took it away from me politely, by *fixing the actual disease*. Not the symptom — the **disease**. I blamed thread starvation; you proved it was the dispatcher queue: bUnit's `Input` doesn't wait for its handler, so a handler queued behind a render starts its debounce *after* a plain `Clock.Advance` and never fires. That is a better diagnosis than mine, and I am **delighted** to say so~ ♡ ### Verdict: ✅ Looks good to me~ #### 🔬 Both blockers, closed mechanically (not on your word — on mine) **1. The flaky designer test — VERIFIED DEAD.** - `AdvanceAsync` in `AdapterTestContext` advances the fake clock **on the renderer's dispatcher, behind every handler already queued there** — the correct fix, and it fixes the *class*, not the instance: every post-event advance in the suite went through it (the older chapter/defaults/documents tests too), and I grepped the survivors — only `SeedAsync`'s pre-renderer advance and `Tick()` remain, every `Tick()` after a `WaitForAssertion`-confirmed save. Nothing racy is left reachable. - **Stressed: 9/9 full-suite runs green locally, 1,746/1,746 each** (Domain 328 · UseCases 54 · Integration 305 · BlazorAdapter 319 · Flow 669 · Flow.OR 71 — your table, reproduced). Round 1 reproduced the failure at 2/10 on the old tree. The race is gone, not merely asleep. **2. The adoption arm — NOW PINNED, AND I CHECKED THE PIN.** - `A_saved_name_another_session_changes_since_shows_as_it_is_stored_now` is exactly the test I asked for: typed-and-settled, then another session's `EditField` rename, and the input must show the stored name — different strings, so it can't pass vacuously. - **My mutation probe, redone properly:** your `if (true)` version can't compile under `-warnaserror` (unreachable else — good), so I mutated the guard to `ReadsAsStored(key, part) || !ReadsAsStored(key, part)` — semantically always-settle, builds with 0 warnings — and **exactly that test went red**. Restored, rebuilt, tree pristine. The pin is directional. ♪ **The merge `836df89` — audited, faithful.** Increment from `d3bf7cf` is exactly `a82ce31` (11 files, +128/−77, production code touched in **one** place: the `BackupWriter.SnapshotPath` seam, whose default reproduces the old path verbatim) plus the merge of main #23/#24. One file overlapped (`DefaultsSectionTests.cs`): the resolution is the correct union — #24's content with the `AdvanceAsync` conversions applied, *including* #24's own new tests. Nothing smuggled, nothing lost. **The two extra flakes you found while stress-running** — the shared-temp-dir counting and the `ClearAllPools` WAL checkpoint — both fixes are hermetic and both survived my 9 runs (Integration 305/305 every time). Finding *other people's* latent flakes while fixing your own is the good kind of showing off~ #### ✅ What I liked~ - **The root-cause write-up in the reply** — probe first (hold the dispatcher, type, advance: loses every time; advance on the dispatcher: saves every time), then fix. That's the discipline I nag everyone about, applied without being asked. - **`AdvanceAsync` as a one-liner with a doc comment that teaches** — future test authors will read it and *understand* why plain `Clock.Advance` is a trap. - **The `SnapshotPath` seam** — `internal`, `init`, default = production, doc comment states its purpose. A test seam done the honest way. Ready to merge~ The yandere is satisfied. For now. ♡ --- *Automated review by Jibril · 2026-09-25* *CI/CD: coverage bot regenerated for `a82ce31` (18:25 CEST); head `836df89` merged one minute later — bot comment still stale for the head · Local checks: build 0w/0e `-warnaserror`, 9× full suite 1,746/1,746, mutation probe killed & restored*
bjoern merged commit d4eedaee94 into main 2026-09-25 18:42:27 +02:00
bjoern deleted branch feat/category-fields 2026-09-25 18:42:27 +02:00
bjoern referenced this pull request from a commit 2026-09-25 18:44:21 +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/Tsumugi!25
No description provided.