499 post-mortem: an O(n) library audit inside a polled GET on a one-core
cgroup, three reverse-proxy hops, and the status no monitor watches.
- measured: 13.6 s idle / 17.7-98.5-35.8 s probes / 76-2507 s under load
- evidence triad: 7x 499 @ portal, zero app-log entries, in-container repro
- fix: 300 s TTL cache + single-flight refresh + refreshing/cached_at flags,
text-first client reader, ?refresh=1
- result: cached path 200 in 0.24-0.36 s (median 0.26 s)
- art: ideogram4 base frame + 4 derived targets, art-meta evidence line
- LinkedIn companion: docs/linkedin/batch-07-linkedin-posts.md
- performUpkeep: external callable, re-check is mandatory security (not stale-data hygiene)
- BasicNft.tokenURI: ownerOf -> _ownerOf, dead custom error explained
- storage (SSTORE) vs EIP-170 runtime bytecode separated, derived ~1.3M gas for the two SVGs
- subs-15 retimed from a fresh faster-whisper word alignment (10.84s first line, was 28.22s) + SRT regenerated
- RDAP needs curl -L (bootstrap 302), ASN/Spamhaus blocklist check added
- learning-project framing -> Sepolia reference implementation / testnet validation build
- dynamicCta on the two posts that ended with no conversion block
- cross-links between the two NFT posts; tracker 91/91 approved
Second pass on articles already approved in Batch-02. Real fixes landed in
19de315; this commit records the outcome, the items that were already fixed
before the reviewer's snapshot, and four reviewer misjudgements (the $TOKEN
'***' artefact comes from the batch-packaging scrubber, not the posts;
06's User Scripts explanation was already correct; 11's Round 3 was already
closed; the 'missing CTA' claims are false). One open question: 01's physical
hardware anchor is unverified and was deliberately not invented.
Tracker also carries the concurrently approved posts from the sibling session.
Review pass on the 041471d delivery, measured rather than eyeballed.
/posts/ on a phone put its first archive row 2.36 screens down (featured block
1235 px + 435 px of controls = row at 1996 px at 390x844), so a reader who came
to browse had to scroll past three stacked 16:9 cards. Below 700 px the cards are
now horizontal — thumb 38 %, excerpt dropped, title clamped to three lines — and
the block header carries `All 91 posts ↓` into #archive-list (with scroll-margin
for the sticky header), which doubles as the bypass-block affordance. Block
1235 → 523 px, card 392 → 153 px, first row 1996 → 1309 px.
Two new small-text colours measured under WCAG AA (.feat-min 3.04:1,
.archive-status 3.67:1 at ~12 px) move to --muted → 5.78:1 and 6.99:1. The
featured heading went with them; pre-existing --faint dates (3.67:1) were left
alone rather than restyling 91 rows.
/categories/ still showed the old Gemini case-studies.webp while the category
page had the new topology banner — one category, two art languages. The tile now
uses case-studies-banner-1200.webp (14 KB) letterboxed on #0b1613: a 16:9 centre
crop sliced the outer host panels in half, the letterbox was measured seamless.
`6min read` was emitted without its space (template-literal fix → `6 min read`,
`6 分钟`, verified in dist/). Both /posts/ ledes stopped claiming the page is
purely newest-first.
probe-layout: 72/72 viewport checks, collapse still 425→0→425 (1280) and
523→0→523 (390) in EN and ZH.
Tracker at 20 approved / 71 pending. Same pass as batch 1: first person throughout,
no LAN IPs in prose, ZH twins normalised to simplified Chinese, dynamicCta where the
post ends on a concrete next step.
- docs/consultant-review/: review brief, per-batch post dumps (01-10),
audit-tracker.json + AUDIT-TRACKER.md generated by scripts/manage_review.py
- scripts/manage_review.py: list/approve/approve-batch/generate over the tracker
- docs/consultant-review-2026-10-27-posts-and-casestudies-plan.md: locked
decision register + 4 work orders for the /posts/ archive rework and the
case-studies portfolio treatment
§4d now carries the real commit chain (f32ae90 → 04a1082 → cf4504e), the
verification that matters (Actions 292/293/295, six pages 200, artwork md5 vs
live, check-assets 162/162) and an owner queue with three items.
The task doc is rewritten around that queue: (1) confirm whether the four stage
timings (14.2 / 2.1 / 32.4 / 48.6 s) were measured — they have no trail in the
repo and the reviewer's complaint was exactly about unmeasured numbers;
(2) supply the before/after jitter clips; (3) decide which session owns edits to
these three posts, since two sessions on the same files put one session's
paragraphs into the other's commit.
Also drops a claim inherited from the parallel session's completion block: the
reviewer's meta descriptions were never adopted, the frontmatter keeps the
originals.
Reviewer's five "highest priority" items, each checked against the source before acting:
* 38% speedup -> 38.1% less render time (352s -> 218s, 1.61x throughput); says it was
a single run each, same clip, same machine, same encoder settings.
* "microsecond-accurate" -> frame-accurate: at 30 fps one frame is 33.3 ms, and the text
now states that word-level timestamps are not microsecond-accurate.
* "Three minutes later" -> "a few minutes later", plus the one measured number (218 s
render for the 4-minute track) so the claim is checkable.
* Zero Jitter / Zero Light Flashing -> "no visible jitter" / "no dark flash at the cut",
scoped to the clip that was measured.
* ti.mode 0o666 -> 0o644. Hygiene, not the syntax error he reported: source and live HTML
both read 0o666 (the live page splits it across two highlight spans and his text
extraction inserted a space), and 0o666 is legal Python.
* "0 MB RAM spike in Chrome" now scopes itself to the Chrome container and says the
Python client still buffers the whole tar. New "What this pattern does not solve"
block: client buffering, target directory must already exist, tar member names become
container paths, Portainer key / CDP 9222 are root-equivalent.
All three posts get a TL;DR and inline cross-links (the trio had zero internal links
before); the ZH links use the real /posts/zh/<slug>/ path.
Not done, because it needs the owner's data rather than invention: end-to-end timing
table, benchmark conditions, whether 1.89 s was a single run, before/after jitter clips,
the "what still needs a human" list. Task list:
docs/consultant-review-2026-10-22-3-video-posts.md.
Artwork for the three slugs regenerated (1600x900 banner, 1200x630 share cards EN+ZH,
400/800 list thumbs); the ZH pipeline card already carries the corrected description.
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.
Six photo cards in 2 columns (3 rows) at desktop widths; phones collapse to a
single full-width column and still show only the first four. Drops the 900px
two-column step (now the base) and the cramped-card type overrides that only
existed for the 2-up phone grid.
Related posts were 4 text-only cards. They now carry the post's language-specific
ogImage (/og/<slug>.jpg for EN, /og/zh-<slug>.jpg for ZH), so an English page never
shows Chinese art. Grid is 3 across above 900px, 2 across below, and phones show the
first 4 only; read-time is dropped under 600px so the date fits.
Verified in the built HTML + headless Chrome at 1264/884/500px: 3/2/2 columns,
6/6/4 visible cards, no horizontal overflow, all six images decoded.
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).
Also carries scripts/og-gen/sync_prompts_catalog.py, the check that keeps the
repo's art inputs (metaphors.json, captions/, prompts_catalog.json) in sync with
the authoring tool dir after any metaphor change.
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
The owner is continuing the artwork batch in a new session with a GPU window,
so the resume kit now exists in the repo instead of in this session.
- New §2 "Phase F — Artwork rollout" placed ABOVE Phase E and flagged as the
NEXT task, with Step B6b: what is already committed (render_from_art.mjs,
template-v2.2, prompts_catalog.json), the measured state (2 of 72 base frames,
2 of 72 evidence lines, live site still v1), the three open consultant
questions with proposed defaults, and the format decision with its numbers
(558 KB PNG vs 84 KB mozjpeg q85, x144).
- Calls out the two migration traps that make a half-done batch break the site:
ogImage on 144 frontmatter files, and PostList.astro's thumb helper regex.
- Documents the order of work, the reproducibility proof (byte-identical
banner/thumb on the two sample slugs, four sha256s), the measurement method
for card layout, and the two known gaps (OCR pseudo-text gate missing,
evidence lines unwritten).
- §4b gains today's measured values: bento live on both homepages, light-theme
contrast fixed, post live at 250 sitemap URLs, cron facts (dev.to 2/70,
LinkedIn 5 published).