Skip to content

fix(card): the eyebrow ran off the canvas, and the check on it counted bytes - #18

Merged
sshlg merged 1 commit into
mainfrom
fix/b-118-social-card
Aug 31, 2026
Merged

fix(card): the eyebrow ran off the canvas, and the check on it counted bytes#18
sshlg merged 1 commit into
mainfrom
fix/b-118-social-card

Conversation

@sshlg

@sshlg sshlg commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes board row B-118, deferred from v0.17.1 by coordinator decision.

The defect

docs/assets/social-preview.png — the image every link to this repository renders —
had its eyebrow cut off at the canvas. The line is generated from the umbrella's
role cell for this pack, 91 characters; at the smallest scale the renderer will use
that needs 1354px of a 1200px canvas, so the last 11 characters were never
drawn. The card read "…AND THE WALLET UNDER" and stopped.

Re-derived here rather than taken on report:

measurement value
accent ink in the committed PNG x 0..1199 of 1200
fitScale for the 91-char eyebrow 2 — its floor, under both metrics
pen would finish at x=1354
last fully drawn glyph index 79 of 90
lost " LLM RESALE" (11 chars)

No scale fixes it. The renderer floors at 2 and was already there, so the text
itself had to get shorter.

The new role string (60 chars)

orchestration, prompts, evals, protocols, and the LLM wallet

It names all four skills where the old one named three — agent-harness was
missing from its own pack's one-line description — and paints to x=919, leaving
196px / 19% of margin inside the content box. The eyebrow renders identically
under both fit metrics, so the fix does not depend on which one the umbrella uses.

The sensor gap is the real repair

test/social_preview.py checked the PNG signature, the chunk order, the byte count
and the dimensions — none of which is a reading about text — so it was green over a
clipped card for as long as one existed.

test/card_ink.py decodes the image and measures where ink actually lands: every
painted pixel must sit inside the content box, with exactly one deliberate exception
(the accent bar bleeding to the left edge), and that bar must be present, so a
blank or mis-decoded image cannot pass by having nothing in the gutters. Clipping
cannot hide from it — losing even one glyph means the line already painted 70px into
the right gutter.

Watched failing before it was believed:

  • the guard on the previously committed card → accent paints at x=1199, y=190 — 84px past the right edge of the content box (x=1115), and reaches the canvas edge, so characters were cut off entirely, exit 1
  • the pre-change check on that same file → OK: … 1200x630, 8686 bytes, exit 0
  • --self-test: clean card accepted, 1px past the box refused, canvas-edge ink refused, blank image refused by the positive assertion
  • new validate.yml plant: one pixel in the last column of a repo copy, plant_guard.py verify confirms it landed, entry point exits 1

Why card_ink.py is a separate module

test/social_preview.py is a shared mechanism across nine repositories, and the
umbrella's divergence check stops comparing copies below SHARED_SIMILARITY = 0.90
for all nine at once, the base being this repository's copy. Inlining a PNG decoder
there would have silently disabled that guard on eight repositories that never asked
for it. The shared file gains two lines; measured similarity to its eight siblings is
0.9133. Its docstring says this so the next reader does not merge it back.

Owed by the umbrella, not by this repository

test/site_test.js byte-compares this card against a render from skills.json, so
the umbrella is red until two edits land in the pin commit:

  1. set agent-stack's role to the 60-char string above, verbatim;
  2. remove agent-stack from LEGACY_FIT in scripts/site.js.

The second is not optional. The legacy metric renders npx skills add ssheleg/agent-stack
at scale 5, painting to x=1131 — 16px past the content box — which this release's own
new check refuses. The umbrella's own comment already names B-118 as the moment that
entry retires, and the two remaining entries still need the gate, so the exactness
fixture stays green.

Verified against the umbrella working copy: with both edits memberCardSpec +
og.card produce these exact 8342 bytes; with only the first, they do not.

Gate

Run on an isolated git worktree of this branch (a sibling agent holds B-126-kv-cache
in the shared tree, so its in-flight edits are excluded and not staged here):

  • npm testexit 0OK: agent-stack structurally valid (13 checks = 9 named + 4 per-skill, 4 skill(s), v0.18.2), PASS: plant_guard — 9 cases, PASS: installer — 11 case(s), every residue line left nothing
  • python3 test/social_preview.pyOK: docs/assets/social-preview.png is 1200x630, 8342 bytes
  • python3 test/card_ink.py --self-test → exit 0
  • python3 test/evals_validate.py + --self-test → exit 0
  • claude plugin validate . --strict and plugins/agent-stack --strict✔ Validation passed
  • version sync 0.18.2 across package.json, .claude-plugin/marketplace.json, plugins/agent-stack/.claude-plugin/plugin.json

…d bytes

docs/assets/social-preview.png -- the image every link to this repository
renders -- had its eyebrow cut off at the canvas edge. The line is generated
from the umbrella's role cell for this pack, 91 characters; at the smallest
scale the renderer will use that needs 1354px of a 1200px canvas, so the last
11 characters were never drawn. The card read "...AND THE WALLET UNDER" and
stopped.

Re-derived here rather than taken on report: decoding the committed PNG puts
accent ink at x=1199 of 1200, and replaying the renderer's own metric gives a
pen finishing at x=1354 with " LLM RESALE" past the edge. fitScale was already
at its floor of 2, so no scale fixes it -- the text itself had to shorten.

The role string is now 60 characters:

  orchestration, prompts, evals, protocols, and the LLM wallet

It names all four skills where the old one named three (agent-harness was
missing from its own pack's one-line description), and it paints to x=919 --
196px, 19%, of margin inside the content box. The umbrella owns that cell; this
ships the card rendered from it.

The sensor gap is the real repair. test/social_preview.py checked the PNG
signature, the chunk order, the byte count and the dimensions, and was green
over a clipped card for as long as one existed, because none of those readings
is about text. test/card_ink.py decodes the image and measures where ink lands:
every painted pixel inside the content box, one deliberate exception (the
accent bar bleeding to the left edge) which must be PRESENT, so a blank or
mis-decoded image cannot pass by having nothing in the gutters.

Watched failing before it was believed. The guard refuses the previously
committed card by name -- "accent paints at x=1199, y=190 -- 84px past the
right edge of the content box ... and reaches the canvas edge, so characters
were cut off entirely" -- while the old check printed OK on that same file. A
new negative self-test in validate.yml plants one pixel in the last column of a
repo copy and requires the entry point to refuse it, with plant_guard.py verify
confirming the plant landed.

card_ink.py is a separate module on purpose. social_preview.py is shared by
nine repositories and the umbrella stops comparing copies below 0.90
similarity -- for all nine at once, the base being this repo's copy. Inlining a
PNG decoder there would have disabled that guard on eight repositories that
never asked for it. The shared file gains two lines: measured 0.9133.

Owed by the umbrella, not by this repo: test/site_test.js byte-compares this
card against a render from skills.json, so two edits are needed in the pin
commit -- set agent-stack's role to the string above, AND remove agent-stack
from LEGACY_FIT in scripts/site.js. The second is not optional: the legacy
metric renders the npx line at scale 5, painting to x=1131, which this
release's own new check refuses. Verified: with both edits the umbrella
generates these exact 8342 bytes; with only the first it does not.

B-118, C-01..C-08. Gate: npm test rc=0, plugin validate --strict passed.

Co-authored-by: NGHTBOY <NGHTBOY@sshlg.me>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sshlg
sshlg merged commit 62bb910 into main Aug 31, 2026
2 checks passed
@sshlg
sshlg deleted the fix/b-118-social-card branch August 31, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant