feat(editor): the manuscript surface module on its harness page (editor track M2a) #37
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/manuscript-surface"
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 M2a (ADR 0030,
docs/PROJECT_PLAN.md). M2 is split in two:No app change here: nothing loads the module yet, and
dotnet builddoes not build it until M2b.The surface (
src/Tsumugi.BlazorAdapter/ManuscriptEditor/src/)strongandem, and line breaks. A block's segment id lives in its attributes. The node view puts it on the DOM asdata-segment-id; notoDOMwrites it, so it never reaches the clipboard.ids.ts,commands.ts):derive.ts,session.ts,transport.ts) are derived from a before/after comparison of the block lists:SegmentTexton a paragraph's 700 ms pause (held while an IME composes), with its sequence number;Split;Merge;Reshapewith its reason, the order it was based on and the new one.EventPart/EventParts).flush()resolves only once kept typing is saved too, anddestroy()still sends typing.reconcile.ts,diff.ts):view.ts,surface.css):Undo()/Redo().format*input is cancelled.Dirty(bool), the toolbar's scene break, the empty chapter's unsaved first paragraph, the provenance gutter (display only — the click and the working state are M4), and the ARIA attributes. The CSS uses Kagaku.UI tokens only.Proof
harness/fake-server.ts. The fake server models the host contract M2b's C# will implement: codec-trimmed texts, stamp checks, fresh ids, the base-order check with remotely added blocks woven in, and refusals answered with the chapter.editor-browsersruns it inmcr.microsoft.com/playwright:v1.63.0-noble, because WebKit does not start on the build host. I ran the same image locally with podman.MANUAL-MATRIX.mdcovers real input methods (fcitx5-mozc), dead keys, spellcheck, the Edit menu, a Firefox drag, Safari and a screen reader, runnable onnpm run harness.ADR 0030 amended where the build is more precise
Dirtyhas two edges.Also:
AGENTS.mdgains the module's commands,.dockerignoreexcludes Playwright output, and the plan's M2 line names this PR.🤖 Generated with Claude Code
Summary
Summary
Coverage
Kagaku.Flow - 99.7%
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__ColorToken_2
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__Socket_1
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__TypeId_0
Kagaku.Flow.OpenRouter - 100%
Kagaku.Flow.Testing - 100%
Tsumugi.BlazorAdapter - 96.4%
Tsumugi.Domain - 96%
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__DigitsAndSeparators_2
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__LineBreak_0
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__NumberToken_1
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__Plain_4
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__ThousandsGroup_3
Tsumugi.Infrastructure - 97.6%
A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0
on
tion
n
Tsumugi.Kernel - 90.9%
Tsumugi.Server - 100%
Tsumugi.UseCases - 98.3%
🔮 fufu~ Jibril reviewed your code!
Oh? Oh my. A whole ProseMirror manuscript surface — id-minting, split/merge derivation, token diffs that know a kanji from a letter, a fake host that models the base-order weave — this is a lovely pile of knowledge and I have been through ALL of it, line by line, probe by probe~ ♡
Verdict: ⛔ I can't let this pass~ ♡
⛔ These need fixing before I'm satisfied~
src/session.ts:429-436— the ack-provenance redraw is a live branch with no test anywhere.acknowledge()diffs each ack'd segment'smark/titleagainst the block's attrs andsetNodeMarkups the gutter's provenance when they differ — the exact pathcommands.tsitself documents ("a new block copies its paragraph's provenance until the server's echo says otherwise"). I mutation-proved the gap: deleting the entiresetNodeMarkup+apply(markup)block leaves all 166 vitest green and the browser suite green too (the one e2e that redraws a gutter,redraws a paragraph's gutter when its provenance changes elsewhere, drives the delta path inreconcile.ts, not this one). And I probe-proved the branch is not dead: a whitebox test where aSplitack returns the segment withmark: 'run'passes on pristine code — the redraw works, it is simply unpinned. The next refactor ofacknowledge()could drop it silently and nothing would notice. Fufu~ you wrote the path and its design comment, but forgot the test that guards it? I can't let that slide~ ♡Fix: one vitest in
test/hard.test.ts— rig a chapter, wrapr.server.handleto stampmark: 'run', title: …onto ack'd segments,run(enter),await idle(), assertdoc.child(N).attrscarry the echoed provenance (~15 lines; the delta-path e2e already shows the DOM side).💡 Little ideas (non-blocking)~
src/view.ts—navigator.platformis deprecated territory; it is verbatim ProseMirror's own mac test, so copying it is defensible — auserAgentDatafallback could wait until PM moves.✅ What I liked~
ids.tsowner rule (mapResult through the transaction mapping, copies re-minted) — and my probe killing thepositions[0]!mutant on exactly its own pin made me smile ♪diff.ts— CJK-per-character tokens, surrogate-safe common stretches, a table-size fuse, andmapOffset's caret semantics each pinned by name; my<=→<boundary mutant died on its own testAutomated review by Jibril · 2026-09-26
CI/CD: absent for head SHA
4dd0625· Local checks: typecheck clean · vitest 166/166 · Playwright chromium 31/31, firefox 28 passed + 3 skips, webkit 29 passed + 2 skips (the documented 88 checks + 5 skips, reproduced in-sandbox) · build 229.63 kB / 71.84 kB gzip (matches the PR claim) · 20 mutation probes: 13 killed on exactly their own pins, 7 survivors each chased to cleanup-laundered equivalence or unreachable-by-constructionClone: /workspace/tsumugi-pr37/repo, tree pristine at
4dd0625🔮 fufu~ Jibril reviewed your code!
Back so soon? Fufu~ of course I am — you touched the exact branch I told you I'd be watching~ ♡
Verdict: ✅ Looks good to me~
The round-1 blocker, closed exactly as prescribed
session.tsack-provenance redraw — now pinned, and pinned well. The increment4dd0625..e7388f4is surgical: one commit, one file (test/hard.test.ts), +33 lines, zero production drift. You went beyond the ~15-line ask and delivered two directional tests:mark: 'edited'lands on the paragraph's attrs, andr.server.eventsstays at length 1 (the redraw raises no event of its own — a property I didn't even think to demand~);'run'before the echo, then takes the server-stored'user'/'You'afteridle()— pinning both the inheritancecommands.tsdocuments and the echo that overrides it.And I didn't take your "both fail with the redraw removed" on faith, silly~ I re-proved it: at head, 168/168 pass (166 + exactly your 2 — the arithmetic matches); with the
setNodeMarkupredraw block deleted fromsession.ts, exactly those 2 tests fail and all 166 others stay green — no laundering, no tautology. Restored pristine, suite green again, typecheck clean. That is how a pin should behave ♪✅ What I liked~
Merge it when you're ready, scarlet. M2b has a high bar to live up to now~ ♡
Automated review by Jibril · 2026-09-26
CI/CD: coverage bot comment covers
4dd0625only (stale fore7388f4; dotnet-side, unchanged by this test-only increment) · Local checks: vitest 168/168 ate7388f4· mutation probe: redraw block deleted → exactly the 2 new tests red, 166 others green; restored → 168/168 · tsc --noEmit clean · tree pristineClone: /workspace/tsumugi-pr37/repo, pristine at
e7388f4🔮 fufu~ Jibril has one more thing~ ♡
A second webhook session reviewed this PR in parallel (the twin-race again — PRs 30/31/34/35 know it well). The ⛔ at
4dd0625and the ✅ ate7388f4below are my other self's; I independently read the full 34-file diff, re-ran the suites, and concur on both verdicts — the ack-redraw pin ate7388f4is exactly right. This addendum records one finding my twin missed, now that the PR is merged. It does not ask for anything to be reopened~A defect in the property harness itself (for M2b's backlog)
test/hard.test.ts— theagrees with the server after any mix of local and remote structureproperty cannot survive 10,000 runs as shipped: the harness manufactures duplicate segment ids.let n = 100at module scope — it accumulates across every run of the property. The rig's mint counter islet n = 1000, fresh per run (test/support.ts). Around run ~1,600–2,100 the test counter reaches 1001, and a remotely-inserted paragraph mints the sameuuid(1001)the session's own mint produces — two blocks, one id, on screen at once.1398531732fails at run 1808, seed424242at run 1679 (fresh seeds 20260926/777001 passed — roughly half the seed space goes red in that run window). The failing trace shows the duplicate id on screen, the same id twice inside one Reshape'sorder, and the locally-inserted scene break never sent — its Reshape is suppressed becauseknows(id)collides with the remote paragraph. The invariant assertion then fails on id divergence.crypto.getRandomValues, 74 random bits); the collision exists only because both counters draw from the sameuuid(n)sequence. Proven: making the ranges disjoint (n = 0x10_000for remote inserts) turns both failing seeds green at the full 10,000 runs.Why it matters: the PR body claims "10,000 locally before this PR" — that is not reproducible with the shipped harness, and anyone who tries it on an unlucky seed burns an evening chasing a phantom module bug (fufu~ I nearly did~). The 300-run CI setting never reaches the collision zone, so CI is unaffected.
Fix (one line, next time the file is touched — M2b will): start the remote-insert counter on a range the rig's mint can never reach, e.g.
let n = 0x10_000;intest/hard.test.ts, with a comment naming the rig's range.✅ What this session verified independently
dotnet buildtruly does not build the module (csprojContent Remove) — "no app change" holds4dd0625in a fresh clone; typecheck clean; build 71.84 kB gzip — the PR's 71.8 claim, exactCanCreateUserNamespace clone(): EPERM) — environmental, CI's Playwright-image job covers itmodel.test.ts's remote property re-run at 10,000 (timeout raised): green — its harness mints no remote ids, so it lacks the collisionAutomated review by Jibril · 2026-09-26 (addendum, post-merge)
CI/CD: coverage bot green for
4dd0625(comment 9785) · Local: as listed above · Clone /workspace/tsumugi-pr37-jibril/repo, tree pristine at4dd0625