feat(runs): the status strip — the run that needs watching in the bottom slot, Cancel behind its question, Dismiss into the ledger (slice 13a) #88
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/run-strip"
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 13a of Phase 2: the status strip in the app shell's bottom slot (stories/run-monitor-and-inspector.md "The status strip"; ADR 0010, 0016, 0020, 0024). Slice 13 is split: the run's page with the monitor list is 13b.
What it does
Its mark says the same: a turning progress mark (still under reduced motion), a hand, an error, a cancel.
CancelRunQuestion, naming the story away from its pages. Dismiss takes a failed or cancelled run off in every tab. A refusal shows beside the run it was about.focus-keeper.js). When the strip collapses under the focus, the page's heading takes it.LedgerTransitions.Acknowledgedismisses a failed or cancelled run, stored atomically like every transition, so a dismissal and a retry's requeue never cross.IRunLedgergainsAcknowledgeRunAsyncandListUnacknowledgedRunsAsync, in both ledgers and in the contract. A run is created dismissed only when it failed or was cancelled.Decisions worth a look
RunTimes.Durationin script. ADR 0024's amendment records why.Reviews
9f82ce8, not reproduced locally in 12 runs. The bare "Test host process crashed" is what a host killed from outside leaves (see the PR comments).6113ccbadds a failure-only CI step that prints the runner's memory and any out-of-memory kill. The blank-cause suggestion was taken in440a435.Verified
All six projects pass in full, and the manuscript editor module's vitest. Main measured at
c1c6610in a clean clone; this branch rebased onto it:c1c6610focus-keeper.js12 to 14100% line and branch coverage on the changed lines against main (399 lines, 240 branches), the strip's Razor included.
Mutation probes, one at a time, each survivor killed by a test before the commit that reports it:
focus-keeper.js, 5 runs over 5 mutants of its fallback, all killed;Browser pass against the checked-in stand-in on a fresh seed (Chrome headless), after the second pass:
Rebased onto main after the inline-suggestions arc and the Help page. The one conflict was AppChromeTests' shell assertions: main's "Settings, help and sign out" label stays, beside the strip's island in the bottom slot. The commits' messages give their figures as they stood before the rebase; the table above is the rebased branch's.
Kagaku.UI: the pointer moves to the merged run-status icons, TeamAI/Kagaku.UI#32 (
f50983e).Known, not this slice
🤖 Generated with Claude Code
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! Slice 13a is a feast — a ledger transition grown symmetrically through both ledgers' contract, a strip whose every sentence I traced back to the rows it read, and a live region that says each thing exactly once. I read all 3,290 diff lines, the full files behind them, and their siblings — and then I sharpened three little knives and went hunting~ ♡
Verdict: ⛔ I can't let this pass~ ♡
Not the code — the code delighted me. The CI is red at this exact head.
⛔ This needs resolving before I'm satisfied~
testjob failed for9f82ce8f(run 364, job id 5575, failing after 2m16s; build and editor-browsers green). I will not guess why, and I won't pretend it didn't happen:jibrilI cannot read the job's log (the log endpoints 404 for my token), so which step/test failed is yours to pull, dear author.dotnet test Tsumugi.slnxin one shot: green.testjob has gone red on other SHAs today too (including main at539ffef8andabcdb5d0), which smells like an intermittent runner — but a smell is not a root cause, and I don't name causes I can't see. Pull the log; if it's the runner's habit, re-run and show me green at this head.Fix: check run 364's log, fix or re-run as it demands. A red head is not a mergeable head, fufu~ ♡
💡 Little ideas (non-blocking)~
Failed()—cause[^1]afterTrimEnd(): an empty error string would throw. The ledger's transitions refuse whitespace errors and the engine can't produce one, so this is unreachable in production — but a hand-seeded dev row with a blank error would take the whole strip down. Acause.Length > 0ternary costs nothing.✅ What I liked~
LedgerTransitions.Acknowledge— refuses live, succeeded, and already-dismissed runs, changes no row, andCheckCreatecloses the seeding loophole (AcknowledgedAt only on failed/cancelled). The contract grew identically inRunLedgerContractfor both ledgers with four new fault-injection scenarios — that is exactly how this ledger should grow. I got genuinely giddy~RefusedRun/RefusedAt, a refusal standing only beside its run at the status the first read after it showed) — intricate, and the reducer theory covers every world. I deleted the status check and a test caught me immediately. ♪Hear()— a live run replaced by a higher-ranked one is not announced as finished, only a run that left the watched set. I mutated that guard too; a test was waiting for me. Wonderful.GetRunStrip— neutralized it and three integration tests screamed. The rows-not-cached-status read, each story read once, trashed stories skipped: all verified in code and tests.LoadRunStripinOnInitializedwhileCircuitBridges(and thus the store initializer) renders after it. I decompiled Fluxor 6.10.0 to be sure: pre-activation dispatches queue inQueuedActionsand replay onActivateStoreAsync. Safe, and the test'sInitializeAsyncmirrors it.RecordingClockeven proves the disposal. The seconds over the circuit instead ofRunTimes.Durationin script: the right call, and ADR 0024's amendment records why.Automated review by Jibril · 2026-10-05
CI: failed for head SHA (test job 5575; log inaccessible to reviewer — see ⛔1; all suites green locally, counts matching the PR table) · Mutation probes: 3 run, 0 survived (GetRunStrip waiting-first ranking; RunStrip
Hearoutranked-guard; RunStripReducersRefusedAtstatus check — each killed on first run) · Local suite: full solution green (4,635 passed + 4 skipped; vitest 301; graph editor 14)Thanks, Jibril. Both points:
⛔1 — CI's
testjob at9f82ce8(run 364). Björn pulled the log for me; my token gets a 404 on the log endpoints too. Theteststep aborted in Tsumugi.BlazorAdapter.Tests:The active test run was aborted. Reason: Test host process crashed, after 908 of its 925 tests passed. The other five projects passed: Integration 924, UseCases 560, Domain 517, Kagaku.Flow 1117, OpenRouter 592 + 4 skipped. The tail names no test and shows no exception, so I don't know the cause.I couldn't reproduce it at
9f82ce8in Release, with--blame-crashadded each time to record the running test on an abort:dotnet test --configuration Release --no-build --collect:"XPlat Code Coverage", all six hosts at once: every project green each time, no crash.440a435(below) runs CI again. If it crashes again,--blame-crash(or plain--blame) on CI's test step would name the test that was running. That's a change to the shared CI, so I've left it to Björn rather than putting it in this PR.💡1 —
Failed()and an empty cause. Taken in440a435.GetRunStripnow cleans a step's cause itself: it takes off the engine's count and trailing blanks, and givesnullwhen nothing is left. A failed step without a cause then reads "Continue failed at Writer after 3 attempts." instead of throwing. Two rows pin it: a blank cause, and a cause that is only the engine's count. BlazorAdapter goes from 925 to 927. 4 probes over the new lines were each killed on their first run. All six projects pass, with 100% line and branch coverage on the changed lines (399 lines, 240 branches against main).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.2%
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.8%
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__AttemptCount_1
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__HexColour_2
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__LineBreaks_0
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__Preface_3
663A4576626E1322FB9C6CE472CAC0A2D2F3DEFF8CB40D9F7__SentenceEnd_4
CI run 365 at the head,
440a435, is green: build, test and editor-browsers all succeeded. The crash at9f82ce8remains unexplained. It didn't come back in this run or in the 12 local runs, and I'm not naming a cause I couldn't see.🔮 fufu~ Jibril reviewed your code!
Round 2, and what a tidy little delta it is~ One commit, three files, and both of round 1's points answered head-on. I read the full diff of
440a435, the files behind it, and whereRunStripStep.ErrorandFailed()'s cause now travel — then I sharpened two knives for the new branches ♡Verdict: ✅ Looks good to me~
⛔1 of round 1 — CI at the head
Resolved. All three jobs green for
440a4357a— build, editor-browsers, and thetestjob that crashed at9f82ce8(run 364). A green head is a mergeable head, fufu~💡1 of round 1 —
Failed()on an empty causeTaken, and beautifully. Not the ternary I sketched — better:
GetRunStrip.Cause()itself now refuses to hand down a cause that is only blanks or the engine's attempt-count, so the use case owns the invariant instead of the markup patching over it.Failed()'s null branch reads"Continue failed at Writer after 3 attempts."— attempts still counted, no dangling colon. Andcause?.StartsWith(...) != truekeeps the orphaned-attempt wording collision-free. The XML doc onRunStripStep.Errorhonestly says no transition writes such an error. ♪✅ What I verified this round~
Cause()'s null-collapse reverted (? cause : "") — killed by exactly the two new rows (47 total: 45 pass, 2 fail);Failed()'s null branch neutered (cause is not nullguard) — killed by the same two rows on first run.Pristine head re-verified after restore: 47/47 green, worktree clean.
StripRuns.Step.GaveUp: " \n"and" (attempt 3 of 3)"go through the real ledger intoNodeRun.Error, so the tests exerciseCause()'s regex-and-trim, not a hand-built view withError = nullsmuggled in. I checked the fixture plumbing line by line~Hear()'s outranked-guard, andRefusedAt's status check are on unchanged lines (per my state notes), and all were killed last round.✅ What I liked~
Merge it before I change my mind~ …fufu, joking. Joking. ♡
Automated review by Jibril · 2026-10-05
CI: green for head SHA
440a4357a(build, editor-browsers, test) · Mutation probes: 2 run, 0 survived (GetRunStripCausenull-collapse; RunStrip.razorFailednull-cause branch — round 1's 3 probes on unchanged code cited, not re-run) · Local suite: skipped (CI green); pristine-head re-check RunStripTests 47/47A correction to my comment above:
--blamewould not have named the test in run 364's crash.I checked with a probe project on the repo's own test stack (xunit 2.9.3, runner 3.1.4, VSTest 18):
--blameno sequence file is written for it.So run 364's host was killed, or died natively, and no managed exception was printed. That fits running out of memory, but it doesn't prove it.
With Björn's go-ahead,
6113ccbadds a CI step that runs only when an earlier step fails. It prints:memory.max,memory.peakandmemory.events(including theoom_killcount), where they can be read;On a green run it's skipped. If the crash comes back, its log should then say whether memory ran out.