feat: the inline markup codec, in C# and TypeScript, and the model-output reader (editor track M1) #34
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/markup-codec"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Editor track slice M1 (ADR 0030,
docs/PROJECT_PLAN.md): the inline markup codec twice — C# inTsumugi.Domain, TypeScript in the newManuscriptEditor/module — with one golden corpus both read, and the lenient model-output reader over Markdig inTsumugi.UseCases. No UI change and nothing stores markup yet: every write path keeps storing plain text until M2's migration.What lands
InlineMarkup(Tsumugi.Domain/Stories) — parse (never refuses), canonical runs,Visible, UTF-16 offsets,Slice/SplitAt/Append/Concaton runs,TrimEnd/Trimon the visible text with .NET'schar.IsWhiteSpace, value equality with==/!=.2 * 3 = 6,C:\tempstay as they are); otherwise every literal*escaped but the last, where that reads the same; otherwise every one. A bare literal*is visible only as its kind's unpaired last delimiter, so a write costs at most two parses however many stars a paragraph holds; runs merge in a builder. 200k star-dense characters write in ~12 ms.codec.ts— the TypeScript twin, same rules, frozen runs (the empty value is shared),concatover an iterable, a copy ofchar.IsWhiteSpace.ModelOutput.Read(Tsumugi.UseCases/Segments) — paragraphs at blank lines,***/* * *as scene breaks, Markdig's inline stage with only escape, emphasis and line-break parsers. Details:Markdig0.38.0 is added toDirectory.Packages.propsat Kagaku.UI's version.tests/Shared/inline-markup-corpus.json— canonical, normalises, plain (the switch-over migration), slices, trimEnd / trim,whitespace(every code unit both codecs trim), andmodelOutput(C# only). Each echo divergence is listed there with a note.ModelOutput.Readand requires an unchanged echo unless the corpus notes why not;tsconfig.jsonfor browser code without Node globals,tsconfig.node.jsonfor tests and config;npm ci, typecheck, test, build);.dockerignored.ADR 0030 amendments (same PR, as the ADRs require)
*in one segment pairs with one in the next, so a reader that strips markup (trigger scanner,search_manuscript) parses each segment on its own; the old sentence said the escaping survives joining. AGENTS.md's segment-text rule says the same.***. "Bold straight into italic" was listed before but echoes fine.Known and pinned, not fixed here
彼女は**「行こう」**と言ったreads as literal asterisks: CommonMark's flanking rules reject both delimiters. Markdig 1.x shipsUseCjkFriendlyEmphasis; adopting it means bumping Kagaku.UI's Markdig pin first.***/* * *as ADRs 0015/0019/0030 say;---,* * * *and a***line inside a paragraph stay prose (corpus rows pin it).Verification
dotnet teston the rebased branch: 2,461 green across six projects (Domain 457, UseCases 155, BlazorAdapter 351, Integration 324, Kagaku.Flow 1,103, OpenRouter 71).ManuscriptEditor/: typecheck, 82 vitest tests and the build are green.🤖 Generated with Claude Code
🔮 fufu~ Jibril reviewed your code!
A codec written twice in two languages, pinned to one golden corpus, with a lenient reader whose every divergence from the strict writer is catalogued with a note... oh? Oh! This is the kind of craft I want to frame on my wall~ ♡ I read all 27 files, traced the three-tier canonical writer by hand, then tried to break it six times. It didn't break. Delicious.
Verdict: ✅ Looks good to me~
✅ What I liked~
tests/Shared/inline-markup-corpus.jsonread by C# and TypeScript, with themodelOutputsection listing every strict/lenient echo divergence with a reason — 「」 flanking, the overlap cases, the***-only paragraph. That's not a test file, that's a treaty between two codecs~*can only ever surface as its kind's unpaired last delimiter. I traced\*sigh*and***\*by hand and the tier selection is exactly right; the 200k-star time-bound holds with 250× headroom.char.IsWhiteSpacecopied, not approximated. U+0085 trims, U+180E/U+200B/U+FEFF don't, and both codecs are checked over all 65,536 code units. The JavaScript\strap is named in a comment and then disarmed. ♪10k-delimiter depth limit caught as a plain-text fallback, pinned by an 11,000-*sigh*test; stray backslashes doubled only where an escape would form; and the U+2028 subtlety — Zl is whitespace to .NET's trim but not to CommonMark, so emphasis around it yields an empty paragraph that's correctly dropped — not just handled but corpus-noted (thea\n\n*\u2028*\n\nbrow). You chased the character-class dragons into their cave, fufuMarkupRun+InlineMarkup,ModelParagraph+ModelOutput) matches 14 sibling files (FieldValue,Result/Ok/Err,Provenance…); the module scaffold mirrors GraphEditor's exactly (four CI steps, RCL content exclusions,.gitignore/.dockerignore, the eval-time-glob problem avoided); Markdig 0.38.0 grep-verified identical to Kagaku.UI's submodule pin.Segment.csunchanged. M2's switch-over already has itsplaincorpus section waiting like a set trap. Good.💡 Little ideas (non-blocking)~
paragraph?.null arm and the iterator's compiler-generated resume arm at 50%/83%; nothing author-written is unexercised. Nothing to do — writing it down so nobody panics at a future coverage report~ ♡What I ran (no coverage-bot comment for
2af6593yet, so local it was): clean Release build 0w/0e;dotnet test2,461/2,461 matching your table to the digit (Domain 457, UseCases 155, Adapter 351, Integration 324, Flow 1,103, OR 71); ManuscriptEditornpm ci+ typecheck + 82/82 vitest + build (3.64 kB / 1.56 kB gzip — your numbers exactly); coverageInlineMarkup.cs100% line and branch,ModelOutput.cs100% on the reading paths. My mutation probes, 6/6 killed on their own pins, each restored + rebuilt + re-verified green: leftover-rule inversion (%2==0) → 55 rows red; scene-break* * *arm dropped → exactly its one corpus row red;EmphasisInlinefold||→&&→ 36 red; risky-backslash arm disabled in each codec → 6 red (C#) / 5 red (TS); whitespace table U+0085→U+0086 → 3 red (TS). Tree pristine at2af6593after all probes.The CJK flanking limitation (彼女は**「行こう」** reads as literal asterisks under CommonMark) is honestly disclosed and correctly deferred — Markdig 0.38 has no
UseCjkFriendlyEmphasis, and bumping Kagaku.UI's pin first is the right order. That one goes on bjoern's table~Automated review by Jibril · 2026-09-26
CI/CD: absent for head SHA at review time (no bot comment yet) · Local checks: build 0w/0e · 2,461/2,461 dotnet + 82/82 vitest · coverage 100% L+B on InlineMarkup · 6/6 mutation probes killed