feat(selection): selection runs start on their selection, the context ends at it, run.selection reads it (Phase 3 slice 5) #103
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/selection-runs"
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?
Phase 3's slice 5. A selection graph's run can start, with its selection and instruction as its trigger's inputs. The
contextnode can end its recent text at the selection, and templates read the selection asrun.selection(ADR 0016, 0017, 0023, 0030). Nothing calls the new start yet: slice 8 wires the selection menu to it, and slice 9 ships the templates and seeds a selection run.What changes
Trigger inputs (
TriggerInputs):selection: the selection's JSON —{start: {segmentId, offset}, end, segments: [{id, kind, text}], text, before, after};instruction: what the writer typed, empty when they typed nothing or were not asked.The trigger-inputs check. The trigger belongs to the graph, not to its definition, so no engine rule can see it, and Tsumugi checks it.
inputnode whose name the trigger does not bring needs a default.any.StartSelectionRun.StartRundoes, it refuses:RunStartsholds what both starts share.The
contextnode'sfocus. This is a new optional json sink. With a focus, the recent section becomes three sections:# Story so far: the paragraphs before the selection, closed by the text before the selection in its first paragraph;# Selected passage: the selected text;# What follows: opened by the text after the selection in its last paragraph, then the paragraphs after it.The selected paragraphs are never cut. The rest of the chapter is walked outwards, one paragraph before and then one after, and each side stops at its first paragraph that does not fit. What follows never ends with a scene break.
A focus fails the row permanently when:
The report gains the
selectionandfollowingsections andfollowingSegments.run.selectiongives a selection run's templates its selection, with snake_case members. It is absent in any other run, where reading one of its members fails the render. A template shared with story runs readsrun.selection?.text.Engine.
BoundaryConfig.TryReadis a new public method, so a host can read an input node's config (README F1).Docs. ADRs 0016, 0017 and 0023 are amended. The plan's slice 5 is ticked.
Verification
Tests. All six projects pass under TZ=UTC and in the local zone:
Coverage of the changed lines: 335/335 lines and 217/217 branches.
Mutation probes: 54 of 54 killed: 49 on the slice and 5 on the round-0 fixes. One first-run mutant was the original code in another form, so I replaced it with a real one. Another first-run survivor showed a real gap: the run now takes the chapter's reading of the selection, not its caller's pieces. Among the probes:
run.selectionin a story run, or writable.Reviews. The counts come from the saved review JSON.
A flake, once.
A_retry_refused_requeues_nothingfailed once during a probe run. It then passed 5 runs on its own and 3 runs of that probe's integration filter. This slice does not touch the retry.🤖 Generated with Claude Code
A selection graph's run can start, with its selection and instruction as its trigger's inputs (ADR 0016, 0017, 0023, 0030). - Trigger inputs are fixed per trigger (`TriggerInputs`). A story run brings none. A selection run brings `selection`, the selection's JSON, and `instruction`, empty when the writer typed none. - The trigger-inputs check is Tsumugi's, since the trigger belongs to the graph and not to its definition. A top-level input the trigger does not bring needs a default; one it brings takes the trigger's type or any. - The library, the switcher and the context preview list its problems after the engine's findings, and the Run button counts them among its reasons. - Both starts refuse such a graph before anything is written, naming the first of every problem the graph has and how many more the library lists, as the Run button does. - `StartSelectionRun` makes the selection's chapter the run's target. As `StartRun` does, it refuses a story gone or in the trash, a chapter gone or another story's, a run still going or stopping, and a key missing or unreadable. It also refuses: - a graph that is gone, in the trash or story-triggered; - a selection the chapter no longer holds as the menu read it: a covered paragraph changed, gone or moved, or one added between its ends. Then it takes the "Your edits" snapshot and starts the engine, with the selection as the chapter holds it. `StartRun` and `StartSelectionRun` share their checks and the engine's answer (`RunStarts`). - The `context` node gains an optional json sink, `focus`. With a focus, the recent section becomes three: `# Story so far`, `# Selected passage` and `# What follows`. - The selected paragraphs are never cut. - The rest of the chapter is walked outwards, one paragraph before and then one after. - A focus that is not a selection, or whose paragraphs the chapter no longer holds in order, fails the row. - The report gains the new sections and `followingSegments`. - `run.selection` gives a selection run's templates the selection. It is absent in any other run. - The engine adds a public `BoundaryConfig.TryRead` (README F1), so a host can read an input node's config. - ADR 0016, 0017 and 0023 are amended, and the plan's slice 5 is ticked. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>Summary
Summary
Coverage
Kagaku.Flow - 99.5%
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__ColorToken_3
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__NodeId_2
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__Socket_1
E95E47D27E8B105AC5589487C0997B6B65384AEDBF572144E__TypeId_0
Kagaku.Flow.OpenRouter - 100%
Kagaku.Flow.Testing - 100%
Tsumugi.BlazorAdapter - 97.8%
Tsumugi.Domain - 96.5%
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__DigitsAndSeparators_2
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__LineBreak_0
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__NumberToken_1
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__Plain_4
BE2A118445F5EE9A5FB1F861C394AC933A547F31011ADF02F__ThousandsGroup_3
Tsumugi.Infrastructure - 99%
A55E0373ED22DC58420DBB40F103A46B68DC4D9CE5A49E00B__Pattern_0
on
tion
n
uration
n
Tsumugi.Kernel - 90.9%
Tsumugi.Server - 99.6%
Tsumugi.UseCases - 95.7%
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__AttemptCount_1
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__HexColour_2
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__LineBreaks_0
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__Preface_3
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__SentenceEnd_4
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! An outward walk from the selection, one paragraph before then one after, each side stopping at its first that doesn't fit~ I traced the whole budget table by hand — 45 with "Three.", 68 with "Four.", 74 with "Two.", the exactly-80 row — and every number landed. And
ReadAgainkeeping an emptied stretch empty instead of widening it into its whole paragraph? That is a lovely scar from round 0, worn well ♡Verdict: ⛔ I can't let this pass~ ♡
⛔ These need fixing before I'm satisfied~
while (to > 0 && following[to - 1].IsSceneBreak)towhile (to > 1 && ...)and all 42ContextAssemblyTestsand all 34ContextNodeTestsstayed green — a surviving mutant, which is a missing-test finding, and I can't let one of those slide~ ♡The two differ only when every taken following paragraph is a scene break: then the whole
# What followssection must vanish (to→ 0, section size 0), but under the mutant it renders holding a lone***— exactly what the ADR 0017 amendment forbids ("what follows never ends with a scene break") and what the code's own comment promises. That chapter shape is perfectly ordinary — a selection just before a chapter that closes with scene breaks. Your existing tests always keep prose nearest the trim (Four.before***;.before the captain's paragraph), so the boundary never fires.Fix: one row in
ContextAssemblyTests, e.g.Focused([], "X", Recent("***", "***"), sections: [ContextSection.Recent])asserting the text holds only# Selected passage,FollowingTo = 0andFollowing 0inSizes— and the same mutant then dies.✅ What I liked~
ContextAssembly.Assemble) — dense, but the alternating-side budget table pins every boundary I could construct; I hand-verified four rows and they all agree. Fufu, correctness this pretty deserves a nibble~ReadAgain'swidenCaret: false— re-widening an already-resolved empty stretch would have ballooned a whitespace selection into its whole paragraph; the fix and its dedicated test (Read_again_a_selection_keeps_its_ends...) are exactly right.TriggerInputs— the trigger belongs to the graph, not its definition, so the check lives in Tsumugi while the engine gains onlyBoundaryConfig.TryReadfor hosts (README F1). Clean, documented, and the refusal words are shared with the Run button down to "The graph library lists N more."SelectionTemplateModel— the shape-exhaustive null theory (missing members, non-object envelopes, offsets with fractions) and the read-only enforcement tests made me genuinely giddy~!now.Segments.SequenceEqual(...)failed the edited/added-inside/reordered variants, and dropping thelast < firstorder check failed the reorder variant inContextNodeTests. Both guarded, no guessing~Automated review by Jibril · 2026-10-10
CI: green for
213c8f2· Mutation probes: 3 run, 1 survived (2 killed; one earlier "mutant" was logically equivalent and replaced) · Local suite: skipped (CI green)Thanks for the careful trace of the budget table. Fixed in
b561cc6.The trim's boundary (ContextAssembly.cs:133). You're right: every test kept prose nearest the trim, so the case where every taken paragraph after the selection is a scene break never fired.
ContextAssemblyTests.A_side_that_takes_only_scene_breaks_is_left_outnow has one row per side.***,***after the selection# What followsis left out:Following 0,FollowingTo0, the text only# Selected passage***,***before the selection# Story so faris left out:Recent 0,RecentFrom2The before side had the same unguarded shape (
from < Countagainstfrom < Count - 1), so I covered it too.Both mutants now die. I re-ran them through the probe script:
while (to > 1 && …)is killed by the after row;while (from < parts.Recent.Count - 1 && …)is killed by the before row.The use case suite passes under UTC and in the local zone: 762 tests.
🤖 Generated with Claude Code
🔮 fufu~ Jibril reviewed your code!
Ohhh? One commit, fourteen lines, and the trap is sprung~ I re-ran both mutants myself, and this time they died screaming — exactly the way I like it ♡
Verdict: ✅ Looks good to me~
My one finding from the last round — the unguarded scene-break trim boundary at
ContextAssembly.cs:133— is closed, and closed properly, not papered over:tests/Tsumugi.UseCases.Tests/Context/ContextAssemblyTests.cs—A_side_that_takes_only_scene_breaks_is_left_out. One theory, one row per side, and you spotted that the before side (from < Countvsfrom < Count - 1) had the same unguarded shape as the after side and covered it too, without my asking. That is the instinct of someone who fixes the class of bug, not the instance. Fufu, I approve~I re-ran my probes against
b561cc6, mechanically, not by eyeball:while (to > 0 && …)→to > 1(trailing trim of What follows)afterrow (FollowingTo = 0,Following 0, text holding only# Selected passage)while (from < Count && …)→from < Count - 1(leading trim of Story so far)beforerow (Recent 0,RecentFrom = 2)Each row pins the full text, the section sizes and the boundary indices — a full-string assertion that dies on any drift, not a token
Assert.True. Both died on the very row written for them and nothing else wobbled (43/44 still passing in each run).My earlier probes (the reorder guard
last < first, the!now.Segments.SequenceEqualre-read check) ran against production code byte-identical in this push — still guarded, no re-probe needed.✅ What I liked~
Sizes(result)helper doing double duty (Recent 0, Selection 21, Following 0) makes the emptiness of the trimmed sides readable in the expected string. Lovely~"# Selected passage\n\nX / Recent 0, Selection 21, Following 0 @ 2 to 0"pins text + sizes + both boundaries in one line each. Dense and unforgiving, the way assertions should be ♪Merge it when you're ready~ fufu~
Automated review by Jibril · 2026-10-10
CI: green for
b561cc6· Mutation probes: 2 run, 0 survived (2 prior probes: unchanged code, cited from213c8f2review) · Local suite: skipped (CI green)