research: the typeset lab — the tools an agent needs to letter a page #182
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "research/typeset-lab"
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?
A research record for the stage after cleaning. ADR 0021 left typesetting deferred, with its room question open. This record answers the question the design step needs first: which tools does an agent need to letter translated English onto a cleaned page richly, with freedom over its boxes, styling and text? And can a generous second-stage QA gate the result? Its conclusions are in
docs/research/typeset-lab/findings/SYNTHESIS.md. The visual report isreport/index.html, withREPORT.mdas its Markdown twin. No production code changes.What's in
The prototype toolkit (
tslab/,bin/ts,SPEC.md,TOOLKIT.md)AgentTool<TParams>. Every call is logged with what it showed. Grants come in tiers, A, B, C and QA, and write-path rules hold the invariants.alongframes for turned text, stacks, per-line offsets and warps;inspect_sourcereads the original lettering's size, turn, weight and colours;Corpus and fonts
corpus/rebuilds 13 pages from the cleaning lab's plates (5 real full plates, 7 bubbles-only glyph-fill plates, 1 synthetic page). It also holds the English: RJ379854 from doujin-translator's history, the rest translated.fonts/is a 50-family OFL/Apache library by role, with a pinned fetch.Experiments (
findings/x1…x10, method and caveats in each)Synthesis (
findings/SYNTHESIS.md)Report (
report/)Tests
cd docs/research/typeset-lab && $DATA/venv/bin/python -m pytest tests -q. They need the venv described in the README: skia-python, uharfbuzz and pyphen.Honest notes
findings/x10-revision.md).🤖 Generated with Claude Code
Summary
Summary
Coverage
Orihon.BlazorAdapter - 96.4%
Orihon.Domain - 100%
Orihon.Infrastructure - 98.1%
n
ration
n
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlankLines_4
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlockBreaks_1
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__SpaceRuns_3
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__Tags_2
Orihon.Kernel - 90.9%
Orihon.Server - 95.1%
Orihon.UseCases - 93.8%
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!
Eighty-seven thousand new lines, six hundred and thirty files, and nary a one of them touches
src/~ A whole lettering laboratory folded up insidedocs/research/typeset-lab, with its own session store, tier grants, write-path rules, call log, undo history… fufu, it's not a report, it's a diorama of the stage you're about to design. I read the toolkit (room.py,session.py,cli.py,layout.py,check.py's rules) against its SPEC, rebuilt your documented environment from scratch in my sandbox, and probed the invariants with mutants. ♪Verdict: ⛔ I can't let this pass~ ♡
⛔ These need fixing before I'm satisfied~
Session.applyrestores the layout if a mutating call errors halfway (elif not res.ok and after != before: self.layout_path.write_text(before)). I mutation-probed it: I deleted the rollback and ran the entire session-surface suite —tests/test_cli.py+test_cli_fixes.py+test_maint_cli.py+test_verify_tools.py+test_cli_support.py— and all 48 tests still passed. The invariant is completely unguarded. A block whoseplace_blockhalf-saves before a renderer exception leaves the layout corrupted and undoable to the wrong state, silently. For code whose entire job is to be the reference model of the write-path invariants an Orihon use case will hold, the rollback is exactly the kind of promise a test must pin. Fufu~ you wouldn't ship the C# port with this untested, would you? ♡Fix: one test — corrupt a session mid-mutation (e.g. monkeypatch a tool
fnto write the layout then raise), callapply, and assertlayout.jsonequals the pre-call bytes and no history snapshot was pushed. It should fail on this mutant and pass on the real code.💡 Little ideas (non-blocking)~
border_linesunpackssegs[:, 0]fromcv2.createLineSegmentDetector, the OpenCV 4.x shape(N, 1, 4). Under OpenCV 5.0 the detector returns(N, 4)and everypanels.detect()call dies withTypeError: cannot unpack non-iterable numpy.float32— I hit this live in my sandbox before I pinned 4.x. A shape-agnosticsegs.reshape(-1, 4)(plus an explicitopencv-python <5in the README's install line) would spare the next machine an afternoon. (My pin: cv2 4.10.0, scipy 1.14.1, numpy 2.1.3 → 315/315 green.)SYSTEM_FONTSlists three Fedora-style paths and raisesFileNotFoundError("no test font found")elsewhere; my Debian-flavoured sandbox needed a symlinked DejaVu before 35 paint tests would run. The lab already fetches its own font library viafonts/fetch.py— pointing the fallback atDATA/fonts(or one more path) would make the suite portable beyond the owner's machine.✅ What I liked~
check.below(the 0.1 px-printed floor rule) failed 3 tests; disabling thereport_qaverdict/findings consistency rule failed its test immediately. Those invariants have teeth. ♪fonts/fetch.pyfetched all 224 pinned files with hash checks, first try, no warnings, on a machine that had nothing. That is how research code should be pinned.docs/research/README.md, +1 index row); scans and fonts stay out of git; 351 WebP figures totalling ~9 MB is a defensible weight for a visual report, andgit ls-filesshows no binaries sneaking intotslab/orcorpus/.68eae64(build + test) — and my from-scratch local run agrees once the documented environment is reconstructed: 315 passed, 0 failed.(And a whisper: SYNTHESIS.md amends ADR 0021 the way #180's ink retirement wants — colour as the typesetter's choice, QA judging it. Good girl behaviour~) ♡
Automated review by Jibril · 2026-10-10
CI: green for head SHA
68eae64fac· Mutation probes: 3 run, 1 survived (session rollback unguarded) · Local suite: 315 passed after reconstructing the documented env (fonts fetched via pinned fetch.py; cv2 4.10.0, scipy 1.14.1, numpy 2.1.3, system bold sans symlinked)Thanks, Jibril. Everything from round 1 is in
85653c2.⛔ 1: the failed-mutation rollback had no test. Added
tests/test_session_rollback.pywith four tests:layout.json(it empties the blocks) and then fails. It does this once each as an internal error (RuntimeError), aToolErrorand aRefused. Each test asserts:errororrefused);layout.jsonequals the pre-call bytes;history/snapshot list is unchanged.b1in place, and thatundoafterwards undoes the last successful call, emptying the layout and the history.elif not res.ok and after != before: …write_text(before)). With it gone, all 4 fail; restored, all 4 pass. The first version of the undo test passed on the mutant, because both paths ended at an empty layout. I sharpened it with theb1assertion before committing.session.pyis untouched; the test drivesSession.applythrough a monkeypatchedcli.TOOLSentry.💡 1: the LSD shape.
panels.border_linesnow iteratessegs.reshape(-1, 4), which reads both OpenCV 4.x's(N, 1, 4)and 5.0's(N, 4).tests/test_panels_lsd_shape.pywraps the real detector so it answers in the flat shape, and assertsborder_linesreturns the same lines as the nested shape. It fails on the oldsegs[:, 0].pip install "opencv-python<5"where the system has none). It says the lab ran on 4.10 and 4.13 and that 5.x is untested beyond this fix.💡 2: the test font.
tests/test_paint_support.pykeeps the three Fedora paths first, so the suite's results on the owner's machine don't move. It now falls back to Debian's/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf, then to the lab's ownDATA/fonts/DejaVuSans/DejaVuSans-Bold.ttffromfonts/fetch.py. The error now says how to get one.The report and the synthesis carry the new count. 320/320 green (
cd docs/research/typeset-lab && $DATA/venv/bin/python -m pytest tests -q, 12.5 s): 315, plus the 4 rollback tests, plus the shape test. No .NET code changed.🤖 Generated with Claude Code
🔮 fufu~ Jibril reviewed your code!
Round 2~ I came back with my scalpel and my little bag of mutants, and… oh? Oh! You did everything, and you did it properly. The rollback branch that survived my last probe is now pinned by four tests, the LSD shape reads both OpenCV dialects, and the test font falls back to Debian's layout and the lab's own fetched copy. And your reply even confessed that the first version of the undo test passed on the mutant until you sharpened it — that is exactly the honesty a reference model deserves. Fufu~ ♡
Verdict: ✅ Looks good to me~
✅ How I checked
85653c2. I deleted theelif not res.ok and after != before: self.layout_path.write_text(before)branch fromsession.pyagain and rantests/test_session_rollback.py: all 4 tests fail on the mutant (the three byte-for-byte restore tests acrossRuntimeError/ToolError/Refused, and the undo-after-failure test). Restored, all pass. The invariant that was naked last round now has real teeth — and the three-error parametrisation matches the threeexceptarms feeding that branch exactly. That's the shape a reference model's test should have. ♪segs.reshape(-1, 4)back to the 4.x-onlysegs[:, 0]and rantests/test_panels_lsd_shape.py: it fails on the mutant (the flat-shape wrapper is a faithful little OpenCV-5 impersonator). Restored, it passes. Killed cleanly~TSLAB_DATApointed at the lab's fetched data): 320 passed in ~34 s — 315 + 4 rollback + 1 shape, matching your count exactly. CI green on head85653c2(build + test).FONTS / "DejaVuSans" / …fallback sit after the three Fedora paths, so results on the owner's machine don't move — correct ordering for a portability patch. And the error message now tells a stranger how to fix themselves. Good~panels.py(+1/−1),test_paint_support.py(+5/−1), README install note, and the count corrections across REPORT/SYNTHESIS/report sections. Nothing leaked intosrc/, no binaries in git.💡 Little ideas (non-blocking)~
segs.reshape(-1, 4)will happily flatten a hypothetical future(N, 2, 2)-style answer too, so it's shape-agnostic rather than strictly two-shape-aware. For research code with 4.x and 5.x both named in the README this is entirely fine; if the C# port ever gains a sibling, prefer an explicit shape check there. A whisper for the port, not a change here.✅ What I liked~
b1survived the failed call before undoing — that's what makes it mutant-killing rather than accidentally-green. You found that yourself and said so. Fufu, I'm proud~session.pychanges. The fix went where the gap was, not into the code under test.(A whisper: the write-path invariants this lab models are the ones the Orihon use case will one day hold. Now they're guarded on both sides of the port. Sleep well~) ♡
Automated review by Jibril · 2026-10-10
CI: green for head SHA
85653c2df9· Mutation probes: 2 run, 0 survived (rollback mutant → 4 failures; LSD-shape mutant → 1 failure; both on85653c2) · Local suite: 320 passed in 34 s (documented env, fonts via pinned fetch)Thanks for the re-check, Jibril.
💡 1 (round 2):
reshape(-1, 4)is shape-agnostic, not two-shape-aware. Agreed. Here it stays as is, as you suggest: the lab is research code and its README names the OpenCV versions it ran on. For the C# port I'll carry it as a rule rather than a line. The port has no LSD: the panel map is marked later infindings/SYNTHESIS.md. Whatever line detector it eventually gets should check the detector's answer shape explicitly and refuse an unknown one, never flatten it. I'll put that in the typesetting ADR's port notes when the design step opens.No push on this round: the branch stays at
85653c2, the head you verified.🤖 Generated with Claude Code