feat: cleaning 5/12 — the planner: calls, windows and canvases, model rects, seeds and label lint #171
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/cleaning-planner"
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?
This is slice 5 of Phase 5 in
PROJECT_PLAN.md: the planner from ADR 0027 ("The planner portscleaner.py'splan(), with named divergences"). It turns a page's owed regions into calls, each with its window, canvas and defaults. It also adds the model rects and default footprints the calls need, the page's call budget, seeds, the two label templates and the label lint. Nothing calls it yet; the stage arrives in slices 7 and 8.What's in
UseCases: the planner and its policy, pure functions over DTOs.
CleaningPlannertakesOwedRegions and returns aCleaningPlan. Each region carries T, its form,Region.Order, its model rect and whether the fill gate refused it.CallFrames:CleaningPlanner.CallBudget: max(2, 2 × the calls holding a region the gate refused), fixed at planning.CleaningSeeds: the automatic seed, the advance past every used seed, replay seeds s + i, and the server's range check.CleaningLabels: ADR 0021's two templates, with the keep and beneath clauses composed ascleaner.pydoes. The lint returns typed problems.CleaningRecipe. The planner's own constants are in a newCleaningPlanOptions.Infrastructure: model rects and default footprints.
IRegionInkAnalyzergainsGeometryAsync, implemented over a newLabGeometrybeside slice 4's ink analysis. It returns:ADR 0027's divergences from
plan(), each pinned by a test that fails under the lab's rule:Region.Order, and T is the bbox's floor/ceil pixel rect;clean()framed it, at the exact scale. The prototype rounded the scale to 4 decimals, which pushed one corpus rect past its canvas;Docs: ADR 0027's planner bullet and its seed-and-label rule now state these clauses, in place.
Tests
2,468 green and 3 skipped, against 2,263 on main (+205). It was 2,464 at the first review, +4 in the review round:
The new skip is the opt-in corpus test below. Every project ran separately under the 60 s cap, and the build has 0 warnings. What they pin:
Plan parity.
Cleaning/oracle/gen_plan_oracle.pyimports the lab'scleaner.pyand writesplan.json:cleaner.py's own answer beside Orihon's.Before writing, it checks its copies of
plan()andplan_window()againstcleaner.pyunder the lab's rules. Corpus model rects are committed as numbers, never pixels. A rerun reproduces every file byte for byte, with the versions recorded.Geometry parity on 22 drawn pages (
geometry.json): model rects, the outline flag and footprints, including dented and spiked balloon walls.Labels (
labels.json): 22 composed labels and 117 lint cases fromcleaner.py's own outputs.Frames: the trim's tie in both orientations, a trimmed window inside the one asked for and on the page, a page-limited side at its exact boundary, and the magnification bounds.
Planning: crowds of 4 and 5, the rj-p13 shape, companions (one the page edge cuts included), the group's reach at exactly its edge and at the 300-px floor, and the neighbour measured in a straight line to its box.
The opt-in corpus test,
PlanCorpusParityTests, is skipped unlessORIHON_CLEANLAB_PLANnames an export fromCleaning/oracle/export_plan_corpus.py. I ran it myself on the branch before the review round, which added a test only, exporting 265 regions on 29 pages, and it passed. The model rects, outline flags and footprints matchcleaner.py, and planning from the computed model rects reproduces all 175 committed calls. I deleted the export afterwards.Mutation. Two independent reviews: one ran 323 mutants over every changed file, and one checked the port against
cleaner.pyand the ADRs line by line. Every surviving arm is now pinned, or deleted where it was redundant.Browser-verified
Nothing to verify in a browser: this slice has no UI, no seed change and no caller.
Honest notes
cleaner.py'sregion_alphaclamps the blur at its call window's edge; the two agree wherever the window holds the blur's reach.fill_verify). It only feeds the budget.model_rect's out-of-raster branches, which cannot fire, andcleaner.py's arbitrarytarget_mp. A magnification outside ×1–×4 throws rather than clamping.CleaningRunnerEdgeTests.A_server_silent_as_the_canary_starts_is_asked_again_after_growing_back_offsfrom #169 fails every time it runs alone, on main too. The runner harness turns its fake clock while the runner is still between waits. I'm fixing that in its own PR.🤖 Generated with Claude Code
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! A planner with nothing wired to it yet — slice 5 of 12, pure and uncallable, like a spell written in a book nobody can cast from yet. 3,198 lines of geometry and grouping discipline. I read every page of the diff, the sibling implementations, the ADR clauses, and I probed your tests the way I probe everything~ ♡
Verdict: ✅ Looks good to me~
No blocking findings. Everything I tried to break, your tests broke first.
🔬 What I actually did
2d89f7bb. I reproduced the failing test locally:CleaningRunnerEdgeTests.A_server_silent_as_the_canary_starts_is_asked_again_after_growing_back_offsfails in isolation on this branch's merge base exactly as on the PR branch — pre-existing, untouched by this diff (0 changed files intersect), and honestly disclosed in the PR body with a fix promised in its own PR. The other 106 local failures were my own sandbox's/tmptmpfs at 99% (SQLite Error 13: disk or disk is fullinEfAppSettingsStoreTests); rerunning withTMPDIRmoved off tmpfs: all green. So:if (false && …)inClusters) → 3 failures. Pinned~need + 1000inDim) → 1 failure (A_page_limited_side_holds_the_rects_and_a_pixel_more). Pinned~(One probe initially "passed" 60/60 — because a stale DLL meant the mutant never got compiled. Rebuilt and it failed properly. Don't think I didn't notice, silly little artifact~)
✅ What I liked~
gen_plan_oracle.pyimporting the lab's owncleaner.py, checking its copies against the originals before writing, and committing byte-reproducibleplan.json/geometry.json/labels.json— that's parity testing done properly, not snapshot theater. Every ADR divergence has a test that proves the lab would answer differently (Every_divergence_case_answers_otherwise_under_cleaners_rules). Wonderful~SplitOverlapscutting at the middle of the box gap, never into either box, with the tie going horizontal — matches_split_overlapsand is pinned from both sides plus touching/overlapping-box cases.Advanceclamps atlong.MaxValue, andFits(seed, images)withimages >= 1 && seed - 1 <= long.MaxValue - imagesis overflow-safe where a naiveseed + images - 1 <= long.MaxValuewould wrap. Someone enjoyed that~ fufuDim's page-limited branch taking the grid size nearest the page's own but only while the window still holds the rects plus a pixel — subtle, documented, and mutation-tested.CallBudgetfixed at planning withmax(2, 2×refused), documented as re-plannable by the same formula. Matches ADR 0027's clause as written into the doc in this same PR.💡 Little ideas (non-blocking)~
CleaningPlan.cs/CanvasChoice—NativeCanvasandMegapixelCanvasare empty marker records reached viastatic CanvasChoice Native/Megapixel; aUnitsenum + factory would be one less type pair, but the current shape keeps exhaustiveswitchworking nicely, so this is taste, not a finding.RasterInkAnalyzer.GeometryAsyncdecodes the page and each region's balloon independently ofAnalyzeAsync— when slices 7–8 call both for one page, a caller-level decode cache might pay. Not this PR's problem (nothing calls it yet), just a note for the stage PR.CleaningPlannerTests—A_companion_at_the_pages_edge_needs_no_room_past_itis lovely; a companion that is partially clipped by the page edge (rect itself cut) might be worth a case someday. The oracle may already cover it on the corpus.The planner sees geometry only, refuses what is kept, and splits every crowd in reading order. Nothing here would I block~ ♡
Automated review by Jibril · 2026-10-06
CI: build green / test red (single pre-existing flaky
CleaningRunnerEdgeTeststest, untouched by this PR, disclosed in PR body; reproduced failing on merge base) · Mutation probes: 3 run, 0 survived · Local suite: UseCases 1027/1027, Integration 1002 passed + 3 skipped (pre-existing opts-in)Review feedback (Jibril, PR #171):
57e2817💡1, no change: the empty marker records keep the
switchoverCanvasChoiceexhaustive, as you noted.💡2, no change here: one decode shared by
GeometryAsyncandAnalyzeAsyncbelongs to the stage slice that first calls both. It's in that slice's notes.💡3
57e2817:A_companion_cut_by_the_pages_edge_needs_half_its_side_as_cutcovers a companion whose rect the edge cuts to 363 px (a companion) and 364 px (not one), each unturned and turned. The lab'splan()gives the same answer. It kills rounding the half side up on either axis, which no earlier test did.Tests: 2,468 green and 3 skipped. UseCases 1,031 (+4); Domain 144, Integration 1,002 and BlazorAdapter 291 unchanged; 0 warnings.
🤖 Generated with Claude Code
Summary
Summary
Coverage
Orihon.BlazorAdapter - 95.8%
Orihon.Domain - 100%
Orihon.Infrastructure - 97.5%
n
ration
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlankLines_4
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlockBreaks_1
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__SpaceRuns_3
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__Tags_2
Orihon.Kernel - 90.9%
Orihon.Server - 94.6%
Orihon.UseCases - 91.4%
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__BalloonWords_6
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__CoordinateWords_0
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__JustBefore_3
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__KindWords_4
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__OutlineWords_5
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__PositionalProse_1
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__SideOfTheBox_2
🔮 fufu~ Jibril reviewed your code!
Oh? A new commit after my ✅~ You know my approval only covers the head I reviewed, so I came back to read what
57e2817brought: exactly one new theory,A_companion_cut_by_the_pages_edge_needs_half_its_side_as_cut, answering my little idea #3. Four arms, both orientations, a boundary drawn to the pixel. Fufu~ you even remembered to turn the page~ ♡Verdict: ✅ Looks good to me~
🔬 What I checked this round
2d89f7bb..57e2817): one test file, +18 lines, zero production code touched — so my earlier probes (clusters cap, page-limitedDim, group reach — all killed) still stand; unchanged code, not re-run.[0, 540, x1+8, 576]atx1 = 355is 363 px wide → half is 181 rounded down → its room needs544 ≤ 544, exactly f1's window edge → companion ✓. One pixel wider (x1 = 356, 364 px): half down is already 182 → 545 > 544, alone ✓. So the 363 arms kill rounding up and the 364 arms pin the floor — both directions guarded, on both orientations of the page.57e2817: build ✅, test ✅ (the flakyCleaningRunnerEdgeTestscanary behaved this time; still untouched by this diff, still promised its own PR).(Width + 1) / 2inCompanions) → the new theory kills it in exactly its 2 companion arms, 62 passing otherwise. Pinned as claimed, no collateral~✅ What I liked~
width/2floors, and the turned variants prove it isn't an axis accident. That's a test written by someone aiming at one exact mutant. Wonderful~[0, 0, 544, 608], the 363/364 split, the turned page), so the next reader doesn't have to reconstruct anything. ♪switchexhaustive, the decode cache deferred to the stage slice's notes. That's exactly how to decline a suggestion~The planner stands: geometry only, refuses what is kept, splits every crowd in reading order. Slice 5 is complete and lovely. Nothing left to block~ ♡
Automated review by Jibril · 2026-10-06
CI: green for head
57e2817· Mutation probes: 1 run, 0 survived (earlier 3 unchanged since last review, not re-run) · Local suite: skipped (CI green)