feat(art): replace all 72 posts' artwork with the v2 pipeline
Deploy / build (push) Successful in 17s
Every post now carries one unique local-ComfyUI/Ideogram 4 base frame, composited into EN + ZH share cards, a banner and list thumbs. - the shipped prompts_catalog held only 21 distinct metaphors for 72 posts (16 shared one visual), so all 72 were rewritten as unique object+state metaphors and 10 frames re-rolled after OCR found pseudo-text rendered at legible size - render_from_art.mjs emits JPEG by default (558 KB PNG -> 84 KB mozjpeg q85); the ZH 1200x630 card did not exist before this batch - 288 frontmatter fields migrated; the 144 v1 PNGs are gone - PostList's thumb helper keys off the base slug (a ZH card is zh-<slug>.jpg while the thumb is <slug>-800.webp, which 404'd the whole ZH listing) - [...slug].astro's <picture> regex accepts .jpg - the repo now carries the art inputs (metaphors.json, captions/<slug>.json, refreshed prompts_catalog.json) so a frame is reproducible from git alone
No files matched your search
@@ -94,7 +94,7 @@ Living list of what's done and what's next for blog.hoelee.com. Work through the
|
||||
|
||||
### Phase F — Artwork rollout: v2 "one art, four targets" (NEXT; user decision 2026-10-18)
|
||||
|
||||
**Step B6b — Replace all 72 posts' artwork with the consultant-approved v2 set.** 🔵 Ready to run; blocked on a GPU window + three answers
|
||||
**Step B6b — Replace all 72 posts' artwork with the consultant-approved v2 set.** ✅ Rendered and staged 2026-10-19 (working tree; **not pushed** — awaiting the owner's read)
|
||||
|
||||
Everything below is measured, not planned. A fresh session needs no other context to continue.
|
||||
|
||||
@@ -103,13 +103,29 @@ Everything below is measured, not planned. A fresh session needs no other contex
|
||||
| The renderer | `scripts/og-gen/render_from_art.mjs` (committed `a41448b`) — one zero-text base frame → banner 1600×900 (`banners/<slug>.webp` + `.jpg` fallback), social card 1200×630 (`og/<slug>.jpg` EN, `og/zh-<slug>.jpg` ZH), thumbs `og/thumb/<slug>-{800,400}.webp` |
|
||||
| The layout | `scripts/og-gen/template-v2.2.html` — frosted 736×536 bento card, blueprint grid, full-width evidence bar, self-fitting headline that reports `FIT <n>px` through `<title>`; a card that never settles fails the run |
|
||||
| Per-post inputs | `scripts/og-gen/art-meta.json` — `art` (default `<art-dir>/<slug>.png`), `evidence` (**required, hand-written**), optional `ogTitle_en/zh`, optional `tags` |
|
||||
| Prompts | `scripts/og-gen/prompts_catalog.json` — **72** entries (one visual metaphor + prompt per post) |
|
||||
| Base art on disk | **2 of 72** — `C:/AI/ideogram4/out/sample_prod_169_00001_.png`, `sample_oom_169_00001_.png` (the consultant samples). **70 still to generate, and this is the GPU half of the job** |
|
||||
| Evidence lines written | **2 of 72** (`running-production-infrastructure-solo`, `docker-reported-exit-0-the-oom-killer-disagreed`) |
|
||||
| Live site | still the **v1** set: 72 `public/banners/*.png` + 72 `public/og/*.png` from the terminal-panel generators (2026-10-06…10-16). Nothing a visitor sees has changed yet |
|
||||
| Prompts | `scripts/og-gen/prompts_catalog.json` — **72** entries, regenerated 2026-10-19 after the metaphor rewrite (the shipped file had only **21 distinct metaphors for 72 posts**: 16 shared one visual, 15 another, 10 another) |
|
||||
| Base art on disk | **72 of 72** — `C:/AI/ideogram4/out/<slug>.png`, 1360×768, produced 2026-10-19 (batch 4.5 h, ~220 s/frame, 20 steps) |
|
||||
| Evidence lines written | **72 of 72** (`scripts/og-gen/build_art_meta.py` holds all 72, each quoting a number/log line/command from its own post) |
|
||||
| Rendered targets | 72 EN cards + 72 ZH cards + 72 banner `.jpg` + 72 banner `.webp` + 144 thumbs, all in `public/`; the 72 v1 `public/og/*.png` and 72 v1 `public/banners/*.png` were deleted |
|
||||
| Frontmatter | 288 fields migrated by `scripts/og-gen/migrate_to_v2_assets.py --write` (144 files: EN `ogImage: /og/<slug>.jpg`, ZH `ogImage: /og/zh-<slug>.jpg`, both `banner: /banners/<slug>.jpg`) |
|
||||
| Component changes | `PostList.astro` thumb helper now derives **from the base slug** (a ZH card is `zh-<slug>.jpg` while the thumb is `<slug>-800.webp`); `[...slug].astro` `<picture>` regex accepts `.jpg` |
|
||||
| Live site | unchanged — still serving v1 (nothing pushed) |
|
||||
| Reference delivery | `C:/Users/hoelee/Desktop/blog-og-image-redesign-brief/delivery-for-consultant-round4/` (+ `00-第四轮-实施反馈与3个待定细节.md` for the three open questions) |
|
||||
|
||||
**What the GPU run has to produce.** 70 zero-text 16:9 frames (1360×768 or 1920×1080), one per post, from that post's entry in `prompts_catalog.json` — Ideogram 4 pipeline (`C:/AI/ideogram4/`) or ComfyUI on the Win11 VM's RTX 3060. Hard rules from the consultant: **no text, letters, numbers, HUD or watermark anywhere in the art** (put them in `art-meta.json` under `art` and drop them into `C:/AI/ideogram4/out/<slug>.png`, which is the renderer's default path).
|
||||
**Rollout log — 2026-10-19 (every number measured this session).**
|
||||
|
||||
1. **The prompt set was not usable as shipped.** `prompts_catalog.json` carried **21 distinct metaphors for 72 posts** (16 posts shared "concentric acrylic rings", 15 shared "rackmount hardware", 10 shared "diagnostic probe"), so the batch would have produced visibly duplicated art across a third of the site. Rewrote one concrete object + state per post → **72/72 unique**, and generated the 72 structured JSON captions with `C:/AI/ideogram4/build_captions.py` (asserts the slug set matches `src/content/posts/*.md` and that no two metaphors are identical).
|
||||
2. **GPU run.** 72 frames at 1360×768 / 20 steps / `--preset default`, ~220 s each, **4.5 h** total, resumable via `C:/AI/ideogram4/run_batch.py --all` (skips slugs whose frame exists). QC: 72/72 present, all exactly 1360×768, left-60 % mean luminance 10.2–23.1 (the space the card's text sits on).
|
||||
3. **Caption defect found and fixed (3 of 3 pilot frames).** With the subject described twice — `high_level_description` repeating the metaphor — the model drew a labelled diagram with a pseudo-title in the corner (`Oypert`, `Oyristes tool`, `CONTRACT`). One short noun phrase in the HLD + the subject only in `elements[].desc` cleared it.
|
||||
4. **The "no text anywhere" rule fails on the accepted samples too.** `sample_oom_169_00001_.png` OCRs as `Kenel L.aser` @0.874 plus six more hits. So the gate judges **legibility**, not hit count: a glyph box under ~14 px on the 1360×768 frame is invisible at banner (1600×900) and thumb (398 px) size.
|
||||
5. **The model renders the prompt's own label nouns as text.** Measured: `access-denied barrier` → "ACCCSS-DENIED", `attachment clip` → "Attachment Clip", `eight labelled ports` → a 389 px glyph, `id-stamped cards` → "ATACHMENT CLIP", `233 fresh rivets` → a row of numbers, `SSD slab` → a 66 px "SSD", `PLUGIN cartridge` → "PLUGIN". Rewriting the metaphor to describe shape/material/state only fixed every one of them on re-render.
|
||||
6. **Result after two correction rounds:** 45/72 frames fully clean, 18 with a single sub-threshold tick mark, and the 10 frames that exceeded the accepted reference all brought down to clean or reference level. Gate + tooling: `C:/AI/ideogram4/ocr_gate.py` (`--baseline` prints the accepted samples' numbers), `qc_frames.py` (per-frame stats + unlabelled contact sheets).
|
||||
7. **Delivery staged, not shipped.** Rendered 72 EN + 72 ZH cards, 144 banner files, 144 thumbs into `public/`; deleted the 144 v1 PNGs; migrated 288 frontmatter fields; fixed the two components. `npm run build` → **251 pages**, 216 distinct image references, **0 missing, 0 stale `.png`**; the ZH listing's thumbs resolve from the base slug (the old helper would have 404'd every one of them).
|
||||
8. **Art weight: 13.4 MB → 15.4 MB (+2.0 MB), and it buys 72 ZH cards that did not exist before.** Per file: EN card 67 KB JPEG (v1 was 71 KB PNG), banner 40 KB JPEG + 30 KB WebP, thumb 7 KB WebP.
|
||||
|
||||
**Two observations for the owner** (not defects, both open):
|
||||
- The hero banner carries no text overlay, so the art's left 60 % negative space reads as dark band on a dark page; on `your-disk-full-alert-is-lying` the subject occupies ~29 % of the banner width. A banner-side crop toward the subject (the card already scales the art 116 %) would roughly double its presence. Not applied — it changes the framing the consultant accepted in round 4.
|
||||
- Several frames are intentionally very dark; at full banner width the subject can read as small.
|
||||
|
||||
**Three decisions still open** (asked of the consultant in round 4 §二; the owner may simply pick the defaults):
|
||||
1. English headline rag → **default: (C) shorten the card headline only**, via `ogTitle_en` in `art-meta.json` (article title untouched).
|
||||
@@ -135,9 +151,9 @@ Everything below is measured, not planned. A fresh session needs no other contex
|
||||
- Tags/banner: `python <skill>/scripts/measure_og_banner.py <slug> <repo>` → `rows==1`, `overflow==0`, banner `delta<=3` — it only covers the tag row and terminal panel, not the headline.
|
||||
- After deploy: every `/og/<slug>.jpg`, `/og/zh-<slug>.jpg`, `/banners/<slug>.{webp,jpg}`, `-800.webp` returns 200 **with cache-busting** (Cloudflare caches a negative 404 for 16 days).
|
||||
|
||||
**Known gaps in the plan**
|
||||
- The **OCR pseudo-text gate** promised to the consultant (auto-regenerate a frame that contains readable characters) is **not implemented** — it has to be written or the promise withdrawn.
|
||||
- `art-meta.json` is committed with only the two sample entries; the 70 others are the work.
|
||||
**Known gaps in the plan — updated 2026-10-19**
|
||||
- ✅ The **OCR pseudo-text gate** promised to the consultant is **implemented**: `C:/AI/ideogram4/ocr_gate.py`, calibrated against the two accepted samples (`--baseline`). Judge legibility (glyph box ≥ 14 px on the 1360×768 frame), never raw hit count — the accepted reference has a readable phrase in it.
|
||||
- ✅ `art-meta.json` now carries all 72 entries; `scripts/og-gen/build_art_meta.py` regenerates them and re-checks coverage against `src/content/posts/*.md` before the renderer runs.
|
||||
- Extra thumbs: the renderer emits 400 px and 800 px; nothing else consumes them.
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |