Skip to content

feat: framing deviation clause + triage of the out-of-band thirteen#108

Merged
TMHSDigital merged 4 commits into
mainfrom
docs/framing-deviation-triage
Jul 22, 2026
Merged

feat: framing deviation clause + triage of the out-of-band thirteen#108
TMHSDigital merged 4 commits into
mainfrom
docs/framing-deviation-triage

Conversation

@TMHSDigital

Copy link
Copy Markdown
Owner

Framing deviation clause + triage of the #107 out-of-band thirteen

Docs-and-triage follow-up to #107. The survey it produced put 13 of 22 measured examples outside the new framing band — including damped-track-aim, a pinned calibration member whose radiating needles bleed past the frame edges by design. When more than half the corpus violates a fresh rule and the rule's own reference standard fails it, the rule is at least partly wrong. This pass gives the rule the same presumption/deviation structure the stage rules have, reconciles two runtime facts, and triages all thirteen (plus vse-cut-list) before the helper gets wired gallery-wide. No re-renders.

Part 1 — deviation clause + helper signature

docs/VISUAL-STYLE.md Layer 1: the 70–90 % fill band and the 2 % edge-margin floor remain the presumption; a composition may deviate when the bleed or the scale is the design — radiating subjects, edge-to-edge fields, world/atmosphere contracts. A deviation requires a one-line README note under a Framing deviation heading (exactly as stage deviations); an undocumented deviation is a defect.

Mechanical, not honor-system — new helper signature:

check_framing(scene, camera, hero=..., elements=..., stage=..., strategy="silhouette", deviation=None)
  • deviation=None (default): enforces (exit 10 on violation) — unchanged for both current callers.
  • deviation="<non-empty reason>": measures and prints the numbers with the reason (framing_deviation reason="..." enforcement=report ...), returns 0 — report, not enforce.
  • deviation="" / whitespace: raises ValueErrora deviation cannot be taken silently.

CLAUDE.md's quality-gates bullet inherits the clause by reference. The helper docstring also scopes the tool: it measures the (scene, camera) pair it is handed (multi-scene examples pass the still's scene explicitly), and world/atmosphere-subject examples report rather than enforce because the matte has no renderable hero to isolate.

Part 2 — two reconciled runtime facts (CLAUDE.md)

  • CI floats within each series. Workflow evidence: blender-smoke.yml resolves the binary with grep -oE "blender-${series}\.[0-9]+-linux-x64\.tar\.xz" | sort -V | uniq | tail -1 on the download.blender.org listing — highest published point release at run time, not a pin. Its 4.5 job ran 4.5.12 LTS on feat: measurable framing gate + restage hygiene/origin heroes into the band #107 while local .scratch/ held 4.5.11. CLAUDE.md now says the series floats and a local point release may lag; the rule that agents state the exact version their binary reports is unchanged.
  • Playwright lesson. Recorded next to the gallery-capture gate: gallery <img> tags lazy-load, and Chromium scroll-anchoring defeats scrollTo(0, 0) after images load (observed on feat: measurable framing gate + restage hygiene/origin heroes into the band #107: scrollY stayed ~3.3–3.5k after the scroll-back) — take full-page captures and crop locally.

Part 3 — triage (all thirteen + vse-cut-list)

Each hero reviewed at full size and gallery-card scale against its README and contract. Measured numbers from the #107 survey (silhouette strategy, union-of-renderables).

example measured classification reasoning
damped-track-aim fill 1.000x/0.917y; touch L/R/B Legitimate deviation Radiating composition — twelve spikes read as converging on the core from beyond the frame; cropping their tails is the design. Calibration member; the reference standard stands. README note added.
wave-displace fill 1.000x/0.817y; touch L/R/B Legitimate deviation Edge-to-edge field — the sheet crops so the wave reads as a surface, not an object on a stage; the fill is the point. README note added.
sky-texture-sun-elevation fill 0.369x/0.489y Measurement artifact The hero is the sky itself (world contract); the matte measured only the cone reference props. No helper special-case — the example documents report-only if ever wired (README note added).
depsgraph-export fill 0.984x; touch L/B Genuine defect Left cube amputated by the left and bottom edges; the clip does no contract work.
driver-wave fill 0.969x over; margins 0.016 Genuine defect Bar skyline over-wide; side margins below the floor.
shader-node-group fill 0.969x over; margins 0.016 Genuine defect Sphere pair over-wide; side margins below the floor.
swatch-grid fill 0.944y over Genuine defect (marginal) Grid 4.4 pt over-tall; margins themselves clear.
gp-lineart-contour fill 0.978y; touch B Genuine defect Glass base cropped at the bottom edge; vertically over-filled.
png-exr-alpha touch B (fill 0.825x/0.783y) Genuine defect (minor) Display plinth touches the bottom edge; screens themselves clear.
vertex-weight-limit fill 0.247x/0.861y; touch B Genuine defect Base cropped at bottom; narrow subject with dead space left.
text-version-stamp fill 0.663x/0.567y Genuine defect (mild) Under-filled; dead lower third below the underline reads as dead space, not design.
triangulate-tangents fill 0.334x/0.661y Genuine defect Buckler small in a large stage; dead space both sides.
curve-bevel-arc right margin 0.012 Genuine defect (marginal) Right cap clears by 1.2 %, under the 2 % floor.
vse-cut-list not surveyed In scope (was survey limitation) Its still renders from a dedicated Bay scene (BayCam); the survey read bpy.context.scene. The helper needs no skip contract — it measures the (scene, camera) pair it is handed. README records the pair explicitly.

The ten defects land on the ROADMAP candidate pool as a restage worklist with measured numbers. No example besides the README notes above was touched; nothing was re-rendered or re-staged.

Falsification evidence (deviation path, mesh-hygiene-audit as probe, 5.1.2)

  • Reasoned deviation reports: lens 50→30 + deviation="probe: deliberate under-fill to witness report mode"framing_fill x=0.481 … FAIL printed, framing_deviation reason="…" enforcement=report fill_ok=False printed, exit 0, artifact written. Restored.
  • Silent deviation rejected: deviation=""ValueError: check_framing deviation requires a non-empty reason string…, exit 1, no artifact. Restored (file byte-identical to main, diff-verified).

Verification

  • Both current check_framing callers re-run after the signature change, render path, exit 0 with unchanged numbers — 5.1.2: hygiene 0.803x/0.733y (margins L0.100 R0.097 B0.050 T0.122), origin 0.816x/0.678y (L0.084 R0.100 B0.072 T0.183); 4.5.11: identical values.
  • Check-only both binaries for the examples importing the helper: exit 0, timings in the historical band (5.1.2: 711/727 ms; 4.5.11: 635/579 ms) — smoke invocations are these check-only paths, so smoke runtimes are unchanged.
  • python scripts/build_gallery.py regenerated; the four regenerated detail pages (damped-track-aim, wave-displace, sky-texture-sun-elevation, vse-cut-list) read character by character — new Framing deviation/Framing measurement sections complete, code spans escaped, no truncation or artifacts.
  • Local binaries: .scratch/blender-5.1.2-windows-x64/blender.exe (reports Blender 5.1.2), .scratch/blender-4.5.11-windows-x64/blender.exe (reports Blender 4.5.11 LTS).

Live-run vs inspection-only

  • Live run (5.1.2): both deviation falsification probes, both callers' render paths, check-only runs, gallery regen.
  • Live run (4.5.11): both callers' render paths + check-only.
  • Inspection only: the triage classifications (full-size + card-scale review of already-shipped heroes — no re-renders by design), and the assumption that CI smoke runtimes track local check-only timings.

PR #107's survey put 13 of 22 measured examples outside the band,
including a pinned calibration member whose edge bleed is the design, so
the band gains the same structure the stage rules have: presumption by
default, documented deviation when the bleed or the scale is the point.
check_framing takes deviation=<reason>: with a non-empty reason string
it measures and prints the numbers with the reason but reports instead
of enforcing; an empty or whitespace reason raises ValueError so a
deviation cannot be taken silently. The docstring also scopes the
helper: it measures the (scene, camera) pair it is handed, so
multi-scene examples pass the still's scene explicitly, and
world/atmosphere-subject examples report rather than enforce because
the matte has no renderable hero to isolate. Both current callers keep
the enforcing default; verified unchanged on 5.1.2 and 4.5.11
(0.803/0.733 and 0.816/0.678) and falsified both ways (reasoned
deviation reports with exit 0; empty reason ValueErrors with exit 1).

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
Layer 1 framing now mirrors the stage rules' structure: the 70-90 fill
band and the 2 percent margin floor remain the presumption, and a
composition may deviate when the bleed or the scale is the design —
radiating subjects, edge-to-edge fields, world or atmosphere contracts.
A deviation requires a one-line README note under a Framing deviation
heading and the helper call switches to check_framing(deviation=reason)
so it reports with the reason printed rather than enforcing; an
undocumented deviation is still a defect, and an empty reason raises.
The CLAUDE.md quality-gates bullet inherits the clause by reference.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
The smoke workflow floats: it resolves the highest published point
release of each series at run time, so CI ran Blender 4.5.12 LTS on PR
#107 while local .scratch held 4.5.11 — CLAUDE.md now says the series
floats and a local point release may lag, with the exact-reported-version
rule unchanged. Also records the Playwright lesson from #107 next to the
gallery-capture gate: Chromium scroll-anchoring defeats scrollTo(0, 0)
after lazy-load, so captures are full-page, not scroll-then-shoot.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
Full-size and card-scale review of each surveyed hero against its README
and contract. Two are legitimate deviations and now carry the one-line
README Framing deviation note: damped-track-aim (radiating composition —
the spikes read as converging from beyond the frame; it is a pinned
calibration member and the gallery's reference standard stays) and
wave-displace (edge-to-edge field — the fill is the point).
sky-texture-sun-elevation is a measurement artifact: the hero is the sky
itself, so the matte only saw the cone props; its README now documents
report-only. vse-cut-list is in scope — its still renders from the Bay
scene with BayCam, and the README records that the (scene, camera) pair
must be passed explicitly. The remaining ten are genuine defects
(clipped subjects, over-fill, dead space) and land on the ROADMAP
candidate pool as a restage worklist with measured numbers. No
re-renders in this pass.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Jul 22, 2026
@TMHSDigital
TMHSDigital merged commit cf9ee1f into main Jul 22, 2026
9 checks passed
@TMHSDigital
TMHSDigital deleted the docs/framing-deviation-triage branch July 22, 2026 22:00
TMHSDigital added a commit that referenced this pull request Jul 22, 2026
…es) (#109)

* fix: target the passed scene in gallery_framing matte renders

_matte_render called bpy.ops.render.render(write_still=True) bare, which
renders the ACTIVE context scene — silently wrong for any example that
stills from a non-context scene (vse-cut-list renders its gallery hero
from a dedicated Bay scene via the operator's scene= kwarg). The matte
now renders the scene it was handed, matching that idiom. Both current
callers use the context scene and measure unchanged (0.803/0.733 on
5.1.2 and 4.5.11); the corrected survey that exposed this now captures
each example's rendered scene via a persistent render_pre handler and
re-measured all 38 examples against the scene and camera each actually
renders from.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>

* fix: nudge curve-bevel-arc framing into the band and gate it

Right cap sat 0.012 from the right edge, under the 0.02 floor. Camera
moves from (2.4,-3.4,1.7) to (2.55,-3.62,1.78) — a pure pull-back, no
restage — landing fill 0.738x/0.633y with margins L0.219 R0.044 B0.083
T0.283. The render path now calls gallery_framing.check_framing before
the beauty render (exit 10); check code and closed forms untouched
(eval_verts=1044, eval_faces=1028, x_span=3.292 all print unchanged).
Gate falsified at lens 65 (margins FAIL, exit 10, no artifact) and
restored. Identical framing numbers on 4.5.11.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>

* fix: nudge driver-wave framing into the band and gate it

Sixteen-column skyline over-filled the frame at 0.969x with side margins
0.016. Camera pulls back from (0,-13.5,2.0) to (0,-15.0,2.0) — pure
distance, no restage — landing fill 0.869x/0.333y with margins L0.066
R0.066 B0.367 T0.300. Render path gated with check_framing (exit 10);
check values unchanged (driven_range 0.404..2.399, flushed_to_original).
Gate falsified at lens 55 (fill 0.994 FAIL, exit 10) and restored.
Identical numbers on 4.5.11.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>

* fix: nudge shader-node-group framing into the band and gate it

Sphere pair over-filled the frame at 0.969x with side margins 0.016.
Camera pulls back from (0,-7.8,2.6) to (0,-8.7,2.6) — pure distance, no
restage — landing fill 0.869x/0.650y with margins L0.066 R0.066 B0.256
T0.094. Render path gated with check_framing (exit 10); check values
unchanged (group users=2, distinct instance tints). Gate falsified at
lens 72 (fill 1.000 FAIL, exit 10) and restored. Identical numbers on
4.5.11.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>

* fix: nudge swatch-grid framing into the band and gate it

The ortho grid over-filled vertically at 0.944y. ortho_scale 7.8 -> 8.4
(height 4.39 -> 4.73 for the 4.14-unit grid) lands fill 0.769x/0.878y
with margins L0.116 R0.116 B0.061 T0.061. verify_png's third/half
cell-center sampling tolerates the scale change — distinct_regions=6
and max_pixel=1.000 confirmed at 1280 default and at the smoke
invocation (640, cycles, 8 samples) on 5.1.2 and 4.5.11. Render path
gated with check_framing (exit 10); falsified at ortho_scale 6.5
(fill 1.000 FAIL, exit 10) and restored.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>

* fix: lift png-exr-alpha plinth off the bottom edge and gate it

The shared display plinth touched the bottom frame edge (margin 0.000).
A pull-back first overshot (fill 0.672 under-band), so the fix is an aim
drop instead: aim z 1.65 -> 1.38 at unchanged camera distance pitches
the composition up, landing fill 0.725x/0.628y with margins L0.122
R0.056 B0.028 T0.150 — distance and composition otherwise untouched.
Render path gated with check_framing (exit 10); falsified at lens 64
(fill 0.916 FAIL, exit 10) and restored. Identical numbers on 4.5.11.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>

* fix: nudge gp-lineart-contour framing into the band and gate it

The neon vessel over-filled vertically at 0.978y with its base cropped
by the bottom edge. Camera pulls back from (3.4,-4.2,2.4) to
(3.95,-4.9,2.62) — pure distance, no restage — landing fill
0.463x/0.867y with margins L0.272 R0.266 B0.044 T0.089 (an intermediate
stop landed y at exactly 0.900, on the band ceiling, so the pull-back
goes one step further for cross-version slack). Render path gated with
check_framing (exit 10); check values unchanged (strokes=10, points=34,
restore recovers both). Gate falsified at lens 62 (fill 1.000 FAIL,
exit 10) and restored. 4.5.11 matches (0.469x/0.867y).

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>

* fix: reframe vse-cut-list bay into the band and gate the Bay scene

The corrected survey (render_pre capture) measured this hero for the
first time: the monitor top was cropped and the desk bled the bottom
edge (fill 1.000y, touch B+T) — the #107 survey had read bpy.context.scene,
which is the cut-list scene, not the Bay the still renders from. BayCam
pulls back from (2.0,-5.2,1.72) to (3.05,-7.9,2.0) with aim z 1.28 ->
1.20, landing fill 0.506x/0.850y with margins L0.247 R0.247 B0.067
T0.083. This was the most-broken of the seven (frame-filling before),
so the composition change is correspondingly larger; the bezel, stand,
desk, and on-screen cut list are all fully in frame now. The gate runs
on the Bay scene (the helper's scene-targeted matte from the previous
commit), falsified at lens 66 (fill 1.000 FAIL, exit 10) and restored.
check and --check-pixels both exit 0 on 5.1.2 and 4.5.11 with identical
pixel witnesses.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>

* docs: reduce the framing worklist to the recomposition remainder

The corrected survey plus the marginal sweep leaves four examples that
need real reframing rather than a camera nudge: depsgraph-export (cube
amputated), vertex-weight-limit (off-center, base cropped),
text-version-stamp (dead lower third), triangulate-tangents (2x-class
move-in). The seven marginal cases are marked swept and gated;
deviations and the sky-texture artifact stay as documented in #108.

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>

---------

Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant