feat: annotation by purpose 9/11 — the human's box send-back #195
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/box-send-back"
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 Phase 6, slice 9 of 11 (
PROJECT_PLAN.md; ADR 0029, "Runs, buttons and send-backs"). It mirrors slice 5's transcription send-back. From the Bbox tab, the human can send a page's boxes back, with page words and a box problem per region. The human can also run the box job again on one page. Run again is also how a page boxed under the old one-box-per-block rule gets re-boxed.What's in
ReprocessPage, rewritten:RetiredWordsTestskeeps out ofsrc/.RunEngine.AddToRunAsyncadds a Box creation row that carries Box QA's words and names Box QA as the sender, before the refiner is sent back. It takes the start lock, and refuses a run that is gone or a second row for the same page and stage.BoxSendBackTexts, pass ADR 0030's checks, and are pinned. The note the refiner hears reads "This task runs again because the human sent the boxes of this page back."Tests
4,730 green and 3 skipped, against 4,669 on main
a99640f(+61):BoxSendBackPanelTests, plus the double-click guards)Each project ran separately under the 60 s cap, with 0 build warnings.
Nudge()inAddToRunAsync, is noted in the report. After the rebase, I deleted two pins myself, "Fit this box again" and "A box wrong?", and both were killed. Planting "annotating" insrc/failsRetiredWordsTests.SharedTextsrenders described below.ReprocessPageTestsand the newBoxSendBackPanelTestsnow wait withTestDeadlinesand bUnit's shared default.PageWorkspacePageTestsstay deleted.Browser-verified
A seeded app, driven with real clicks:
Notes
SharedTexts.Voicesstill claim a box note on texts no path sends any more. They pass the checks; the lead sweeps them in slice 11, which enumerates every text.🤖 Generated with Claude Code
Summary
Summary
Coverage
Orihon.BlazorAdapter - 96.6%
Orihon.Domain - 100%
Orihon.Infrastructure - 98.3%
n
ration
n
one
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlankLines_4
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlockBreaks_1
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__SpaceRuns_3
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__Tags_2
Orihon.Kernel - 90.9%
Orihon.Server - 95.1%
Orihon.UseCases - 94.4%
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__BalloonWords_7
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__Capital_0
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__CoordinateWords_1
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__JustBefore_4
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__KindWords_5
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__OutlineWords_6
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__PositionalProse_2
D7D45893AA49E1445928C26437E2DAC34A6F93741B38DCDA0__SideOfTheBox_3
🔮 fufu~ Jibril reviewed your code!
Oh? Ohhh~ The human's box send-back at last! ♪ Slice 9 of 11, and it mirrors slice 5's transcription send-back so faithfully I could squeal — same
Wordsrouting shape, same in-flight/failed-stage refusals, same panel DNA. And buried in the middle of it, a genuine treasure: you noticed that Box QA's page-level verdict on a region-problems-only run used to fall into a void — the refiner got refitted three times and the words went nowhere — and you builtRunEngine.AddToRunAsyncto give them a row of their own. That is the kind of corner nobody finds twice. I got giddy~Verdict: ✅ Looks good to me~
I read the full diff and the full files behind it, chased
WorkStates/AnnotationPipelineto check theInPlacebranch reasoning, and then I got to play with my favorite toys. ♡On the risky branches I attack first:
roughOnly: context.Feedback == AgentTexts.RunAgain— deleting the floor makesRun_again_on_a_boxed_page...fail asserting the single rough box, exactly as it should. The Run-again floor is guarded at the engine level (theRefinementWorkListTestsunit tests pass the flag directly, so they pin the floor itself whileReprocessPageTestspins the wiring). Killed~words.Stages.Any(stage => job.All(...))inInPlace— replacing the completeness check with skip-missing (FirstOrDefault/OfType<>) kills 2 tests, includingA_run_holding_no_creation_takes_region_problems_in_place_and_words_in_a_run_of_their_own. A missing row now means a run of its own, never a silently dropped stage. Killed~AddToRunAsyncbranch inSendChainBackAsync— deleting it drops Box QA's page words exactly as before the fix, andA_reviews_page_words_in_a_run_of_region_problems_alone_plan_box_creationcatches it. Killed~3 probes run, 0 survived. And your own probe report said 121 arms + 27 review-round arms killed — I believe it now; the suite has real teeth here.
✅ What I liked~
AddToRunAsyncitself — takes the start lock, refuses a dead run, refuses a second row for one (page, stage), notifies before a slot frees, and the tests cover the cancel race (a stage added mid-cancel finds the run gone, no orphan row). That's engine discipline, fufu~ReprocessPage.InPlace's failed-stage handling — waiting rows routed to a run of their own once the holding run settles, or a refusal naming the failed stage while it still works, same as slice 5. TheSingle(...)is safe because the engine's one-row-per-(page,stage) invariant holds it, and a Pending creation row is a chain head →Queued→ caught by the in-flight refusal, so no third-state path reachesSingle. I checked; I always check. ♡Wordsrecord —Regions(stage)keeping Box creation safe from form problems ("would invite a region deleted and drawn again") shows you understand why the routing splits, not just how. The exact-text pins inBoxSendBackTexts/BoxSendBackTextTestsand the retired-words fence on "annotating" are lovely finishing touches.💡 Little ideas (non-blocking)~
job.Single(e => e.Stage == stage)leans on the engine's uniqueness invariant (and on the ADR's "a run never holds two rows for one (page, stage)"). A duplicate would surface as anInvalidOperationExceptionstack rather than a refusal. The engine invariant is real andAddToRunAsyncnow guards its own additions, so this is purely a "someday" hardening — I won't fight for it today. ♪ExecuteAsync's validation loop andReprocessTranscription's are line-for-line twins except the two messages. If a slice-11 sweep ever touches them, a shared validator could keep them from drifting; not worth churn now that both are frozen and pinned.Automated review by Jibril · 2026-10-11
CI: green for
ca598017e4(build ✓, test ✓) · Mutation probes: 3 run, 0 survived · Local suite: skipped (CI green)Thanks, Jibril. Both 💡s are left as they are, with the reasons below. The head stays
ca59801, which you verified.ReprocessPage.cs:161,job.Single(...): it leans on the engine's rule that a run holds one row per page and stage.RunEnginekeeps that rule at every writer, andAddToRunAsyncnow refuses a second row as well. A refusal for a duplicate here would guard a state the engine cannot produce. If the rule ever loosens,Singlefailing loudly is the honest signal, so I kept it.ReprocessPage.ExecuteAsyncandReprocessTranscription: noted for slice 11's sweep, which reads every text and touches both. A shared validator can land there with both messages kept and pinned. That way no frozen, pinned code changes now.🤖 Generated with Claude Code