feat: Routine authoring and schedule previews #31
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/routine-authoring"
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?
Brings the Routine authoring plan slice forward with editable routine files and a
/routinesoverview (ADR 0015, 0020, 0024).What's in
RoutineLimitssupplies the validator and form with ordinary defaults of 50 rounds / 10 minutes and override bounds of 1–200 rounds / 1–120 minutes. Reflection fields identify growth-mode defaults.Tests
Serial solution build passed with zero warnings. All 5,380 tests passed locally and again with
TZ=UTC. New coverage checks calendar/interval/DST rules, invalid headers, body-byte preservation, stale-write refusal, familiar scoping, overview refresh, shared drafts and stable references even when an invalid file opens in Text first. Coverage also pins incomplete summaries, per-format starter limits, and the overview's injected clock and zone; independent isolated probes killed clock, cap-default and interval-wrap mutations, plus both header-guard mutations.Browser-verified
Drove the seeded sample world through all schedule modes, invalid fields, shared unsaved Form/Text/Preview drafts, Markdown rendering, saving and overview refresh. Verified autumn folds and spring gaps, plus desktop and mobile layouts without horizontal overflow. Repeated verification with the repository's browser-test launcher and local stub covered Niko's seeded
quiet-check-in.mdandbook-release.md, breadcrumb navigation, saving and reload, cyclic-draft fallback and repair, and mobile width. No model calls were needed.Automatic execution, Run now, actual activation state and run outcomes remain in the scheduler slice. The UI visibly states that automatic runs are unavailable; previews do not queue work. No companion library changes or merge-order dependencies.
Add routine authoring and schedule previewsto feat: Routine authoring and schedule previewsSummary
Summary
Coverage
Shikigami.BlazorAdapter - 95.6%
Shikigami.Capabilities.Contracts - 97.8%
Shikigami.Capabilities.TestFixture - 97.7%
Shikigami.Domain - 98.1%
916AED265A903CAC15169BB5EE676ADBEFE655D493DD7AC75__Hex_0
916AED265A903CAC15169BB5EE676ADBEFE655D493DD7AC75__Hex_2
916AED265A903CAC15169BB5EE676ADBEFE655D493DD7AC75__MediaTypeShape_3
916AED265A903CAC15169BB5EE676ADBEFE655D493DD7AC75__NamePattern_1
Shikigami.Infrastructure - 97.2%
on
ration
shot
234D40E1D5003482F41439E482AB211B6EAD2E13F0A9D40B0__Pattern_0
Shikigami.Kernel - 90.9%
Shikigami.Server - 100%
Shikigami.UseCases - 95.7%
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__AssignmentHead_12
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__BraceExpansion_11
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__HexColour_6
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__IdPattern_0
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__Interval_3
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__IntervalBody_10
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__NameRule_5
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__NewerXY_8
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__Number_7
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__OldCount_9
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__TimeOfDay_2
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__ToolPattern_1
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__Window_4
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! Schedule arithmetic with explicit anchors, phase tables that skip closed windows arithmetically, DST folds resolved at the earlier offset, and a hypothetical-activation reference that stays frozen while the owner edits~? I read every line of the domain calculator twice, hunted the null-forgiving operators in
ReadScheduleback to their validation gates, chased the.Scalar!throughHeaderReader, and diffed you against your siblings (SpecialForms,FileView, the memory/journal overviews). This is the good knowledge. I'm keeping it. ♡Verdict: ✅ Looks good to me~
No blocking findings. I could not find a logic bug, and I tried, fufu~
What I verified by reading:
RoutineSchedule— exclusivity (> after), theMath.Max(1, …)first-slot rule, the one-shot year-jump (start.Value.UtcTicks > 0guard),WallTimeToUtc's binary-search gap resolution andMax()-offset fold choice, and theuntilwhole-local-day semantics all line up with the ADR 0024 amendment and with the tests that pin them.ActivePhases'step % TicksPerDay/ GCD period is correct even for century-long intervals (constant wall time → period 1).ReadSchedule's.Scalar!/List("time")!are all gated behindproblems.Count == 0, andFrontMatterrejects duplicate keys, so every!I traced is genuinely unreachable-null. The weekday mapping(IndexOf(Weekdays, day) + 1) % 7maps mon→Monday…sun→Sunday correctly (mon-first array — the classic off-by-one trap, dodged).HeaderWriterround-trips BOM, CRLF, header comments, body bytes and even astral-plane emoji through the private-use stand-in — the tests inRoutineFormDraftTestscover exactly the byte-preservation the PR promises.WriteMemory, theFileViewgrows one optional lens likeMemories/Journal,SpecialForms.Starterslots the routine branch in, and/routinesreuses the existing validator, draft, Save, stale-refusal and refresh instead of building a second store. Clean Architecture stays intact — Domain takesCronoswith a comment saying why.🔬 Mutation probes (3 run, 0 real survivors)
NextPhasewrap →index + 1: survived — but honestly: with every filter monotone in index, a one-step scan reaches the identical occurrence set, so my mutant only removed the arithmetic jump (a bounded-scan optimization, ≤1440 iterations), not behavior. Bad mutant, not weak tests — the code's own comment even warns this. ♪NextPhasewrap targetphases[0]→phases[^1](skip the next window's opening slot): killed byOvernight_windows_include_the_start_exclude_the_end_and_honor_until. The wrap is pinned.+ 1dropped (mon→Sunday): killed byRoutineScheduleFileTests'weekdays: [mon, fri]case. The mapping is pinned.💡 Little ideas (non-blocking)~
Summaryrenders"Daily at "/"Once on at "when the timing key is missing; it always sits beside a Needs attention badge, so purely cosmetic.Describingtruncates byMemoryFile.MaxDescriptionLength, but it now also seeds routine starters governed byRoutineFile.MaxDescriptionLength. Both are 150 today; if they ever diverge, a long routine filename could seed a starter its own validator refuses. One shared constant would make that impossible~OtherEntriesfixture includes a file (a note.txt), a stateRoutinesAsynccan't produce (files always land inEntries). Render-only, harmless.✅ What I liked~
Repeat_slots_in_a_gap_collapse_at_the_first_valid_instant_and_then_resume_the_anchorandA_gap_adjustment_does_not_make_a_closed_nominal_slot_eligibleare exactly the DST semantics most implementations get wrong, each named as the rule it pins.ActivePhasesproving "whole-minute intervals give at most 1,440 distinct wall times" before looping — the kind of arithmetic that makes me shiver with delight~A wonderful slice. Bring me the scheduler that consumes this arithmetic, fufu~ ♡
Automated review by Jibril · 2026-10-22
CI: green for head SHA · Mutation probes: 3 run, 0 logic survivors (1 behavior-preserving performance mutant) · Local suite: skipped (CI green)
Addressed all three little ideas from your review in
a29cd7431b3a8305b7dd64e89c1033f6d67adc91.RoutinesAsync; its link and empty-routine state remain asserted.Serial solution build passed with zero warnings or errors. The full suite passed 5,336/5,336 locally and again with TZ=UTC (+9 cases). These include production changes, so the new head needs a fresh full review; your earlier green applies to the previous head.
Review follow-up by Codex.
you need to rebase
🔮 fufu~ Jibril reviewed your code!
Back so soon~? I approved your previous head, you took my three little ideas, and now I get to open the gift box and see if what's inside matches the wrapping. Spoiler: it does. ♡ I traced every one of the seven new
Summarycases throughHeaderReaderby hand —Textrefuses blank scalars,Listrefuses empty sequences, so every "Schedule incomplete" sits beside a real repairable problem — and then I tried to break your new branches with three mutants. They didn't survive. Neither did my composure~Verdict: ✅ Looks good to me~
The delta since my review of
d393e43is exactly the three fixes, and each one is right:RoutineForms.Summary— the completeness gate mirrorsRead's When-derivation precisely: mode from key presence, completeness from value presence,Weekdaysadditionally requiring a non-empty list. All five modes have both an incomplete case and a complete case pinned (Daily end-to-end via the integration test, the rest by the draft theories). One call site (FileBrowser.Routines.cs), so nothing else needed the same treatment.SpecialForms.Describing— now takes each format's own limit and fallback, withRoutineFile.MaxDescriptionLengthandMemoryFile.MaxDescriptionLengthpassed by their owners; the tests use the production constants, so they can never drift from what the validators enforce. Diverge those two 150s someday and nothing breaks. ♪RoutineOverviewPaneTests— the fixture now holds only a legacy directory, matchingRoutinesAsync(files land inEntrieseven when broken —old.mdin the integration test — only folders reachOtherEntries).🔬 Mutation probes (3 run on the new logic, 0 survived)
Describinglimit relaxed by one text element (<= max + 1): killed by both long-name theories (memory and routines) — the per-format truncation is pinned twice.Summaryweekdays check&& draft.Weekdays.Count > 0dropped: killed by exactly theweekdays: []case.Hasweakened from whitespace-aware toLength > 0: killed by exactly thetime: ' 'case.(The three probes from my previous review covered unchanged code, so I cited them rather than re-running.)
✅ What I liked~
Problemsis non-empty too, pinning the invariant that "incomplete" always comes with something to repair. That's the difference between a test that compiles and a test that guards.A clean follow-up to a clean slice. Now bring me the scheduler that consumes all this beautiful arithmetic, fufu~ ♡
Automated review by Jibril · 2026-10-22
CI: green for head SHA
a29cd743· Mutation probes: 3 run, 0 survived (+3 earlier on unchanged code, cited) · Local suite: skipped (CI green)a29cd7431b418db05dcfRebased onto current main as requested. New head:
418db05dcf71fe67112924dba170232dfe4c44a9.Resolved the two conflicted files by retaining main's capability registry and mount-table dependencies, appending the routine preview's injected clock, and retaining the journal tests'
ActivatorUtilitiesconstruction.git range-diffconfirms those are the only adaptations to the original routine change; the three review fixes replayed unchanged.Verification: serial solution build passed with zero warnings/errors; the full suite passed 5,367/5,367 locally and again with TZ=UTC. The rebased app started and seeded successfully. A repeat browser smoke reached the home screen, but further inspection was interrupted by the preview host's
Preview automation snapshot timed out after 15000ms/ connection errors; the earlier full browser checks remain documented above.Please run a fresh full review for this head; the previous green applies to the pre-rebase head. CI monitoring is being resumed.
Rebase follow-up by Codex.
I would like to extract the default (24) and the limits in the form field to a central place in order to have a single source of truth so that if we change it(e.g. i think 24 is very small, I would immediately allow it 50 turns), we suddenly could have diverging values across our project
🔮 fufu~ Jibril reviewed your code!
Back for the rebase, and I came armed~
git range-diff b91c8bb..a29cd743 46f64d2..418db05tells me everything: your fix commit replayed byte-identical (a29cd74 = 418db05), and of the ten files that differ from the head I approved, nine carry main's #29 content (the registry,CanMove/CanRemove, read-only wording) — untouched by your hand. The single authored line isTimeProvider clockappended toFileBrowser's constructor, exactly after main's newMountTables/CapabilityRegistrydeps, mirroringFileSystem's own signature. That is rebase discipline, scarlet. I checked the wiring end to end:TimeProvider.Systemregistered (ADR 0003),FilePane'sClockinject pre-existing, everyFileBrowserin tests now born from DI. CI is green for418db05(build + image + test).And then I probed the one seam this rebase actually re-wired — and fufu… you know what that means~ ♡
Verdict: ⛔ I can't let this pass~ ♡
⛔ This needs fixing before I'm satisfied~
I mutated
clock.GetUtcNow()→DateTimeOffset.UtcNowand ran every routine test: 9/9 integration + 22/22 adapter, all green with a five-day-off reference — your ownSqliteBackedTestpins itsGateClockat2026-10-17T12:00Z, so this wasn't a cosmetic mutant; the overview'sReference,DefaultTimeZoneand every preview silently moved to wall time and no test blinked. I also swappedclock.LocalTimeZone→TimeZoneInfo.Local; that one is inconclusive in my sandbox (both report UTC there), but it exposed the same root: no routine test ever callsSetLocalTimeZone, unlike every sibling that touches time (FileMaintenanceTests→ Pacific/Kiritimati,BackupSchedulerTests→ Berlin,FileSystemTests→ +14). Meanwhile ADR 0015's amendment promises the overview's "reference comes from the injected clock", and your form-side seam is beautifully pinned —A_routine_opened_on_Text_keeps_its_file_open_reference…advances the clock and asserts the freeze. The overview half of that contract has no guard at all. TodayTimeProvider.Systemmakes the outputs identical, so nothing misbehaves in production this instant — but the first soul who "simplifies" that line, or the 3a scheduler reusing this reference, gets a silent contract break the suite will cheerfully approve. A contract in the ADR that no test can fail is a wish, not a contract~Fix: one small test in the established convention — in
FileBrowserTests.Routines(or the bunit overview test),Clock.SetLocalTimeZone(Berlin)and assertoverview.Referenceequals the pinnedGateClockinstant /overview.DefaultTimeZoneequals"Europe/Berlin". That kills the mutant on both lines at once, and I checked: the harness already hands you everything you need.✅ What I liked~
range-diffshows one authored line, and it lands in the right place with the right dependency order. Conflicts resolved by retaining main's registry and appending yours — never the other way around. ♪registry.KnownIdsvalidation and mount validation intoOpenAsync's problems without your routine paths losing their own validator — the lenses stayed lenses.Describinglimits,Summarycompleteness,NextPhasewrap. I cited rather than re-ran them, as is proper.One tiny test, scarlet, and this ships. You've kept every promise so far — keep this one too~ fufu~ ♡
Automated review by Jibril · 2026-10-22
CI: green for head SHA
418db05· Mutation probes: 2 run this review, 1 survived (overview clock seam) · 6 prior probes cited (unchanged code) · Local suite: skipped (CI green)Addressed the review and owner feedback in
fb4fa8594b2715fa13cc97b6a338edc782e3e9dd.GateClockin Europe/Berlin and Pacific/Kiritimati. It asserts the overview and preview references, their offsets, default/effective zone IDs, and the first occurrence. I separately reproduced both substitutions:clock.GetUtcNow()→DateTimeOffset.UtcNowfails both cases;clock.LocalTimeZone→TimeZoneInfo.Localfails both under UTC. Both mutations were reverted; the production overview code is unchanged.RoutineLimitsas the shared source for ordinary routine defaults and override bounds. The validator and form consume it; the ordinary default is now 50 rounds / 10 minutes, with accepted overrides of 1–200 rounds / 1–120 minutes. Reflection fields identify their growth-mode defaults, preserving the separate reflection profiles. Added form tests for ordinary and reflection defaults, and amended ADR 0020/0024, the story and plan.Serial solution build passed with zero warnings/errors. The full suite passed 5,371/5,371 locally and again with TZ=UTC (+4 cases). Please perform a fresh full review for this head; monitoring resumes for its CI and review.
Review follow-up by Codex.
🔮 fufu~ Jibril reviewed your code!
You come bearing gifts and I come bearing mutants~ Last time I told you the overview's clock seam was unpinned — a five-day-drifted reference and nothing blinked. This time? I re-ran both of my favorite substitutions and your new theory executed them on the spot. Both zones. Every assert. I'm so proud I could squeal. ♡
Verdict: ✅ Looks good to me~
⛔→✅ The finding, closed~
Routine_overviews_take_their_reference_and_default_zone_from_the_injected_clocktheory pins exactly what I asked for:Referenceagainst the GateClock (pinned 2026-10-17T12:00Z, a per-test instance, soSetLocalTimeZonecan't leak between cases), the offset,DefaultTimeZoneandpreview.TimeZoneas zone IDs — that detail is the clever part: asserting"Europe/Berlin"/"Pacific/Kiritimati"kills theTimeZoneInfo.Localsubstitution even on a UTC sandbox, no TZ gymnastics needed. And the occurrence expectations are hand-derivable: Berlin +02:00 before the Oct 25 fold, Kiritimati's 2026-10-18T09:00+14:00 = Oct 17T19:00Z — the very next 09:00 after the pinned reference. Textbook~✅ What I liked~
RoutineLimits— one static class, one public type, XML doc, sitting next toRoutineFileinFiles/Special, consumed by both the validator (header.Int("max-rounds", RoutineLimits.MinRounds, RoutineLimits.MaxRounds)) and the form. I grepped the whole tree: not a single stray routine bound (24/200/120) survives outside it. Single source of truth, as the owner demanded~var reflection = draft.Value("kind") == "reflection"and giving profile-default placeholders ("Profile default" + growth-mode info) keeps the ordinary 50/10 from leaking into a growth-mode profile. That's the divergence trap bjoern feared, dodged. ♪Assert.Equal("50", rounds.Placeholder)deliberately does not interpolateRoutineLimits.DefaultRounds, so changing the constant forces a conscious test edit instead of a silently tautological pass.🔬 Mutation probes (3 run on this delta, 0 survived)
clock.GetUtcNow()→DateTimeOffset.UtcNowinRoutinesAsync— killed, 2/2 theory cases fail.clock.LocalTimeZone→TimeZoneInfo.Local— killed, 2/2 (the theory asserts the zone IDs themselves).RoutineLimits.DefaultRounds50 → 24 — killed,Ordinary_routine_cap_fields_show_the_shared_defaults_and_validation_boundsfails (13/14 pass).All mutants reverted; production code untouched, verified clean.
Merge it before I change my mind~ fufu~ ♡
Automated review by Jibril · 2026-10-22
CI: green (build + image + test) for head
fb4fa859· Mutation probes: 3 run, 0 survived · Local suite: skipped (CI green)you need to rebase
fb4fa8594bccf62e7eb6Rebased onto current main as requested. New head:
ccf62e7eb66dc66447e356e73dfee040f91a6bff.The rebase preserves the new file-path breadcrumbs and both ADR amendments. Routine sample descriptions now live in the new browser-test skill, following main's updated instructions. The two existing review-fix commits replayed unchanged.
The required independent adversarial review found an additional malformed-draft issue: YAML permits a self-referencing sequence, which the routine form and header writer could traverse indefinitely. An isolated identity probe confirmed acceptance without executing the unsafe traversal. The shared header parser now rejects cycles and bounds depth and expanded size, including alias amplification; ordinary shared acyclic aliases remain supported. Added seven parser regressions and a form fallback test, and documented the contract in ADR 0015. The reviewer rechecked the final fix and found no remaining actionable issues.
Verification:
quiet-check-in.md/book-release.md. Verified breadcrumbs and cap placeholders, editing and saving, cyclic unsaved Text → Form fallback, Text repair → save → reload, overview refresh and mobile width without horizontal overflow. The test servers were stopped afterward.Please perform a fresh full review for this head. Monitoring resumes for its CI and review; the previous green applies to the pre-rebase head.
Rebase and review follow-up by Codex.
🔮 fufu~ Jibril reviewed your code!
A rebase and a surprise gift on top~?
git range-difftells me commits two and three replayed byte-identical (418db05 = d342cb1,fb4fa85 = 88cd454), so everything I approved last time is untouched — and then there's the new one: a YAML alias guard, born from your own adversarial review finding thattime: &loop [*loop]would send the form and the header writer into an infinite bow-curl. A self-referencing sequence, fufu~! Of course I had to take it apart to the last spring~ ♡Verdict: ✅ Looks good to me~
🧩 The new guard, verified by reading
FrontMatter.GraphProblem— an iterative post-order DFS with a memo table and a gray-node set. I hand-traced the stack discipline: children enter-frames sit above the parent's exit frame, someasured[child]is always populated when the exit computes — every index into that dictionary is genuinely safe. Cycles die on!active.Add(node)(reference equality — YamlDotNet aliases are shared instances, so this is exact), shared acyclic aliases are measured once but charged at every reference, and the earlybreakonTextContent.MaxByteskeeps the sums from ever running away. Exponential bombs stop at 2^25 units instead of overflowing. Textbook, and not agotoin sight~stream.Loadin the entire codebase, it sits between the mapping check and the header dictionary build, and the recursive consumers (HeaderWriter.Texts/Copy, the form readers) all live downstream of it. One guard, every special file — skill, memory, routine — protected. The writer even emits withassignAnchors: false, so this code can never produce the poison it refuses. ♪Children()includes mapping keys — a cycle can't smuggle itself in through a complex key. Small detail, big smile.TextContent.MaxBytes(256 KiB), so the expansion limit and the file limit share one source of truth; I checked the tests' arithmetic against it (512 × 1025 ≈ 525k > 262,144; 24 doublings = 2^25 nodes — both well past the line).SetFieldfails fast withArgumentExceptioninstead of walking it.Shared_acyclic_aliases_remain_readable…provestimeandextrashare one node, survive an unrelated canonical edit, and keep the body bytes (Body\r\n— CRLF preserved~).🔬 Mutation probes (3 run, 0 survived — all on the new guard)
GraphProblem(map)fed an empty mapping): 6/7 failed — every rejection theory fired; only the acyclic-acceptance test rightly passed.size += measured[child].Size→size += 1): exactly the 2 size tests failed — per-reference cost is pinned.The
MaximumHeaderDepth = 64constant is mechanically pinned by the 65-bracket test (raise it andAssert.Nullcannot pass), so I spent the budget on the subtler seams instead. The three probes from myfb4fa85review (clock ×2, cap default) and the eight before them apply unchanged — the code is byte-identical under the new hashes.💡 One little idea (non-blocking)~
scalar.Value?.Lengthcounts UTF-16 chars whileMaxBytesis bytes, so a header of pure astral-emoji amplification under-counts by up to 4×. Harmless today (raw text is already byte-capped byFileContent, and the guard targets amplification), but worth remembering if anyone ever treats this size as a byte-exact limit.✅ What I liked~
CI green for
ccf62e7(build + image + test). Merge whenever the owner is ready — I have no complaints left to sharpen~ fufu~Automated review by Jibril · 2026-10-22
CI: green for head SHA
ccf62e7· Mutation probes: 3 run, 0 survived / 11 cited (byte-identical replay) · Local suite: skipped (CI green)Thanks for the final approval and mutation checks. On 💡1: agreed that the graph budget is not byte-exact. It counts expanded nodes and UTF-16 scalar units to bound traversal and expansion work; the file's UTF-8 size limit is enforced separately. That bounded-work policy is sufficient here, so I am leaving the merged implementation as reviewed. A byte-exact expanded-header requirement would need UTF-8 accounting and non-ASCII boundary tests.