From 22c84181734b344d4a38a00b0d16322871399fa8 Mon Sep 17 00:00:00 2001 From: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Date: Sun, 4 Oct 2026 20:19:43 -0400 Subject: [PATCH] fix(gallery): render inline code in callouts; add the asset-sheet gate to the example prompt The parent-inverse-orrery witness callout showed raw markdown ("Bare `.parent =` ...") because build_gallery.py only html-escaped the teaches and witnessesFix text. Both now render backtick spans as , like the landing page's inline_code; alt text, an attribute that cannot carry markup, now fails the build if it contains a backtick. docs/new-example-prompt.md covered the contact-sheet gate but never the asset-sheet gate CLAUDE.md requires for asset-type examples (three of the pinned asset references are examples). It now describes the gate, the SELECT row it needs, and the asset-quality floors. Closes #368 Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 --- docs/gallery/index.html | 2 +- docs/gallery/parent-inverse-orrery/index.html | 2 +- docs/new-example-prompt.md | 14 +++++++++++ scripts/build_gallery.py | 24 +++++++++++++++---- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/docs/gallery/index.html b/docs/gallery/index.html index 2826c966..aa5adac2 100644 --- a/docs/gallery/index.html +++ b/docs/gallery/index.html @@ -349,7 +349,7 @@

Color Attribute WheelParent Inverse Orrery

parent-inverse-orrery

Data-API parenting for a brass orrery — the keep-world idiom (child.parent = pivot; child.matrix_parent_inverse = pivot.matrix_world.inverted()) carrying arms, planets, and a two-level moon through spinning pivots.

-

witnesses Bare `.parent =` really does teleport the child; the idiom restores world position exactly; matrix_world stays stale until view_layer.update(); every orbit lands on its closed form.

+

witnesses Bare .parent = really does teleport the child; the idiom restores world position exactly; matrix_world stays stale until view_layer.update(); every orbit lands on its closed form.

diff --git a/docs/gallery/parent-inverse-orrery/index.html b/docs/gallery/parent-inverse-orrery/index.html index 76518d3f..9f6f2b56 100644 --- a/docs/gallery/parent-inverse-orrery/index.html +++ b/docs/gallery/parent-inverse-orrery/index.html @@ -53,7 +53,7 @@

Parent Inverse Orrery

A brass orrery on a turned walnut base: a glowing gold sun atop the column, mottled blue, green and orange planets on riser posts, orbit rings beneath them, and a silver moon.

Rendered headless by the example itself. Select it to enlarge.

-
witnesses Bare `.parent =` really does teleport the child; the idiom restores world position exactly; matrix_world stays stale until view_layer.update(); every orbit lands on its closed form.
+
witnesses Bare .parent = really does teleport the child; the idiom restores world position exactly; matrix_world stays stale until view_layer.update(); every orbit lands on its closed form.

tags objects transforms

blender --background --python examples/parent-inverse-orrery/parent_inverse_orrery.py --
diff --git a/docs/new-example-prompt.md b/docs/new-example-prompt.md index 4699d913..3b2af9d6 100644 --- a/docs/new-example-prompt.md +++ b/docs/new-example-prompt.md @@ -148,6 +148,20 @@ is not acceptable evidence. When a new example outclasses a member, update the pinned set in `CLAUDE.md`, its canonical home. One successful render command is not proof that the image is good, and neither is the second. +Asset-sheet gate (required when the subject is a game asset: a prop, kit piece or +vehicle the example ships as a model): render the hero asset alone (neutral +three-quarter view, plain studio light, no staging, labels or comparison props) +beside the pinned asset-quality reference set, whose canonical membership is also in +`CLAUDE.md` § Quality Gates for Example Runs, with +`python scripts/asset_sheet.py --blender PATH NAME`. Add the entry's row to that +script's `SELECT` table first (`tests/check_asset_sheet_select.py` enforces it for +showcase pieces). Commit the composite under `docs/gallery/asset-sheets/`, link it in +the PR body, and report a verdict. The asset ships only if it is not identifiable as +the least-designed object in the lineup: a strong scene can carry a weak model, and +this gate removes the scene. Its measurable floors (naming, material variation, edge +treatment) live in `examples/gallery_asset_quality.py`, called on the render path +(exit 11 on violation). + After implementation and local verification: 1. Review the complete diff and remove accidental churn or temporary files. Stage diff --git a/scripts/build_gallery.py b/scripts/build_gallery.py index 97034c01..fc493790 100644 --- a/scripts/build_gallery.py +++ b/scripts/build_gallery.py @@ -136,6 +136,20 @@ def check_alts(entries: list) -> None: if len(alt) > ALT_MAX: raise SystemExit(f"gallery entry {e['name']!r} alt is {len(alt)} chars " f"(max {ALT_MAX}); describe the still in one sentence") + if "`" in alt: + # An attribute cannot carry , so a backtick would be read + # aloud and shown literally; teaches/witnessesFix render it (#368). + raise SystemExit(f"gallery entry {e['name']!r} alt contains a backtick; " + "alt text is plain prose") + + +def inline_code(text: str) -> str: + """Escape text and render its `backtick` spans as , as the landing + page does. Unbalanced backticks stay literal.""" + parts = html.escape(text).split("`") + if len(parts) % 2 == 0: + return "`".join(parts) + return "".join(f"{p}" if i % 2 else p for i, p in enumerate(parts)) def check_categories(entries: list) -> None: @@ -1446,7 +1460,7 @@ def build_detail(ex: dict, entries: list, *, base: str, repo_root_url: str, site f'
  • {html.escape(name)}
  • ') parts.append(f'

    {html.escape(display_title(ex))}

    ') parts.append(f'

    {html.escape(ex["dir"])}/

    ') - parts.append(f'

    {html.escape(ex["teaches"])}

    ') + parts.append(f'

    {inline_code(ex["teaches"])}

    ') parts.append(" ") parts.append('
    ') parts.append(pager_html(prev, nxt, idx + 1, len(peers), noun)) @@ -1454,7 +1468,7 @@ def build_detail(ex: dict, entries: list, *, base: str, repo_root_url: str, site parts.append(f' {html.escape(ex[') parts.append(" ") parts.append(f'

    Rendered headless by the {noun} itself. Select it to enlarge.

    ') - parts.append(f'
    witnesses {html.escape(ex["witnessesFix"])}
    ') + parts.append(f'
    witnesses {inline_code(ex["witnessesFix"])}
    ') cat = ex.get("category") if cat: parts.append(f'

    category ' @@ -1523,7 +1537,7 @@ def build_detail(ex: dict, entries: list, *, base: str, repo_root_url: str, site .replace("__SIZES__", MINI_SIZES) .replace("__ALT__", html.escape(r["alt"], quote=True)) .replace("__TITLE__", html.escape(display_title(r))) - .replace("__TEACHES__", html.escape(r["teaches"]))) + .replace("__TEACHES__", inline_code(r["teaches"]))) parts.append("

    ") parts.append(" ") @@ -1673,8 +1687,8 @@ def build_index(data: dict, *, base: str, repo_root_url: str, site: str, css_v: .replace("__CATATTR__", f' data-category="{html.escape(ex["category"], quote=True)}"' if ex.get("category") else "") .replace("__KIND__", kind_noun(ex)) - .replace("__TEACHES__", html.escape(ex["teaches"])) - .replace("__WITNESSES__", html.escape(ex["witnessesFix"])) + .replace("__TEACHES__", inline_code(ex["teaches"])) + .replace("__WITNESSES__", inline_code(ex["witnessesFix"])) ) content = (