fix(showcase): hay-bale quality pass with five new hygiene budgets, and exit-pre-sidecar doc accuracy - #205
Merged
Merged
Conversation
…gets The inspection sheet found nine defects that every shipped budget passed. The belts were constant rectangles hung on a rounded, bulged loaf, so they stood proud at the middle of each face, were swallowed at the corners, and stopped 2.9-8.3 mm above the floor with a visible notch -- while the AABB zmin gate stayed green because the loaf grounded the box. The two belts were 5.4 mm out of step because the loaf's ridge term was odd in x. Nothing cinched: the waist measured 0.1 mm. All 426 faces were smooth-shaded, which erased every ridge and nap and left a bar of soap. Fixed structurally, not by nudging: - Build, bevel and ground the loaf FIRST, then raycast its finished cross-section per belt station and follow that profile at a named bite. Moving a belt now moves its seat with it. - Every closed-form shaping term is even in x (cos, not sin), so the two stations sample identical surface heights. - The cinch is derived from BELT_XS, and the ridge fades out under each belt because twine compresses straw flat where it bites. - Shading decided per part: flat loaf, smooth cord. - Straw detail moved to the high mesh, where the bake stage already exists to carry it onto the low mesh. Five budgets so none of it can come back, all recomputed from the generated mesh: cross-shell coplanar z-fight pairs (15), per-belt wrap zmin (16), shell count (17), banded per-station belt seat depth (18), belt mirror symmetry and cinch depth (19). Four new falsifiers, each landing on the budget it targets, proven on 4.5.11, 5.1.2 and 5.2.1: --stray-vert 15, --float-belts 16, --slack-belt 18, --skew-belt 19. --stray-vert parks its vertex inside the bale because above it the bbox budget fired first; --skew-belt shifts along X because a Z skew tripped the z-fight budget instead. Also deletes the temp glTF once its size is measured: Blender's TMPDIR resolves to the working directory on a portable build, so every run left a .glb in the repo root. Geometry is identical on all three binaries. The piece still reads as a wrapped parcel rather than straw at hero scale; that is recorded in the README as a material and bake problem, not a geometry one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
…e falsifiers The module docstring told readers the catalog's min_version was 5.0 and wrong. It has been 5.1 since the example's introducing commit, so the note was never true and sent anyone who read it to "fix" a correct catalog row. Re-verified the version claim against all three binaries: hasattr(bpy.app.handlers, "exit_pre") is False on 4.5.11 LTS and True on 5.1.2 and 5.2.1 LTS, and exit_pre is the only exit- or quit-related name in bpy.app.handlers on the versions that have it -- so there is no older spelling to fall back to. The documented behaviour is correct. README also now: - states the contract and why it matters (atexit is the bug class this catches), with the 4.5 and 5.2 bpy.app.handlers API links - explains that exiting 77 on 4.5 is correct by design, and what stops a skip from spreading: min_version 5.1, the runner failing a skip above the floor, and summarize.py reddening an all-skip job - enumerates the falsifiers in a table with what each breaks, its script exit and how the harness fails, and names --write-in-main and --atexit-instead as the two an existence-only check would pass, which is why the catalog row carries sidecar_contains Contract, assertions and exit codes unchanged. Verified before and after on 4.5.11, 5.1.2 and 5.2.1: default SKIP/PASS/PASS, all five harness falsifiers FAIL, --force-run exits 2 on 4.5, plain default exits 77. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
…tions Three conventions every future piece inherits, each from a defect the hay-bale pass found that no existing budget could see. Even shaping terms and mirror symmetry (19): where a prop has mirrored members, every closed-form term in the host's shaping function must be even in the mirrored axis. hay-bale used sin(x*24), which is odd, so its two belt stations sampled different surface heights and the wraps shipped 5.4 mm out of step. Pair the mirrored shells and compare. Wrappers follow the host's profile, and the host is finished first (16/18): build, bevel and ground the host, then hang the wrapper on the finished surface by raycasting the host's own cross-section. A constant rectangle on a rounded host stands proud at the middle of each face and is swallowed at the corners. Assert per wrapper shell that the loop passes under the host, because the AABB zmin gate stays green when the host grounds the box. Seat depth is measured station-locally, along the vertex's own direction from the station axis, never by nearest-surface distance: a wrapper in a concave waist has its nearest host face on the bulge shoulder at a neighbouring station. Bin by rounding to the nearest station, never by flooring, or a station on a bin boundary splits its inner and outer rings and the inner-only bin reports the outer offset as the seat depth. Also sharpens the z-fight budget: count coplanar pairs cross-shell, not merely over faces sharing no vertex. Two quads two steps apart on one flat cap share no vertex and are coplanar by construction -- that read 96 pairs on hay-bale, none of them a hazard, which is the same unsatisfiable-budget trap the surrounding paragraph already warns about. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
TMHSDigital
added a commit
that referenced
this pull request
Sep 22, 2026
…iene budgets (#206) * fix(showcase): hoop the cart's tyre onto the felloe and round the rim The tyre's inner radius was set equal to the felloe's outer radius (`r_mid = RIM_OUTER + TYRE_T/2, radial_t = TYRE_T/2`), which put both surfaces on one plane for every segment: 32 coplanar cross-shell face pairs, 16 per wheel. It was visible as speckle on the committed hero and in a clay pass, and no budget could see it. Derive the tyre's inner radius from the felloe's outer radius minus a named interference (TYRE_SEAT) and its tread from the felloe plus its own thickness, so the band bites into the rim and still owns the ground contact. Wheel segments 16 -> 24: at 16 the rim read as a polygon and the facet facing the key light rendered as a hard white plate. Iron raised from (0.13, rough 0.32) to (0.20, rough 0.50), inside the range its sibling pieces use, because the tyre was reading as black plastic. Four new budgets, each recomputed from the generated mesh: - coplanar cross-shell face pairs = 0 (hay-bale's combinatorics, copied) - each tyre's own zmin on the floor, not just the AABB's - tyre seat depth banded per angular station, 48 stations - the two wheels mirrored in Y within 5e-5 m Four new falsifiers, each landing on its own code and sized to stay inside BBOX_TOL so the AABB gate cannot steal the failure: --flush-tyre (15), --sink-tyre (18), --float-wheel (16), --skew-wheel (19). Also drops the temp .glb once measured — the leak PR #205 found in hay-bale is in this piece too. Base triangles re-fitted to 2760-2960: narrower than the 2470-2900 it replaces and centred on the measured 2856. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> * fix(showcase): put the well's posts under the roof corners and tenon them in Three structural defects, all found by looking at an inspection sheet: 1. The four roof posts stood at 0/90/180/270 degrees while the roof's 4-gon cone is rotated pi/4, so its corners sit on the diagonals. The posts were under the midpoint of each eave: the roof's corners cantilevered 0.80 m unsupported, and one post stood dead centre in the well mouth in every orthographic view. Posts moved to the diagonals, and the windlass, crank and grip turn with them. 2. Each post's bottom face landed exactly on the coping's top face — 4 coplanar cross-shell face pairs, one per post. Posts are now tenoned POST_SEAT into the coping, with POST_TOP held fixed so nothing above them moves. 3. EAVE_HALF was derived from the post ring plus an overhang, giving a 1.64 m roof over a 1.08 m drum: an umbrella, not a well house. It is now derived from what the roof must cover — the curb's own radius plus a named clearance — which brings the AABB to 1.312 m. The bucket hung at z=0.68 with only its rim level with the coping: invisible in the hero and in all six orthographic views. It now hangs BUCKET_CLEAR above the measured coping top and reads in the still. Five new budgets, each recomputed from the generated mesh — the tenon and the bucket clearance measure against the coping top read off the stone material, not against the constant the builder used: - coplanar cross-shell face pairs = 0 - all 12 bottom-course stones on the floor, not just the lowest - post tenon depth banded, per post - each post within 0.02 rad in plan of a roof hip corner - bucket clearance banded above the coping Five new falsifiers. Two needed re-aiming to hit their own budget: --stand-posts also returns the posts to the axis, because flush alone at 45 degrees leaves no coping face within COPLANAR_CENTRE_MAX of a foot; --turn-posts rotates by exactly one curb facet, because any other angle sets the feet on different coping geometry and the gap gate (17) steals the failure, and a full 45 degrees swings the crank past the eave and fails the AABB gate (8). Collider ceiling 320 -> 380: the convex hull of the resized roof measures 346. Also drops the temp .glb once measured. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> * fix(examples): make ngon-triangulate's falsifier actually falsify `--skip-triangulate` printed an error and returned 4 from its own branch. The flag announced a failure rather than causing one, and the handling assertions it exists to falsify never ran. It now skips the repair and lets those assertions catch the unrepaired mesh, which reports the real measurement (`ngons=1 tris=0 quads=4`). The contract, the assertions and the exit codes are unchanged. Proven identical before and after on 4.5.11 LTS, 5.1.2 and 5.2.1 LTS: default 0, --no-dissolve 3, --skip-triangulate 4. README: adds the verified `calc_tangents` abort text, which is byte-identical on all three binaries, a falsifier table naming what each flag breaks, and 4.5/5.2 API links for calc_tangents, dissolve_edges and triangulate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> * docs(showcase): fold this pass's defects into the shared conventions Seven conventions every future piece inherits, each from a defect in this pass that no existing budget could see: a band is hooped onto its host and never set flush against it; a member is tenoned into its seat and never stood on it, measured against the host read off the mesh; posts go under a roof's corners, with the corners taken from the pooled eave vertices rather than one fascia board; a roof is sized from what it covers, not from what carries it; the subject hangs where it can be seen; mirrored assemblies pair and compare, with the falsifier sized inside BBOX_TOL; and segment counts are a silhouette budget. Also registers this pass's falsifier flags in the shared list and regenerates the gallery for the two rebuilt pieces. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> --------- Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 22, 2026
TMHSDigital
added a commit
that referenced
this pull request
Sep 23, 2026
…sifier-table check (#210) All three READMEs gained falsifier tables in #205 and #206, and the static checker already parses them (28 pieces, 181 falsifiers), but they stayed listed in KNOWN_UNDOCUMENTED. That exemption is now a hole: if one of those tables were deleted, the checker would skip the piece silently instead of failing it. Every falsifier was run, not read: the runtime mode reports 19/19 exits matching their declared budgets on Blender 5.2.1, and a direct sweep gives the same 19 codes on 4.5.11. Closes #203 Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
Quality pass over two items: one showcase piece (Track A) and one
pathology example (Track C).
Binaries — all local, exact paths and self-reported versions:
.scratch/blender-4.5.11-windows-x64/blender.exe→ Blender 4.5.11 LTS ·.scratch/blender-5.1.2-windows-x64/blender.exe→ Blender 5.1.2 ·.scratch/5.2/blender-5.2.1-windows-x64/blender.exe→ Blender 5.2.1 LTS1.
showcase/hay-bale— Track ANever had a visual quality pass (last touched by #170, a rebuild).
Inspection sheet: 18 renders at 1200 px in
.scratch/qp2/hay-bale-r1/(six orthos, two three-quarters, clay, wireframe overlay, seven joint
close-ups, ground contact), all opened and looked at. Four rounds.
Defects found, and the view that revealed each
ortho_bottom(bottom face bare, only two stub ends),ground_contact(belt terminates in mid-air), shell probezmin=+0.00832/+0.00294vs loaf0.00000sin(x*24)is odd in x, so the stations sampled different surface heightsortho_top(loop nubs at different heights)ortho_front,ortho_top, cinch profile probeclay_three_quarter,three_quarter_aortho_leftortho_top,wire_three_quarterjoint_belt0_sideif v.co.z < 0: v.co.z = 0ortho_bottomshading gradientFixes, and why each is structural
belt raycasts the finished cross-section at its own station
(
loaf_profile) and follows it at a named bite. Placing belts againsthalf-built geometry and shifting everything afterwards is what left
each one a different distance off the floor. Moving a belt now moves
its seat with it. (1, 2, 8)
cos, notsin. An odd termcannot produce mirrored members. (2)
BELT_XS, and the ridge fades out undereach belt because twine compresses straw flat where it bites. Without
that fade the ±14 mm ridge swamped the waist. (3, 6)
mass), smooth cord. (4)
exists to carry it. A low mesh dense enough to model flakes directly
would cost thousands of triangles. (5)
of doming down and being clamped. (9)
displacing border verts flared the corners into spikes) and keyed to
Z strata, not radial rings — the ring form resolved at
BAKE_RESinto a pixelated bullseye on the hero.
New hygiene budgets — all recomputed from the generated mesh
The z-fight count is cross-shell, not merely share-no-vertex: two
quads two steps apart on one flat cap share no vertex and are coplanar
by construction, which read 96 pairs, none of them a hazard.
Exit codes — default and every falsifier, all three binaries
--skip-decimate--stray-vert(new)--lift-z--float-belts(new)--slack-belt(new)--skew-belt(new)Two falsifiers were re-aimed rather than widening anything:
--stray-vertparked its vertex above the bale and tripped the bboxbudget (8) first, proving nothing about hygiene — it now sits inside.
--skew-beltused a Z shift that landed a belt face coplanar with aloaf face and tripped z-fight (15) — it now shifts along X, where the
loaf profile barely changes so every other budget stays green.
tests/check_falsifier_targets.pypasses: 166 falsifiers, 26 pieces.Triangles and determinism
Base 852 → 2012 tris (band widened 750–1100 → 1700–2400; showcase
budgets are meant to grow). Collider 78 → 156, ceiling 400 unchanged.
Every geometric figure is identical on all three binaries; only the
glTF export differs, by 8 B on 5.2.1, which is not a gated axis.
Hero and gates
Framing gate passes,
check_framingcalled with nodeviation=:fill x=0.575 y=0.789 band=0.70..0.90, margins0.237 / 0.188 / 0.106 / 0.106, no edge touch. Camera moved from 50 mm at 2.1 m to 70 mmfurther back (a 0.92 m subject had the near end reading half again as
large as the far one) and the yaw from −32° to −20° so the long ribbed
flank reads rather than the end.
Contact sheet:
docs/gallery/contact-sheets/hay-bale-contact-sheet.webp,against the pinned set (
armature-bend,damped-track-aim,bmesh-gear).bmesh-gearStopping honestly
Four inspection rounds; past the three-round limit. Form, binding and
hygiene are right and locked; the surface is not — it still reads as
a wrapped parcel, not straw. That is a material and bake problem, not a
geometry one: straw needs directional fibre and colour break-up that a
256 px tangent-space normal off a smooth high mesh cannot deliver.
Recorded in the piece README under Known gap so the next run does
not re-litigate the silhouette. The stale still was re-rendered because
the geometry changed and leaving it would misrepresent the asset.
Incidental
The piece deleted no temp export, and Blender's
TMPDIRresolves to theworking directory on a portable build, so every run left a
.glbin therepo root (39 accumulated during this pass). Fixed here; the same
pattern is in all 28 showcase pieces and is left for a follow-up
rather than touched in a quality-pass PR.
2.
examples/exit-pre-sidecar— Track CContract frozen. Documentation only.
Correctness bug found in the docs, not the contract. The module
docstring read "Catalog
min_version5.0 is wrong — the floor is5.1." The catalog has carried
min_version: 5.1since the example'sintroducing commit (
bb5a2df), so the note was never true and wouldsend a reader to "fix" a correct row. Removed.
Re-verification against all three binaries. The documented behaviour
is correct:
hasattr(bpy.app.handlers, "exit_pre")isFalseon4.5.11 LTS,
Trueon 5.1.2 and 5.2.1 LTS — andexit_preis the onlyexit- or quit-related name in
bpy.app.handlerson the versions thathave it, so there is no older spelling to fall back to.
README improvements: states the contract and the bug class it
catches (
atexitfor teardown work); adds the 4.5 and 5.2bpy.app.handlersAPI links, noting the 4.5 page lists noexit_pre;explains that exiting 77 on 4.5 is correct by design and what stops
a skip spreading (
min_version5.1, the runner failing a skip above thefloor,
summarize.pyreddening an all-skip job); and enumerates thefalsifiers in a table with what each breaks, its script exit and how the
harness fails — naming
--write-in-mainand--atexit-insteadas thetwo an existence-only check would pass, which is why the catalog row
carries
sidecar_contains.Contract, assertions and exit codes unchanged. Identical before and
after:
--silent-handler--no-handler--wrong-text--write-in-main--atexit-instead--force-run(script)3.
showcase/README.mdconventionsThree conventions every future piece inherits, each from a defect above
that no existing budget could see: even shaping terms and mirror
symmetry (19); wrappers follow the host's profile, and the host is
finished first (16/18), including per-wrapper-shell grounding because
the AABB gate stays green when the host grounds the box; and
station-local seat measurement — never nearest-surface distance in a
concave waist, and bin by rounding, never flooring, or a station on a
bin boundary splits its inner and outer rings. Plus the cross-shell
sharpening of the z-fight budget.
Proven by live run vs. inspection
Live run on all three binaries: every exit code, every measured
budget figure, the version claims in §2, the framing gate, determinism
across versions, and the temp-export cleanup. Inspection only: the
contact-sheet verdicts are my judgement of the committed composite, and
the "reads as a parcel" verdict in §1 is a judgement call, not a
measurement.
Inspection renders stay in
.scratch/, which is gitignored.🤖 Generated with Claude Code