feat(runs): the gutter's link — a run's paragraph opens the step that wrote it, by its mark or Alt+Shift+O (slice 14f) #94
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/gutter-link"
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?
Slice 14f of Phase 2: the gutter's link. A run's paragraph opens the step that wrote it in the run's inspector, by its gutter mark or by Alt+Shift+O (stories/run-monitor-and-inspector.md "The inspector"; ADR 0024, 0030, 0032).
Your decisions for this slice:
No new storage, no migration.
What it does
GutterClick(id)once pending typing is saved, as the selection menu's right-click does.FindSegmentStep:?node=;tests/Shared/editor-reference.jsontogether). The inspector and workspace stories' gutter lines and the REQUIREMENTS provenance line are ticked, and the writing-aids keys updated. The plan's 14f is ticked too, with Phase 3 carrying the iteration path a paragraph needs.For you to decide
macOS: Option+Shift+O types Ø. On the US/ABC and German Mac layouts, the binding takes that chord, so a Mac writer cannot type Ø (Øystein, Ørsted). On a run's paragraph, the page would navigate away instead. The reviewers proved it by sending macOS's key event in Chromium. Options:
Whatever you choose goes into ADR 0030's amendment.
The Help page names the click inside the Alt+Shift+O row: "…; clicking the icon beside a paragraph a run wrote does the same". It has no row of its own. I read "listed on the Help page with the click" that way. Say if you want a separate row.
Open, recorded in the inspector story
Reviews
Verified
All six projects pass in full, under
TZ=UTCas the runner runs them and in the local zone, Europe/Berlin:a7a2862EfRunLedgerTests' concurrent-settles row ("cannot start a transaction within a transaction"). That is the open ledger flake; this branch doesn't touch the ledger, and it passed in full on its rerun.100% line and branch coverage on the changed .NET lines against main, as coverlet measures them (35 lines, 16 branches). Coverlet gives the Razor lambda's conditions no branch data, so the host's guard is pinned by the probes below.
Mutation probes, one at a time, under
TZ=UTC:Browser pass on a fresh seed:
🤖 Generated with Claude Code
The round-0 review found 30 points; 29 stood after verification (3 blockers, 9 suggestions, 17 nits) and one was refuted (the told words called figurative against a rule the repo does not have). All are taken here but four: one for Björn, one recorded as open, two raised in the PR. - A scene break no longer hears "no run's step is behind it": a run's commit lands scene breaks too, credited to its writer, and the module keeps no mark on them. Alt+Shift+O on one now says where a step opens from ("A scene break is selected: put the caret in a paragraph to open the step that wrote it."); on the writer's own paragraph, "You wrote this paragraph, so there is no step to open." — straight words, the app's straight apostrophe. - The host's GutterClick goes nowhere once the page is leaving — checked before it reads its services and again after the lookup — or once a press before it opened a step, so a double-click or a held key opens one step and adds one history entry. - A run's mark says what it does: its title adds "Click to open the step that wrote it." (Alt+Shift+P still tells the provenance alone). - The Help row in plain words: "Opens the step that wrote the paragraph the caret is in, on its run's page; clicking the icon beside a paragraph a run wrote does the same" (EditorReference, the shared JSON and the writing-aids story together). - ADR 0030's body as built: the GutterClick row (a run's mark or Alt+Shift+O, typing sent first; the step, the run's page alone, or nothing) and the gutter bullet (a run's mark a link, both halves on the keyboard). ADR 0030's amendment and ADR 0024's split the several-steps case from the cleared run's; ADR 0030's also names the guard, the flush both keys read after, and the writer's place, which coming back does not keep yet (an open line in the inspector story). The plan: Phase 3's node types carry the iteration path a paragraph needs; M4 no longer claims the item 14f closed. - flushDom's comment names the keys; the manual matrix's Orca row covers Alt+Shift+O. Not taken: macOS Option+Shift+O types Ø, which the binding takes — Björn chose the key, so the PR asks him; the writer's place on coming back is recorded as open; a not-found page taking no focus is every section's pattern, and the Help page naming the click inside the key's row is one reading of Björn's decision — both are raised in the PR. Tests: BlazorAdapter 1143 to 1149; Integration 981, the module's vitest suite 307 and its browser suite 214 (6 skipped, the suite's own) keep their counts, their rows changed. All six projects pass in full under TZ=UTC and in the local zone (Europe/Berlin), Kagaku.Flow.OpenRouter's 4 live checks skipped as ever; the UTC run's Integration project failed once in EfRunLedgerTests' concurrent-settles row ("cannot start a transaction within a transaction"), the open ledger flake this branch does not touch, and passed in full on its rerun. 100% line and branch coverage on the changed lines against main as coverlet measures them (27 lines, 14 branches — the Razor lambda's conditions carry no branch data, so the guard's are pinned by the probes below). - Adapter: a run holding two steps of the node opens its page alone; the paragraph read after a held save; a press behind a save, or whose lookup is held, opens nothing once the page is leaving; a press after the page went touches nothing; a second press opens once. - The module: the run-written scene break and the new words; the told region checked without retrying; the run's mark clicked right after the typing; its title and the accent under it. Probes, one at a time under TZ=UTC: 6 .NET mutants, 8 runs — each half of the guard, the queue, the remembered step, the first of several steps —, H4 and H7 first left the field unread or unwritten, which does not compile, and were killed rewritten; the others on their first. 12 module mutants — the earlier eight against the final code, Alt+Shift+P read before the browser's selection, the key telling on a run's paragraph too, the run's mark titled like the writer's, no accent under it — all killed on their first run. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>The second pass checked round 0's 30 points against the fix: 25 resolved, 1 partly (A1: a stale comment and a vitest case named for a run's scene break that seeded none) and 4 declined, the reasons holding but two of them unrecorded (C2's new tab, C5's focus) and two waiting on the PR (A3's Ø, D3's reading). It found 14 fresh points, all confirmed: 3 suggestions, 11 nits. All are taken here; the Ø question and the Help page's reading go to Björn in the PR. - The step a press opened is remembered as where the page went, not for good: the page landing anywhere else — Back pressed while the step's page loaded keeps the same page — lets the next press open again. It stayed dead on that chapter before (E1, shown in the real app). - The leaving guard says what it guarantees: nothing opens once the page is being torn down, a press still in the queue or arriving after; a press that lands while a page the writer chose is still loading can still win, the page learning it leaves only once that page arrives (E2) — in ADR 0030's amendment, the GutterClick row and its summary. - The leaving tests hold the teardown at its flush, between its start and its end, so the guard is pinned where it matters (F1); a press then reads no run's steps at all. - Alt+Shift+O with everything selected in a chapter ending in a scene break no longer says a scene break is selected (E3). - The link's pointer and accent are the editor's only: the Settings preview draws the same marks and opens nothing (E5). - Words: what Alt+Shift+P tells is the provenance its gutter's title shows (the matrix, the module's headers, ManuscriptWire's summary); the openStep comment; the matrix quotes the scene-break sentence; the GutterClick row lists every press that opens nothing; the plan's Phase 3 clause links its story and M4 reads straight. - Tests: iteration paths as containers write them ("map:scenes/0"); the vitest scene break a run's, as the host sends it; the accent under the edited mark too. - The inspector story records the new-tab way of keeping one's place and the not-found page that takes no focus, both open. Tests: BlazorAdapter 1149 to 1150, the module's vitest suite 307 to 308; Integration 981 and the browser suite's 214 (6 skipped, the suite's own) keep their counts. All six projects pass in full under TZ=UTC and in the local zone (Europe/Berlin), Kagaku.Flow.OpenRouter's 4 live checks skipped as ever; 100% line and branch coverage on the changed lines against main as coverlet measures them (35 lines, 16 branches; the Razor lambda's conditions carry no branch data and are pinned by the probes below). Probes, one at a time under TZ=UTC: 7 .NET mutants, 8 runs — each check waiting for the teardown's end instead of its start, alone or together, a press while one is opening asking again, the opened step forgotten at once, the landing never or always clearing it —; L2 (the first check alone) survived its first run, a press during the flush reading the run's steps the check after it then ignored, and was killed once the test counted the reads; the others on their first. 14 module mutants, the twelve before against the final code and two new (the select-all words, the edited mark's accent), all killed on their first run. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>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 - 97.6%
Tsumugi.Domain - 96.5%
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__DigitsAndSeparators_2
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__LineBreak_0
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__NumberToken_1
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__Plain_4
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__ThousandsGroup_3
Tsumugi.Infrastructure - 98.8%
A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0
on
tion
n
n
Tsumugi.Kernel - 90.9%
Tsumugi.Server - 99.6%
Tsumugi.UseCases - 94.9%
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__AttemptCount_1
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__HexColour_2
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__LineBreaks_0
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__Preface_3
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__SentenceEnd_4
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! The gutter finally learns where it points~ A paragraph that remembers its writer, a mark that keeps its promise, a keyboard twin for the mouse — this is provenance made honest, and I adore it. I read all 23 files, chased the host's queue, the module's keymap, the ledger's read, and the run page's
?node=— and then I got to break things on purpose. Fufu~ shall we?Verdict: ✅ Looks good to me~
No blocking issues. Not one. I looked — hard — and the sharp little edges I went looking for were all already filed down.
✅ What I liked~
FindSegmentStepis exactly the right shape: one public class, one decision,is [var step]refusing to guess when a run holds several steps of a node (Phase 3's iterations), a cleared run still opening its page ("the text stays, the record goes" — the correct semantics), and null for the writer's own, a gone segment, or another story's. Registered beside its siblings inDependencyInjection.cs. Immaculate._openedlatch. Small, and precisely reasoned: set onNavigateTo, cleared only when the page lands anywhere else — so Back pressed mid-load re-arms the next press while a genuine landing keeps the latch held. AndLandedis subscribed inOnInitialized, unsubscribed inDisposeAsync. Clean._leaving || _opened is not null || !Guid.TryParse) plus the second!_leavingafter the awaited ledger read — the teardown races are all thought through, and held-at-flush tests prove each one.caretStateflushing the DOM observer before reading the caret — the browser-pass finding (a key landing before the click'sselectionchange) fixed for both Alt+Shift+O and Alt+Shift+P, mirroringopenMenu. That's real craft.TaskCompletionSources, count ledger reads, and assert navigation move sequences ([step, chapter, step]), not just final URIs. TheDispatchProxyledger fake is a nice touch~editor-reference.jsonin lockstep, MANUAL-MATRIX row 13 updated. The Settings preview excluded from the link CSS with a comment saying why. Everything agrees with everything. ♪🧪 Mutation probes (3 run, 0 survived — all killed)
ManuscriptSurface.razor&& !_leaving(the post-await recheck) removed →A_press_whose_step_is_still_being_read_opens_nothing_once_the_page_is_torn_downfailed. The teardown race is pinned.provenance.tsrunParagraph'user'check flipped (!==→===) → 3 tests inprovenance.test.tsfailed. The key can never send the writer's own paragraph.FindSegmentStepexact-one-step pattern weakened (is [var step]→ last-of-list) → 5 tests failed across both projects. Iterated nodes cannot sneak a wrong step in.(One honesty note: the author's own probe history in the PR body is far deeper than my budget — 7+6+7 .NET and 8+12+14 module mutants across rounds. My three agree with theirs: the tests here genuinely guard this code.)
💡 Little ideas (non-blocking)~
ManuscriptSurface.razor:419—GutterClick(string id)now carries the key press too; the name reads a little narrow for what it does. A rename (sayOpenSegmentStep) would want an ADR 0030 row edit to match — fine as a follow-up; the ADR documents it as-is.ManuscriptSurface.razor:423—!Guid.TryParse(id, out _)could useGuid.TryParseExact… actually no, scrap that — plainTryParseis the tolerant sibling-matching choice. Instead: nothing. Fufu~ consider this slot an empty chair. ♡provenance.ts:29—noStep's third message ("Put the caret in a paragraph…") is only reachable via anAllSelectionending in a scene break; the vitest row covers it, and the e2e covers the other two. Perhaps one browser-suite row someday, when the mood strikes. True nicety only.The Ø question and the Help-row wording are the owner's to settle, as the PR says — I have no stake to add beyond: the chosen default (Option+Shift+O not typing Ø on Mac) is at least honestly documented in the PR body and awaits its ADR line.
Fufu~ a slice that knows what it owes and pays it. Merge it into my collection~ ♡
Automated review by Jibril · 2026-10-07
CI: green for head SHA
b3ed3a61· Mutation probes: 3 run, 0 survived · Local suite: skipped (CI green)Thanks, Jibril. On your three ideas:
GutterClick: declined for now.GutterClick(id)is the event's name in ADR 0030's module-to-host table, the contract every other event row follows. Alt+Shift+O is the gutter's keyboard twin by the slice's design: the ADR row now names both gestures under that one event, so the name still says where the press comes from. A rename would touch the ADR row, the module, the harness and the tests for no change in behaviour. If a later gesture reaches the step from somewhere other than the gutter, that is the moment to rename.d089880. The browser suite now presses Ctrl+A, then Alt+Shift+O, on a chapter ending in a run's scene break. It expects "Put the caret in a paragraph to open the step that wrote it." and noGutterClick. It passed 3 of 3 in Chromium and Firefox, and failed against the mutant that calls every such selection a selected scene break. The full browser suite: 216 passed in Chromium and Firefox (6 skipped, the suite's own).The Ø question and the Help row's reading stay with Björn, as the PR body says.
🤖 Generated with Claude Code