The YuE2 and subtitles posts shipped text-only: render_from_art.mjs only READS
ogImage/banner, and after the 2026-10-19 bulk migration no script had to write
them, so both pages advertised /og-default.jpg and rendered no hero banner.
- new metaphors (in build_captions.py, frozen here via sync_prompts_catalog.py),
captions, art-meta evidence, banner plan and the five derived files per slug
- frontmatter: ogImage + banner in EN and ZH for both posts
- scripts/check-assets.mjs is now step 1 of npm run build, so CI fails when a
published post lacks a language-correct og card, banner, banner .webp or
400/800 list thumb. It reported 16 problems on the unfixed tree, all here.
The consultant's review of the shipped set found two defects, both ours, and both fixed
at the source rather than in the output:
1. The banner/thumb crop re-framing detected only the glowing core of the object. Column
brightness measures 2-6 levels above the background on dark glass, so it boxed 8 % of
the frame for an object group spanning 35 %+. The detector is now a background-plane
deviation plus the largest connected components (`plan_banner_crops.py`, detector in
`probe_band_crop.py`), 0 % false-positive on the frame's own negative space, stable
across thresholds. The crop plan is frozen as `banner-plan.json` and consumed by the
renderer; a thin subject is centred at 53.5 % of the banner width with the shortfall
filled by the art's own edge pixels, one row at a time (a single averaged fill left a
visible tonal step, tried and rejected). Measured on the delivered 72: 0 clipped,
silhouette median 47.0 %, centre median 53.5 % (`verify_banner_crops.py`).
2. `art-meta.json` pinned two slugs to the hand-accepted sample frames — the very frames
that carried the HUD readouts — which also kept them out of the crop plan. Pins removed.
Prompt side: 9 frames still printed glyphs at the consultant's bar (>=12 px and >=2
characters). Every offending noun is measured and now asserted against: a literal `marked
at 28` printed "28"; `storefront kiosk` printed "PRINATA"; `voucher card` printed
"Voucher"; `market booths` printed "10MARKET"; `glass button` printed "OC"; `gateway pylon`
printed "GATELY". 50 forbidden words, 0 hits across 72 captions, and all 9 frames
re-rendered clean.
Also: YAML escapes are honoured in the frontmatter reader and straight quotes become
curly ones (the ZH cards shipped a literal backslash), the evidence line is capped at 65
characters, and the light theme gives dark artwork a hairline border plus a soft shadow
(consultant option A) so it reads as a dark display case instead of a black slab.
OCR gate at the consultant's bar: 72/72 frames clean (16 carry sub-threshold single
glyphs, exempt as geometry noise).