fix: try the scale step on the selected page before spending the ISO and the sheet (#1338) - #1340
Merged
Merged
Conversation
…r tests `GRM03` in tests/test_issue_1296, _1298 and _1299 pointed at /Users/paul/steps/GRM-03_thumbwheel_drive_screw_AP242_PMI.step, so four tests — including the one pinning the 841x594 / 10:1 outcome now reported as #1336 — ran on one machine and skipped in CI and for every other contributor. The file is copied in as tests/fixtures/grm03_thumbwheel_drive_screw_ap242_pmi.step; its sha256 is unchanged (4b6462b9...010a21), so the three modules' existing SHA guards still hold and their `skipif(not GRM03.exists())` markers are gone. Measured, not assumed: those four tests take 5.34 s, 5.23 s, 2.13 s and 0.51 s; the three modules plus the new one run 82 passed in 19.45 s in the fast tier. The new module asserts the fixture's own preconditions — the pinned sha, 18 PMI records (10 dimension + 8 manufacturing_requirement, part21 ids #2000..#2028), the #2004 internal thread whose "DIA 1.6 tapping drill" becomes the callout that drives #1336's escalation, and the single o1.6 dimension record. It also records that this fixture is NOT the plain grm03 fixture with annotations added: 28.7 mm / 16 faces against 23.5 mm / 14 faces, so the pair is not a pmi on/off comparison of one part. All four checks were mutated (repointed at the PMI-free fixture) and all four fail. Refs #1336 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Lp1DcM175cph7KE2fm5HF
…and the sheet The automatic ladder's only recovery order was "drop the optional ISO, then escalate the page", so an incomplete plan could settle on a candidate strictly worse than one it never reached. GRM-03 (28.7 x 10 x 10 mm) settled on 5:1/A3 WITHOUT its ISO while 5:1/A4 covers the same stations WITH it; the synthetic five-step profile behind #1299 did the same. The bounded larger-scale trial on the already-selected page — the one #1155 added for a conservative recovery detail — now also runs when the plan is incomplete, and runs FIRST. Both callers share one helper (`_try_larger_scales_on_selected_page`) rather than a second copy of the loop; the trial keeps its hard two-candidate budget, stays on the settled page and arrangement, and wins only through the same structural, required-outcome and (when the failure was axial) axial-coverage gates the larger sheet would have had to pass. Measured on the fixtures, pmi="off", against the lint set on main (ie. including #1322's `annotation_ink_overlap`): plain grm03 5:1/A4 no ISO -> 5:1/A4 with ISO, no error/warning issues at all grm03 AP242 PMI 5:1/A3 no ISO -> 5:1/A4 with ISO, same one code either way five-step (#1299) 5:1/A3 no ISO -> 5:1/A4 with ISO, all five step lengths explicit page=A4 incomplete 2:1 -> 5:1/A4, axial_length_missing gone "Same one code either way" is the honest form and is asserted as such: the AP242 fixture and the five-step profile each carry one pre-existing `annotation_ink_overlap` ('2' draws 2.2 mm of line-work through the label '0.5'), and it is identical on the A3 fallback this change avoids. The recovery neither causes nor cures it — it is the step-chain collision class tracked by #1331/#1333. No candidate carries an error, a dropped outcome, or a missing axial length. An explicit page pins the SHEET, not the scale, so the trial applies there too — it fills the sheet the caller chose instead of returning an incomplete layout on it. `pmi="annotate"` on GRM-03 is deliberately unchanged at 10:1/A1: the same-page trial is attempted and rejected (recovery_detail_retained, then structural_error), so this does not paper over #1336. Test expectations that moved, each verified as the better drawing before being rewritten (not adjusted to whatever the code now prints): - #443's two GRM-03 tests: the truthful step lengths — its actual requirement — still hold; what the replan spends to get them changed, so `"iso" not in views` became `"iso" in`. - #1299's six ladder tests: the real-part ones now assert same-page recovery; the fakes gain the two rejected same-page trials, and one fake needed a (297, 210) page mapping. - #1308's golden bbox dict gains `note_iso_nts`; all 16 other boxes are byte-identical. - docs/multi-feature-object-reference-workflow.md no longer quotes the four `authored_views` lines: with the automatic plan intact the emitter authors no view block at all. New tests assert the precondition (2:1/A4 really is axially incomplete, so the recovery is exercised), that relaxing the named mechanism changes the outcome (with `_AUTOMATIC_UPSCALE_TRIAL_LIMIT` monkeypatched to 0 the same part falls back to the pre-#1338 A3-without-ISO result), and that the recovery introduces no new lint code — the avoided sheet's code list is compared against the recovered drawing's. Full fast tier on this rebase: 4872 passed, 5 skipped, 2 xfailed in 99.06s. `scripts/pr-check --static` exits 0. Known limits, stated rather than hidden: the recovery branch is still gated on the plan having an optional ISO, so a plan without one gets no replan at all — pre-existing, and widening it is a separate decision with no evidence in hand. And each accepted upscale is built through the explicit scale+page path, whose conservative `_fits` predictor logs "Requested scale 5 on (297.0, 210.0) page may not fit the 4-view layout" at WARNING on a drawing lint then measures clean — pre-existing for #1155's trials, now more common. Fixes #1338. Refs #1336, #1155, #1299, #443, #1333. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Lp1DcM175cph7KE2fm5HF
pzfreo
force-pushed
the
fix/issue-1338-scale-before-page-escalation
branch
from
August 25, 2026 11:00
79cada5 to
66555f2
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1340 +/- ##
==========================================
+ Coverage 93.59% 93.67% +0.08%
==========================================
Files 61 62 +1
Lines 20803 21274 +471
Branches 3972 4083 +111
==========================================
+ Hits 19470 19928 +458
+ Misses 774 767 -7
- Partials 559 579 +20 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1338. Two commits: the fixture the tests need, then the fix.
The defect
The automatic ladder's only recovery order was drop the optional ISO, then escalate the
page, so an incomplete plan could settle on a candidate strictly worse than one it never
reached. GRM-03 (28.7 × 10 × 10 mm) settled on 5:1/A3 without its ISO while
5:1/A4 is clean with it — same scale, smaller sheet, pictorial view retained.
Raising the drawing scale is how the ladder buys annotation room, and text height is fixed
in page mm — so every step up it shrinks the text relative to the part. Spending a sheet
size and an optional view before trying the cheaper move on the sheet already chosen was
the wrong order.
The fix
The bounded larger-scale trial on the already-selected page — the one #1155 added for a
conservative recovery detail — now also runs when the plan is incomplete, and runs first.
Both callers share one helper (
_try_larger_scales_on_selected_page) instead of a secondcopy of the loop.
Unchanged from #1155's contract: the hard two-candidate budget, confinement to the settled
page and arrangement, compilation through the ordinary pipeline, and a win only by passing
the same structural, required-outcome and (when the failure was axial) axial-coverage gates
the larger sheet would have had to pass. Every attempt, rejection and winner is still
recorded in the structured scale decision.
Measured
pmi="off", on the fixtures:grm03fixturegrm03AP242+PMIpage="A4"axial_length_missinggone"Same one code either way" is the honest form, and CI is why this line reads as it does — see
below. The AP242 fixture and the five-step profile each carry one pre-existing
annotation_ink_overlap('2' draws 2.2 mm of line-work through the label '0.5'), and it isidentical on the A3 fallback this change avoids. The recovery neither causes nor cures it —
it is the step-chain collision class tracked by #1331 / #1333. No candidate carries an error,
a dropped outcome, or a missing axial length.
An explicit page pins the sheet, not the scale, so the trial applies there too: it fills
the sheet the caller chose instead of returning an incomplete layout on it.
GRM-03 with
pmi="annotate"is deliberately unchanged at 10:1/A1. The same-page trial isattempted and rejected (
recovery_detail_retained, thenstructural_error), so this does notpaper over #1336 — that issue is about what a single dropped callout may buy, and stays open.
Test expectations that moved
Ten tests failed on the first full run. Each new outcome was verified as the better drawing
before the expectation was rewritten — none was adjusted to whatever the code now prints:
hold; what the replan spends to get them changed, so
"iso" not in viewsbecame"iso" in.the two rejected same-page trials, and one needed a
(297, 210)page mapping. Pageescalation itself stays covered by the fake bounded-recovery test and by Lower AP242 thread, tapped-hole and knurl requirements into typed drawing intent #1298's GRM-03
pmi="annotate"acceptance.note_iso_nts; the other 16 boxes are byte-identical.docs/multi-feature-object-reference-workflow.md— no longer quotes the fourauthored_viewslines: with the automatic plan intact the emitter authors no view block.New tests assert the precondition (2:1/A4 genuinely is axially incomplete, so the recovery
is exercised) and that relaxing the named mechanism changes the outcome — with
_AUTOMATIC_UPSCALE_TRIAL_LIMITmonkeypatched to 0 the same part falls back to the pre-#1338A3-without-ISO result.
The first CI run failed — what it was
The first push failed one test on all five Linux legs: my own
test_automatic_recovers_on_a4_at_a_larger_scale_instead_of_escalating_the_sheet, on anannotation_ink_overlapwarning that does not exist in this repo at the commit I branchedfrom. It was not platform divergence, and not a defect in the fix.
mainhad advanced by two commits — #1322 (Lint the line-work drawn through a label…) and#1335 — and GitHub tests the merge, so CI was running a lint rule my base didn't have. The
branch is now rebased onto those commits, and the drawing was measured against the new rule
both ways: the A4 recovery and the A3 fallback report the same single code, so the
overlap is pre-existing and sheet-independent.
The test asserted "no warnings at all", which was never what #1338 is about. It now asserts
the requirement — no errors, nothing dropped, no missing axial length — plus a direct
comparison of the recovered drawing's lint codes against the avoided sheet's, which is the
assertion that would actually catch the recovery making a drawing worse.
Gates
(
-n auto --dist worksteal).scripts/pr-check --staticexits 0 (exit code read, not output grepped).Architecture
ADR 0004 owns outer scale/page selection, so it carries a new amendment (2026-08-25) recording
the order and its constraints, alongside the 2026-08-23 amendment this generalises. The
amendment also records what it does not settle: scale remains the ladder's only lever for a
placement shortage, and bounding what one dropped annotation may buy is #1336.
The fixture commit
GRM03intest_issue_1296/_1298/_1299pointed at an absolute path on one developer'sdisk, so four tests — including the one pinning #1336's 841×594/10:1 outcome — skipped in CI
and for every other contributor. The file is now
tests/fixtures/grm03_thumbwheel_drive_screw_ap242_pmi.step(identical sha256, so the existingSHA guards still hold) and the
skipifmarkers are gone. A new module asserts the fixture's ownpreconditions — 18 PMI records, the
#2004internal thread behind #1336's escalating callout,and that this is a different solid from the plain grm03 fixture (28.7 mm / 16 faces vs
23.5 mm / 14 faces), so the pair is never mistaken for a pmi on/off comparison of one part. All
four checks were mutated and all four fail. The four un-skipped tests cost 5.34 s, 5.23 s,
2.13 s and 0.51 s.
Known limits, stated rather than hidden
gets no replan at all. Pre-existing; widening it is a separate decision with no evidence in
hand.
_fitspredictor logsRequested scale 5 on (297.0, 210.0) page may not fit the 4-view layoutat WARNING on a drawing lint then measures clean. Pre-existing for GRM-04 auto layout leaves most of A4 unused while dropping the Ø2.4 callout #1155's trials(that path also ships candidates built this way), but more common now. Demoting a predictor
that the measurement contradicts would mean threading a flag through builder → analysis →
compose and changing a documented contract — maintainer's call.
🤖 Generated with Claude Code
https://claude.ai/code/session_015Lp1DcM175cph7KE2fm5HF