You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
docs site: homepage hero cover is a 406 KB PNG served at ~1024 px #12242
apps/docs/public/hero-cover-dark.png is a 2400×1200 PNG, 406 KB, displayed at ~1024 px as the homepage video poster (added in #12218). It is the largest element above the fold, so it is the LCP candidate.
Measured
$ ls -la apps/docs/public/hero-cover-dark.png
406703 bytes
$ file apps/docs/public/hero-cover-dark.png
PNG image data, 2400 x 1200, 8-bit/color RGB
Expected
Either
serve it through next/image (automatic resize + WebP/AVIF negotiation), which is the idiomatic fix in this app but introduces the first next/image usage on the site — check it works on the Vercel deployment before claiming it; or
ship a pre-encoded WebP alongside and reference that, keeping the PNG only where the README needs it.
Expect ~80% off the transfer either way. Keep the poster visually identical — it is the same cover the README uses, and the two should not drift.
Acceptance
homepage poster transfers under ~100 KB on a cold load
the rendered poster is visually unchanged at 1×, and sharp at 2×
docs/screenshots/hero-cover-dark.png (README's copy) still resolves
Source
Found in an SEO review of the docs site (apps/docs) run on 2026-08-25, measured against the local dev server and against production. The canonical origin is https://objectstack.ai — maintainer ruling recorded in #10659:
Unblocked: PR #12218 merged to main at 2026-08-25T14:56:33Z as eeec62a98, so apps/docs/public/hero-cover-dark.png and its reference in apps/docs/app/[lang]/page.tsx are now on main and this card has a real base to work against.
Still held one more round — not by #12218 any more, but by a hot file: #12239 (homepage title/description) edits apps/docs/app/[lang]/page.tsx too, and it is the higher-priority card (p1 keyword work vs p2 weight). One of the two goes per round. Known trap to carry into whichever goes second: the poster reference and the page metadata live in the same file, so the second card must rebase rather than assume its base still matches what it read.
Requirement added to this card, from #12235's report — read before starting.
PR #12325 wires Open Graph metadata and, in doing so, created two call sites that spell the literal /hero-cover-dark.png in page metadata (app/[lang]/page.tsx and app/[lang]/blog/[[...slug]]/page.tsx), on top of the poster reference in the homepage body. That asset is exactly what this card re-encodes.
So this card now owns one more thing: hoist the card path into apps/docs/lib/site.ts as a single constant and have every call site read it. Rationale, from #12235's dev and I agree with it: leaving three literals means a re-encode has to find all of them, and the two metadata ones are easy to miss because they do not look like image references — they look like SEO config.
That widens this card's file surface to include apps/docs/lib/site.ts and the two metadata files, which is why it is written here rather than assumed. It stays serialised behind #12235.
Claim: PM loop round 6 (epic PM for #12243)
Session: session_f9f0958b-ab68-46cc-801c-216aa7ee2107
Branch: claude/issue-12242-hero-png-weight
Worktree: objectstack-issue-12242
Domain: domain:devx
File surface: apps/docs/public/, apps/docs/lib/site.ts, apps/docs/app/[lang]/page.tsx, apps/docs/app/[lang]/blog/[[...slug]]/page.tsx (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: opus — no path-derived mandate
Clause-②: no
Serial constraints cleared: every card that shared these files has landed — #12235 (c48d46d70), #12240 (PR #12355), #12234 (c463ec3b0). Nothing is in flight anywhere in this epic. This is the last dispatchable card in the subtree.
PM ruling, made because #12333 removes the evidence the alternative would need
The card offers two routes: next/image, or a pre-encoded WebP alongside. Take the pre-encoded route.
Not on taste — on what can be verified today. next/image would be this app's first usage of the image optimizer, and its whole risk sits in how the Vercel deployment handles it. Production has not deployed since ~16:55 (#12333), so that is exactly the evidence nobody can collect right now. Shipping an unverifiable dependency on a platform feature while the platform channel is dark is the wrong trade for an 80% byte saving that a static asset gets anyway.
If measurement changes your mind — for instance if the pre-encoded route cannot hold visual fidelity at 2× — say so and stop rather than switching routes silently.
Dev claim (os-dev subagent, dispatched by the epic PM above).
Session: session_f9f0958b-ab68-46cc-801c-216aa7ee2107 (os-dev subagent of the epic PM session)
Branch: claude/issue-12242-hero-png-weight (pushed empty at claim time; write route verified)
Worktree: /Users/zhuangjianguo/Documents/GitHub/objectstack-issue-12242, base 1f94049a18a589856c743edc4b08bc1921bf9fa5
File surface as dispatched: apps/docs/public/, apps/docs/lib/site.ts, apps/docs/app/[lang]/page.tsx, apps/docs/app/[lang]/blog/[[...slug]]/page.tsx
Serial predecessor cleared: PR #12355 merged 2026-08-25T20:21:34Z. Base is origin/main as of the claim, not the tree the card was written against — re-deriving the card's numbers rather than inheriting them, and measuring against a local next build && next start per #12333.
Route ruling acknowledged: pre-encoded asset, not next/image.
{
"issue": 12242,
"status": "done",
"branch": "claude/issue-12242-hero-png-weight",
"pr": "https://github.com/objectstack-ai/objectstack/pull/12377",
"premise_still_valid": true,
"summary": "Re-encoded apps/docs/public/hero-cover-dark.png (2400x1200, 406,703 B) to a WebP at quality 80 / effort 6 derived from the master docs/screenshots/hero-cover-dark.png, keeping 2400x1200 so the og:image:width / og:image:height declarations did not have to move, and deleted the now-unreferenced PNG from public/ so a later re-encode cannot pick the wrong file. Took the pre-encoded route as ruled; AVIF was measured (better per byte: SSIM 0.998 at 65 KB vs WebP q85's 0.996 at 94 KB) and rejected because the asset is a plain <img> with no <picture> fallback AND the og:image, so it must decode in every browser and every unfurler. Hoisted the path into HERO_COVER in apps/docs/lib/site.ts next to SITE_ORIGIN/absoluteUrl(), carrying the re-encode recipe and the reasons the dimensions and format are constrained; all three call sites now read it. Card premise holds in full: 406,703 B and ~1024 px both re-derived, the displayed width is exactly 974 CSS px (1948 device px at DPR 2), capped by max-w-5xl minus px-6 at every viewport >= 1024 px.",
"tests": "All at pushed head 63e50a103, tree clean; every verdict quoted is the gate's own line, never a bare $?. TRANSFER (local `next build && next start` on :3242, never production per #12333): before `Content-Type: image/png`, `Content-Length: 406703`, curl size_download=406703, no Content-Encoding; after `Content-Type: image/webp`, `Content-Length: 83272`, curl size_download=83272, no Content-Encoding = 20.5% of the PNG, 79.5% off. Chrome Resource Timing on the running page: transferSize 83572, encodedBodySize 83272. Reverse check: GET /hero-cover-dark.png -> 404, so the old asset is really gone from the build output rather than merely unreferenced. DISPLAYED SIZE read off the running page in Chrome, not inferred from Tailwind classes: displayedCssW 974, displayedCssH 486, devicePixelRatio 2, deviceW 1948, naturalWidth 2400 — unchanged at viewports 1280/1440/2560, so 974 CSS px is the cap, not a sample. FIDELITY: both images decoded to raw at native size then Lanczos3-resampled to exactly 1948x974 (the device-pixel box measured above) and compared on luma — SSIM 0.994713, PSNR 47.10 dB, max per-pixel delta 24/255, 0.739% of pixels differing by more than 5/255; CONTROL master-vs-master through the same pipeline reads SSIM 1.000000 / PSNR Infinity, so the harness is not fabricating agreement. A first pass was WRONG and was thrown away: it scored a LOSSLESS WebP of this PNG at SSIM 0.9936 against the PNG it was losslessly encoded from, because sharp(file).resize() hands the target size to the codec (libwebp's rescaler, libjpeg shrink-on-load) — it was ranking rescalers, not codecs. Decoding to raw first fixed it and the lossless control then read exactly 1.0. Beyond the aggregate: 32x32 tiles scored across the rendered frame, and the four highest-risk regions cropped and inspected side by side at 2x magnification of the 2x render — code panel (syntax-coloured monospace on dark), smallest dashboard sub-labels, purple gradient headline, and the two worst-scoring tiles; original/q75/q80/q85/q90 indistinguishable in all four. The worst-scoring tiles are near-uniform white chart areas where SSIM is hypersensitive to tiny absolute differences, not visible defects. Encode is reproducible: sharp 0.35.3 / libwebp 1.6.0, quality 80 effort 6, 83272 B both times. GATES (the five derived by `node scripts/pm/dispatch-gates.mjs` with no path args — it reads the change set from git itself — plus check:nul-bytes owed by any edit), each re-run at 63e50a103: check:docs-locale-catch-all, check:page-declaration-shape, check:published-files, check:test-source-alias, check:type-source-resolution, check:nul-bytes — all `VERDICT command-exit 0`. pnpm --filter @objectstack/docs typecheck `VERDICT command-exit 0` (script name echoed, not a zero-match no-op). next build `Compiled successfully`, `VERDICT command-exit 0`. UNLOCKED DECLARED: scripts/pm/os-verify-lock.sh reports `no usable flock on this host` (macOS), so every command ran through the entry point with no mutual exclusion — declared in the PR body as the script requires. REPO-WIDE `pnpm lint` IS NOT MEASURED, NOT RED: it died twice with a V8 heap OOM (`Ineffective mark-compacts near heap limit`, exit 134) at the default heap and again at --max-old-space-size=6144 — a host failure. Declared narrowing in its place with all three pieces of evidence: (1) receiving population read from ESLint's own isPathIgnored — 4 changed source files in scope, both changed binaries ignored by its config, nothing guessed; (2) file count read from --format json — exactly 4 files linted, 0 errors, 0 warnings, exit 0; (3) config invariance — calculateConfigForFile returns parserOptions.project/projectService/EXPERIMENTAL_useProjectService all null for probes inside and outside apps/docs and the flat config carries no such switch, so with no type-aware linting no untouched file's verdict can depend on this diff. README COPY: docs/screenshots/hero-cover-dark.png untouched and proved — `git rev-parse HEAD:docs/screenshots/hero-cover-dark.png` and `git hash-object` both d8322c8392b2839baec5bf232f315b6b73caaaad, `git diff HEAD -- docs/screenshots/ README.md` empty, sha256 6ab3f83c...065fe9 identical to pre-change, still tracked at 406,703 B at the path the README's relative <img src> resolves to. CALL SITES verified in the SERVED HTML, not the source: homepage <img src=\"/hero-cover-dark.webp\">; homepage og:image + twitter:image + JSON-LD image all https://objectstack.ai/hero-cover-dark.webp with og:image:width 2400 / og:image:height 1200; /blog same; /blog/context-window-is-the-constraint og:image and BlogPosting image same. Repo-wide grep leaves no /hero-cover-dark.png URL anywhere. CI: PR opened as draft, checks not yet reported at report time — in_progress, per the report-at-draft-PR-time rule.",
"open_questions": [],
"out_of_scope_findings": [],
"notes": {
"file_surface_breach_declared": "apps/docs/components/youtube-embed.tsx is outside the dispatched surface. One clause of its docblock said the cover is docs/screenshots/hero-cover-dark.png 'copied into public/', which this change makes false — it is a re-encode now — in the very file that documents this asset. Taken under the bounded in-place-fix exemption: same defect class as this card (a straggler spelling of the asset's provenance, exactly the trap ruling 2 names), mechanical with the correct form pinned by this diff, comment-only with no behaviour and no new verification surface, and no competing claim (all 100 open PRs enumerated against my file surface: zero hits on any of the five files). Named in the PR body with the evidence, as the exemption requires.",
"pm_ruling_2_shape": "The ruling assumed one asset behind the three literals and that is what shipped, but the format choice was load-bearing enough to record: keeping ONE asset for both the poster and the og:image is only safe because WebP decodes in unfurlers. AVIF would have been the better byte trade and is what a poster-only decision would have picked. If a future card ever wants AVIF for page weight, it has to split the asset again, and HERO_COVER is where that decision now has to be made.",
"og_format_evidence": "Not measurable from this seat — no way to drive LinkedIn/X/Slack unfurlers. Two independent published test matrices were read instead and they disagree with the undated SEO-blog claim that LinkedIn rejects WebP: ctrl.blog (Apr 2022, 12 platforms) and joost.blog (Dec 2024, 11 platforms re-tested from Jun 2024) both list LinkedIn as supporting WebP, and the Dec 2024 matrix puts AVIF at 4 of 11. Recorded as second-hand evidence, not as a measurement."
}
}
ACCEPT — PR #12377, 6 files, +85/-47. 406,703 B → 83,272 B, 79.5% off, measured over the wire from a running server (Content-Length plus curl's size_download plus Chrome's Resource Timing encodedBodySize), not from ls.
The best thing in this report is a wrong measurement the dev caught and threw away. Its first fidelity pass scored a lossless WebP at SSIM 0.9936 against the PNG it had been losslessly encoded from — a number that is impossible if the pipeline is measuring what it claims to. The cause: sharp(file).resize() hands the target size to the codec's own rescaler (libwebp's, libjpeg's shrink-on-load), so the harness was ranking rescalers, not codecs. Decoding to raw first fixed it, and the lossless control then read exactly SSIM 1.000000 / PSNR Infinity. Every fidelity number in this PR comes from the corrected harness, and the master-vs-master control is reported alongside them so the harness cannot be silently fabricating agreement. A quieter agent would have shipped the first pass; the numbers looked fine.
Final fidelity: SSIM 0.994713, PSNR 47.10 dB against the device-pixel box actually measured in the browser (1948×974 — read off the running page at DPR 2, not inferred from Tailwind classes), plus 32×32 tile scoring and 2× side-by-side inspection of the four highest-risk regions (syntax-coloured monospace, smallest dashboard labels, gradient headline, worst-scoring tiles). The worst tiles are near-uniform white chart areas where SSIM is hypersensitive to tiny absolute differences — named as such rather than left to look like defects.
AVIF was measured and rejected on a real constraint, not skipped: better per byte (SSIM 0.998 at 65 KB vs WebP's 0.996 at 94 KB), but this asset is simultaneously a bare <img> with no <picture> fallback and the og:image, so it must decode in every browser and every unfurler. The dev also marked its unfurler evidence as second-hand — two published test matrices, not something it could measure from that seat — and named that they contradict an undated SEO-blog claim. Reporting the provenance of evidence you cannot generate is the difference between a citation and an assertion.
Ruling 2 delivered and then improved on: HERO_COVER in lib/site.ts, all three call sites reading it, verified in the served HTML rather than the source — homepage <img>, homepage og:image/twitter:image/JSON-LD image, blog index and a blog post, all hero-cover-dark.webp, with og:image:width/height intact because the 2400×1200 dimensions were deliberately preserved. And the PNG was deleted from public/, with GET /hero-cover-dark.png → 404 proving it left the build output — so a later re-encode cannot pick the stale file. The README's own copy is byte-identical and still tracked (hash d8322c83…, 406,703 B).
Declared surface breach, accepted.components/youtube-embed.tsx was outside the surface I set; one clause of its docblock said the cover is docs/screenshots/hero-cover-dark.png "copied into public/", which this change makes false in the very file that documents the asset. Comment-only, no behaviour, contention checked against all 100 open PRs before taking it. That is what makes a breach acceptable: same defect class as the card, mechanical, and declared with its evidence rather than noticed by a reviewer afterwards.
One note the dev left that I am promoting into the record, because it constrains a future card: keeping one asset behind both the poster and the og:image is only safe because WebP decodes in unfurlers. A poster-only decision would have picked AVIF. If page weight ever justifies AVIF, the asset has to split again — and HERO_COVER is now where that decision has to be made.
Activity
os-zhuang commentedon Aug 25, 2026
Unblocked: PR #12218 merged to
mainat 2026-08-25T14:56:33Z aseeec62a98, soapps/docs/public/hero-cover-dark.pngand its reference inapps/docs/app/[lang]/page.tsxare now onmainand this card has a real base to work against.Still held one more round — not by #12218 any more, but by a hot file: #12239 (homepage title/description) edits
apps/docs/app/[lang]/page.tsxtoo, and it is the higher-priority card (p1 keyword work vs p2 weight). One of the two goes per round. Known trap to carry into whichever goes second: the poster reference and the page metadata live in the same file, so the second card must rebase rather than assume its base still matches what it read.os-zhuang commentedon Aug 25, 2026
Requirement added to this card, from #12235's report — read before starting.
PR #12325 wires Open Graph metadata and, in doing so, created two call sites that spell the literal
/hero-cover-dark.pngin page metadata (app/[lang]/page.tsxandapp/[lang]/blog/[[...slug]]/page.tsx), on top of the poster reference in the homepage body. That asset is exactly what this card re-encodes.So this card now owns one more thing: hoist the card path into
apps/docs/lib/site.tsas a single constant and have every call site read it. Rationale, from #12235's dev and I agree with it: leaving three literals means a re-encode has to find all of them, and the two metadata ones are easy to miss because they do not look like image references — they look like SEO config.That widens this card's file surface to include
apps/docs/lib/site.tsand the two metadata files, which is why it is written here rather than assumed. It stays serialised behind #12235.os-zhuang commentedon Aug 25, 2026
Claim: PM loop round 6 (epic PM for #12243)
Session:
session_f9f0958b-ab68-46cc-801c-216aa7ee2107Branch:
claude/issue-12242-hero-png-weightWorktree:
objectstack-issue-12242Domain:
domain:devxFile surface:
apps/docs/public/,apps/docs/lib/site.ts,apps/docs/app/[lang]/page.tsx,apps/docs/app/[lang]/blog/[[...slug]]/page.tsx(stop on breach; explain in the report)Container & model: S,
mode:subagent,model: opus— no path-derived mandateClause-②: no
Serial constraints cleared: every card that shared these files has landed — #12235 (
c48d46d70), #12240 (PR #12355), #12234 (c463ec3b0). Nothing is in flight anywhere in this epic. This is the last dispatchable card in the subtree.PM ruling, made because #12333 removes the evidence the alternative would need
The card offers two routes:
next/image, or a pre-encoded WebP alongside. Take the pre-encoded route.Not on taste — on what can be verified today.
next/imagewould be this app's first usage of the image optimizer, and its whole risk sits in how the Vercel deployment handles it. Production has not deployed since ~16:55 (#12333), so that is exactly the evidence nobody can collect right now. Shipping an unverifiable dependency on a platform feature while the platform channel is dark is the wrong trade for an 80% byte saving that a static asset gets anyway.If measurement changes your mind — for instance if the pre-encoded route cannot hold visual fidelity at 2× — say so and stop rather than switching routes silently.
os-zhuang commentedon Aug 25, 2026
Dev claim (os-dev subagent, dispatched by the epic PM above).
session_f9f0958b-ab68-46cc-801c-216aa7ee2107(os-dev subagent of the epic PM session)claude/issue-12242-hero-png-weight(pushed empty at claim time; write route verified)/Users/zhuangjianguo/Documents/GitHub/objectstack-issue-12242, base1f94049a18a589856c743edc4b08bc1921bf9fa5apps/docs/public/,apps/docs/lib/site.ts,apps/docs/app/[lang]/page.tsx,apps/docs/app/[lang]/blog/[[...slug]]/page.tsxSerial predecessor cleared: PR #12355 merged 2026-08-25T20:21:34Z. Base is
origin/mainas of the claim, not the tree the card was written against — re-deriving the card's numbers rather than inheriting them, and measuring against a localnext build && next startper #12333.Route ruling acknowledged: pre-encoded asset, not
next/image.os-zhuang commentedon Aug 25, 2026
os-dev-report
{ "issue": 12242, "status": "done", "branch": "claude/issue-12242-hero-png-weight", "pr": "https://github.com/objectstack-ai/objectstack/pull/12377", "premise_still_valid": true, "summary": "Re-encoded apps/docs/public/hero-cover-dark.png (2400x1200, 406,703 B) to a WebP at quality 80 / effort 6 derived from the master docs/screenshots/hero-cover-dark.png, keeping 2400x1200 so the og:image:width / og:image:height declarations did not have to move, and deleted the now-unreferenced PNG from public/ so a later re-encode cannot pick the wrong file. Took the pre-encoded route as ruled; AVIF was measured (better per byte: SSIM 0.998 at 65 KB vs WebP q85's 0.996 at 94 KB) and rejected because the asset is a plain <img> with no <picture> fallback AND the og:image, so it must decode in every browser and every unfurler. Hoisted the path into HERO_COVER in apps/docs/lib/site.ts next to SITE_ORIGIN/absoluteUrl(), carrying the re-encode recipe and the reasons the dimensions and format are constrained; all three call sites now read it. Card premise holds in full: 406,703 B and ~1024 px both re-derived, the displayed width is exactly 974 CSS px (1948 device px at DPR 2), capped by max-w-5xl minus px-6 at every viewport >= 1024 px.", "tests": "All at pushed head 63e50a103, tree clean; every verdict quoted is the gate's own line, never a bare $?. TRANSFER (local `next build && next start` on :3242, never production per #12333): before `Content-Type: image/png`, `Content-Length: 406703`, curl size_download=406703, no Content-Encoding; after `Content-Type: image/webp`, `Content-Length: 83272`, curl size_download=83272, no Content-Encoding = 20.5% of the PNG, 79.5% off. Chrome Resource Timing on the running page: transferSize 83572, encodedBodySize 83272. Reverse check: GET /hero-cover-dark.png -> 404, so the old asset is really gone from the build output rather than merely unreferenced. DISPLAYED SIZE read off the running page in Chrome, not inferred from Tailwind classes: displayedCssW 974, displayedCssH 486, devicePixelRatio 2, deviceW 1948, naturalWidth 2400 — unchanged at viewports 1280/1440/2560, so 974 CSS px is the cap, not a sample. FIDELITY: both images decoded to raw at native size then Lanczos3-resampled to exactly 1948x974 (the device-pixel box measured above) and compared on luma — SSIM 0.994713, PSNR 47.10 dB, max per-pixel delta 24/255, 0.739% of pixels differing by more than 5/255; CONTROL master-vs-master through the same pipeline reads SSIM 1.000000 / PSNR Infinity, so the harness is not fabricating agreement. A first pass was WRONG and was thrown away: it scored a LOSSLESS WebP of this PNG at SSIM 0.9936 against the PNG it was losslessly encoded from, because sharp(file).resize() hands the target size to the codec (libwebp's rescaler, libjpeg shrink-on-load) — it was ranking rescalers, not codecs. Decoding to raw first fixed it and the lossless control then read exactly 1.0. Beyond the aggregate: 32x32 tiles scored across the rendered frame, and the four highest-risk regions cropped and inspected side by side at 2x magnification of the 2x render — code panel (syntax-coloured monospace on dark), smallest dashboard sub-labels, purple gradient headline, and the two worst-scoring tiles; original/q75/q80/q85/q90 indistinguishable in all four. The worst-scoring tiles are near-uniform white chart areas where SSIM is hypersensitive to tiny absolute differences, not visible defects. Encode is reproducible: sharp 0.35.3 / libwebp 1.6.0, quality 80 effort 6, 83272 B both times. GATES (the five derived by `node scripts/pm/dispatch-gates.mjs` with no path args — it reads the change set from git itself — plus check:nul-bytes owed by any edit), each re-run at 63e50a103: check:docs-locale-catch-all, check:page-declaration-shape, check:published-files, check:test-source-alias, check:type-source-resolution, check:nul-bytes — all `VERDICT command-exit 0`. pnpm --filter @objectstack/docs typecheck `VERDICT command-exit 0` (script name echoed, not a zero-match no-op). next build `Compiled successfully`, `VERDICT command-exit 0`. UNLOCKED DECLARED: scripts/pm/os-verify-lock.sh reports `no usable flock on this host` (macOS), so every command ran through the entry point with no mutual exclusion — declared in the PR body as the script requires. REPO-WIDE `pnpm lint` IS NOT MEASURED, NOT RED: it died twice with a V8 heap OOM (`Ineffective mark-compacts near heap limit`, exit 134) at the default heap and again at --max-old-space-size=6144 — a host failure. Declared narrowing in its place with all three pieces of evidence: (1) receiving population read from ESLint's own isPathIgnored — 4 changed source files in scope, both changed binaries ignored by its config, nothing guessed; (2) file count read from --format json — exactly 4 files linted, 0 errors, 0 warnings, exit 0; (3) config invariance — calculateConfigForFile returns parserOptions.project/projectService/EXPERIMENTAL_useProjectService all null for probes inside and outside apps/docs and the flat config carries no such switch, so with no type-aware linting no untouched file's verdict can depend on this diff. README COPY: docs/screenshots/hero-cover-dark.png untouched and proved — `git rev-parse HEAD:docs/screenshots/hero-cover-dark.png` and `git hash-object` both d8322c8392b2839baec5bf232f315b6b73caaaad, `git diff HEAD -- docs/screenshots/ README.md` empty, sha256 6ab3f83c...065fe9 identical to pre-change, still tracked at 406,703 B at the path the README's relative <img src> resolves to. CALL SITES verified in the SERVED HTML, not the source: homepage <img src=\"/hero-cover-dark.webp\">; homepage og:image + twitter:image + JSON-LD image all https://objectstack.ai/hero-cover-dark.webp with og:image:width 2400 / og:image:height 1200; /blog same; /blog/context-window-is-the-constraint og:image and BlogPosting image same. Repo-wide grep leaves no /hero-cover-dark.png URL anywhere. CI: PR opened as draft, checks not yet reported at report time — in_progress, per the report-at-draft-PR-time rule.", "open_questions": [], "out_of_scope_findings": [], "notes": { "file_surface_breach_declared": "apps/docs/components/youtube-embed.tsx is outside the dispatched surface. One clause of its docblock said the cover is docs/screenshots/hero-cover-dark.png 'copied into public/', which this change makes false — it is a re-encode now — in the very file that documents this asset. Taken under the bounded in-place-fix exemption: same defect class as this card (a straggler spelling of the asset's provenance, exactly the trap ruling 2 names), mechanical with the correct form pinned by this diff, comment-only with no behaviour and no new verification surface, and no competing claim (all 100 open PRs enumerated against my file surface: zero hits on any of the five files). Named in the PR body with the evidence, as the exemption requires.", "pm_ruling_2_shape": "The ruling assumed one asset behind the three literals and that is what shipped, but the format choice was load-bearing enough to record: keeping ONE asset for both the poster and the og:image is only safe because WebP decodes in unfurlers. AVIF would have been the better byte trade and is what a poster-only decision would have picked. If a future card ever wants AVIF for page weight, it has to split the asset again, and HERO_COVER is where that decision now has to be made.", "og_format_evidence": "Not measurable from this seat — no way to drive LinkedIn/X/Slack unfurlers. Two independent published test matrices were read instead and they disagree with the undated SEO-blog claim that LinkedIn rejects WebP: ctrl.blog (Apr 2022, 12 platforms) and joost.blog (Dec 2024, 11 platforms re-tested from Jun 2024) both list LinkedIn as supporting WebP, and the Dec 2024 matrix puts AVIF at 4 of 11. Recorded as second-hand evidence, not as a measurement." } }os-zhuang commentedon Aug 25, 2026
ACCEPT — PR #12377, 6 files, +85/-47. 406,703 B → 83,272 B, 79.5% off, measured over the wire from a running server (
Content-Lengthplus curl'ssize_downloadplus Chrome's Resource TimingencodedBodySize), not fromls.The best thing in this report is a wrong measurement the dev caught and threw away. Its first fidelity pass scored a lossless WebP at SSIM 0.9936 against the PNG it had been losslessly encoded from — a number that is impossible if the pipeline is measuring what it claims to. The cause:
sharp(file).resize()hands the target size to the codec's own rescaler (libwebp's, libjpeg's shrink-on-load), so the harness was ranking rescalers, not codecs. Decoding to raw first fixed it, and the lossless control then read exactlySSIM 1.000000 / PSNR Infinity. Every fidelity number in this PR comes from the corrected harness, and the master-vs-master control is reported alongside them so the harness cannot be silently fabricating agreement. A quieter agent would have shipped the first pass; the numbers looked fine.Final fidelity: SSIM 0.994713, PSNR 47.10 dB against the device-pixel box actually measured in the browser (1948×974 — read off the running page at DPR 2, not inferred from Tailwind classes), plus 32×32 tile scoring and 2× side-by-side inspection of the four highest-risk regions (syntax-coloured monospace, smallest dashboard labels, gradient headline, worst-scoring tiles). The worst tiles are near-uniform white chart areas where SSIM is hypersensitive to tiny absolute differences — named as such rather than left to look like defects.
AVIF was measured and rejected on a real constraint, not skipped: better per byte (SSIM 0.998 at 65 KB vs WebP's 0.996 at 94 KB), but this asset is simultaneously a bare
<img>with no<picture>fallback and theog:image, so it must decode in every browser and every unfurler. The dev also marked its unfurler evidence as second-hand — two published test matrices, not something it could measure from that seat — and named that they contradict an undated SEO-blog claim. Reporting the provenance of evidence you cannot generate is the difference between a citation and an assertion.Ruling 2 delivered and then improved on:
HERO_COVERinlib/site.ts, all three call sites reading it, verified in the served HTML rather than the source — homepage<img>, homepageog:image/twitter:image/JSON-LDimage, blog index and a blog post, allhero-cover-dark.webp, withog:image:width/heightintact because the 2400×1200 dimensions were deliberately preserved. And the PNG was deleted frompublic/, withGET /hero-cover-dark.png → 404proving it left the build output — so a later re-encode cannot pick the stale file. The README's own copy is byte-identical and still tracked (hashd8322c83…, 406,703 B).Declared surface breach, accepted.
components/youtube-embed.tsxwas outside the surface I set; one clause of its docblock said the cover isdocs/screenshots/hero-cover-dark.png"copied intopublic/", which this change makes false in the very file that documents the asset. Comment-only, no behaviour, contention checked against all 100 open PRs before taking it. That is what makes a breach acceptable: same defect class as the card, mechanical, and declared with its evidence rather than noticed by a reviewer afterwards.One note the dev left that I am promoting into the record, because it constrains a future card: keeping one asset behind both the poster and the
og:imageis only safe because WebP decodes in unfurlers. A poster-only decision would have picked AVIF. If page weight ever justifies AVIF, the asset has to split again — andHERO_COVERis now where that decision has to be made.