Skip to content

Blog SVG drop shadows cut off by their filter region in 12 SVGs (hard edges at the fold), and placeholder "concept diagram" alt text in 5 locales of 4 posts #224

Closed

Description

@objectstack-fleet

Found while #221 was staged on 2026-10-06 (main @ 53764e0), which puts it in the same family as #221. The seat confirmed item 1 by viewing the crop. Priority: P2. Covers sit at the fold.

1. Drop shadows cut off by their filter region

In 12 SVGs, 35 filtered elements have their feDropShadow clipped by the filter region, within 1.5σ of the shadow edge.

  • The filter is set to height=150% on a 120px card with dy16 σ22, which leaves a hard horizontal edge about 60px under the card.
  • Example: /en/blog/natural-language-to-app-metadata/, under the "Natural language" card.

Affected SVGs:

  • enterprise-ontology-race-open-vs-closed cover and cover-en
  • why-ai-agent-pilots-fail-four-layers cover and cover-en
  • the automation-* covers
  • objectos-automation-engine cover-en and run-record-en
  • natural-language-to-app-metadata cover and cover-en
  • ontology-vs-semantic-layer cover-en

Fix: enlarge the filter region (for example x=-50% y=-50% width=200% height=200%, or explicit userSpaceOnUse bounds). Extend #221's svg-text-fit gate, or add a sibling check, so a shadow cut inside its own canvas fails.

2. Placeholder alt text in five locales

#221 rewrites the en and zh-Hans alt text of the decorative concept images in 4 posts. The de, es, fr, ja and ko versions still say "Konzeptdiagramm", "Diagrama conceptual", "Schéma conceptuel", 「概念図」 and "개념 다이어그램": 20 mdx lines in total.

The posts:

  • crm-ai-understands-customers
  • extend-existing-systems-with-ai
  • low-code-vs-ai-native-app-platform
  • manufacturing-legacy-systems-ai

Fix: translate #221's descriptive alt text into each locale, following AGENTS.md Localization.

Done looks like

  • Every shadow is fully inside its filter region, with before/after native-size renders that have been looked at.
  • The gate is red on today's files and green after the fix.
  • The 20 alt lines describe the image in their own language.
  • Gates are green.

Order

After #221 lands.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop, repo:www.objectos.ai seat
    Session: session_01FeA1nwBz1ohH65dvffUGKr
    Account: hotlong (the seat's linked user, as GET /user answers it; always the card's assignee)
    Branch: claude/pm-dispatch-objectos-ju9td1, the session's one designated push branch for this repo. It carries PR #212 until its Thu 2026-10-08 slot. After that, wave 1 goes in this order: #216, #213, #217, #221, then the #209 post sweep. All five are staged and reviewed.
    This card is staged in wave 2. Its work is built and verified on a local branch cut from the local integration branch stack/www-wave1 (origin/main with the five wave-1 branches merged in queue order, no conflicts). At push time only this card's own commits are replayed onto origin/main (rebase --onto).
    Worktree: www-issue-224
    Domain: n/a (single-lane repo)
    Seat: repo:www.objectos.ai (objectstack-ai/objectstack#12224)
    File surface: as the card's Surface section names. Overlaps with the other wave-2 cards (#218, #219, #224, #225) stay in separate hunks.
    Stop on breach and explain in the report.
    Container & model: M, mode:subagent, model: default.
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: no. Pushes are serial on the one branch, in the queue order on #209 6010716407, with wave 1 first.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 224,
     "status": "staged",
     "branch": "staged/issue-224",
     "worktree": "/home/user/www-issue-224",
     "base": "7f69d97 (local stack/www-wave1: origin/main 53764e0 + staged #216, #213, #217, #221, #209-sweep)",
     "final_commit": "cee0607",
     "commits": [
      "38305d2 build: fail a drop shadow cut off by its filter region; grow 27 regions to hold theirs",
      "cee0607 content(blog): translate the four concept-illustration alt texts into de, es, fr, ja and ko"
     ],
     "replay": "Both commits are self-contained on top of stack/www-wave1 and replay with rebase --onto origin/main stack/www-wave1 once wave 1 lands. 38305d2 edits #221's scripts/svg-text-fit.mjs, so it needs #221 landed as staged. cee0607 touches only the 20 alt lines; the 4 ja lines are the only contact with #225's CJK surface.",
     "pr": null,
     "pr_body_draft": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-224/pr-body.md (first line: Fixes #224)",
     "session": "session_01FeA1nwBz1ohH65dvffUGKr — the parent's id (this is a subagent run)",
     "premise_still_valid": true,
     "summary": "Both card items are done and verified at cee0607. Item 1: the card's numbers reproduced exactly: 35 elements in the same 12 SVGs are cut within 1.5σ of the shadow's edge. Measuring every filtered element (181 in 78 SVGs) against the full 3σ shadow reach found 81 cut elements in 27 SVGs. Each cut filter's objectBoundingBox region was grown, never shrunk, to hold the 3σ reach of every element that uses it, rounded out to 10%: one line per SVG, 27 lines. In headless Chromium every filtered SVG now renders pixel-identically to the same SVG with unbounded regions (0 of 78 differ). Before and after differ in exactly 14 SVGs (the card's 12 plus beyond-agentforce cover and cover-en at a 1-level step), and only by the pixels the unbounded region adds. The gate: #221's svg-text-fit.mjs gains rule 3 in place. It fails a filtered element whose shadow's 3σ reach crosses its filter region on the canvas, and fails anything but a single feDropShadow as unmeasurable. It runs in pnpm svg:lint and pnpm build, so package.json is unchanged. Red on the stack files (81 cut shadows, 27 SVGs, exit 1); green at cee0607. The text rules print byte-identical clearances for all 2244 boxes. Item 2: all 20 placeholder alts are translated from #221's English description into de/es/fr/ja/ko, using each post's own terms; zero placeholders remain. Committed locally only: not pushed, no PR, worktree kept.",
     "files": [
      "scripts/svg-text-fit.mjs (rule 3: drop shadow inside its filter region; +193 −13 incl. header)",
      "27 SVGs, one filter element each: ai-wrote-your-app-dare-to-merge/review-loop-en.svg, ai-wrote-your-app-dare-to-merge/review-loop.svg, airtable-style-ai-builder/cover-en.svg, airtable-style-ai-builder/cover.svg, automation-cross-system-flows/cover-en.svg, automation-governed-flow-changes/cover-en.svg, automation-trigger-model/cover-en.svg, beyond-agentforce-copilot-open-runtime/cover-en.svg, beyond-agentforce-copilot-open-runtime/cover.svg, business-app-in-16k-tokens/cover-en.svg, business-app-in-16k-tokens/cover.svg, enterprise-ontology-race-open-vs-closed/cover-en.svg, enterprise-ontology-race-open-vs-closed/cover.svg, forward-deployed-engineer-tools/cover-en.svg, forward-deployed-engineer-tools/cover.svg, forward-deployed-engineer-vs-solutions-architect/cover-en.svg, mcp-governed-tool-layer/cover-en.svg, mcp-governed-tool-layer/cover.svg, mcp-governed-tool-layer/tool-call-lifecycle-en.svg, mcp-governed-tool-layer/tool-call-lifecycle.svg, natural-language-to-app-metadata/cover-en.svg, natural-language-to-app-metadata/cover.svg, objectos-automation-engine/cover-en.svg, objectos-automation-engine/run-record-en.svg, ontology-vs-semantic-layer-vs-knowledge-graph/cover-en.svg, why-ai-agent-pilots-fail-four-layers/cover-en.svg, why-ai-agent-pilots-fail-four-layers/cover.svg",
      "20 mdx alt lines: content/blog/{crm-ai-understands-customers,extend-existing-systems-with-ai,low-code-vs-ai-native-app-platform,manufacturing-legacy-systems-ai}/index.{de,es,fr,ja,ko}.mdx"
     ],
     "measurements": {
      "filtered_elements": "181 in 78 SVGs",
      "cut_at_3sigma_on_canvas": "81 elements / 27 SVGs (bottom 75, top 55, left 15, right 15 sides)",
      "cut_within_1_5sigma": "35 elements / 12 SVGs, the card's exact list",
      "cut_with_opacity_at_least_1_of_255": "41 elements / 16 SVGs",
      "chromium_pixel_change_before_vs_unbounded": "14 SVGs. Max step by SVG: why-ai-agent-pilots 15, enterprise-ontology-race 14, automation-trigger-model 8, automation-cross-system-flows 7, natural-language 7, objectos-automation-engine cover-en 6, ontology-vs-semantic-layer 4, automation-governed 3, run-record-en 3, beyond-agentforce 1. The other 13 SVGs (deepest cut at least 1.9σ) change 0 pixels.",
      "chromium_after_vs_unbounded": "0 of 78 filtered SVGs differ",
      "per_svg_table": "in pr-body.md: filter id, cut elements, deepest cut in σ, new region, Chromium step",
      "deepest_cut_before": "why-ai-agent-pilots-fail-four-layers cover and cover-en: the region's bottom edge sits 0.16σ past the cast card, where the shadow is still 6.55% opaque"
     },
     "gate_red_green": {
      "red": "final script on stack/www-wave1 7f69d97 files: 'svg-text-fit: FAIL — 81 drop shadows cut off by their filter region on the canvas (112 SVGs, 2244 text boxes, 181 filtered elements, tolerance 2px, 1 allowlisted)' exit 1",
      "green": "at cee0607: 'svg-text-fit: OK — no text box crosses the shape under it or runs under a shape painted over it, and no drop shadow is cut off by its filter region (112 SVGs, 2244 text boxes, 181 filtered elements, tolerance 2px, 1 allowlisted)' exit 0",
      "text_rules_unchanged": "the stack script and the new script print identical --verbose text clearance lines for all 2244 boxes; the old gate is OK on both trees",
      "synthetic": "scratch cases, not committed: a short card fails on 4 sides; a userSpaceOnUse region passes; an off-canvas cut is not reported; a CSS filter rule is read; feGaussianBlur and a missing id fail as unmeasurable"
     },
     "tests": "All at cee0607, clean tree, heavy runs through os-verify-lock. pnpm content:lint: '✓ content lint passed (339 files, 44 glossary terms checked)' exit 0. pnpm content:lint --published: same, exit 0. pnpm svg:lint: OK line above, exit 0. pnpm check: 'Result (143 files): 0 errors, 0 warnings, 0 hints', VERDICT command-exit 0. pnpm build: VERDICT command-exit 0; '880 page(s) built', '✓ content lint --dist passed (478 built blog pages checked)', '✓ content lint --dist links passed (89567 internal links in 880 built pages resolve to built files)'. pnpm seo:smoke: 'SEO smoke test passed (870 HTML pages checked)' exit 0. Also: all 27 changed SVGs ship byte-identical in dist/_astro (hash match); the 20 built alt attributes were read back from dist; the control-byte scan of changed files found none. The CI workflow steps (check, content:lint --published, build, seo:smoke) were all run locally.",
     "screenshots": {
      "dir": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-224/shots",
      "native": "shots/native: 27 changed SVGs × before/after × light (#f3f7f2) / dark (#101713) page = 108 renders. shots/sheets: before|after per SVG, plus native-scale crops of the changed area, plain and contrast ×6, for the 14 SVGs whose pixels change. Looked at: the bands are visible unstretched before (enterprise-ontology, why-ai, natural-language, automation-trigger) and gone after; all 27 render fine on both page themes; every changed SVG has an opaque background.",
      "pages": "shots/pages: 27 article pages (22 changed covers, 5 changed body diagrams) at 1440×750 and 360×800, light and dark, after. For the 14 visibly changed pages also before (the image request answered with the stack file): viewport plus element shots. Contact sheets: shots/contact-pages-{1440,360}-{light,dark}.png. Looked at: no layout change; the band shows at 1440 before and is gone after; at 360 the step is at most 13 levels.",
      "also": "shots/pages-list: /en/blog/ at 1440 and 360, and /de/blog/crm-ai-understands-customers/ in dark mode"
     },
     "deviations": [
      "Scope by measurement: the gate bound is 3σ ('fully inside', the bound Skia gives a blur), so 27 SVGs / 81 elements changed instead of the card's 12 / 35. 13 of them change no Chromium pixel today (deepest cut at least 1.9σ). To keep only visible fixes, lower SHADOW_REACH to 2 and revert those 13 one-line changes; the gate header records the calibration.",
      "Gate extended in place (svg-text-fit.mjs rule 3), not a sibling script: no package.json edit, which keeps out of #219's surface.",
      "Viewports: native size plus 1440×750 and 360×800, as the dispatch asked. AGENTS.md's other four (1366×625, 1512×830, 1920×945, 390×844): NOT MEASURED, reason: not requested for this staged card and no layout code changed.",
      "Commit trailers are the contract's model-free pair (Co-Authored-By: Claude, Claude-Session), not the harness reminder's model-named line. The PR draft footer is the contract's session-URL form, not the harness's 'Generated with' lines.",
      "38305d2's message was amended once, locally and before any push, to correct a wording error.",
      "Seat tooling borrowed from the objectstack checkout: os-verify-lock.sh for heavy runs, post-stamped.mjs for this comment. www has neither."
     ],
     "mcp_calls": "0",
     "api_writes": "1 — POST /repos/objectstack-ai/www.objectos.ai/issues/224/comments (this os-dev-report, via scripts/pm/post-stamped.mjs)",
     "open_questions": [
      {
       "question": "Keep the 3σ bound (27 SVGs, 13 of them with no Chromium-visible change), or lower it to the visible threshold?",
       "options": [
        "A keep 3σ: every shadow fully inside its region by the bound a blur is given, one-line changes",
        "B SHADOW_REACH = 2: revert 13 SVGs and tie the gate to today's Chromium threshold"
       ],
       "recommendation": "A. The card's done-criterion is 'every shadow fully inside', and the 13 extra lines are mechanical with zero Chromium pixel change. Engines other than Chromium were not measured, so the visible threshold is known for one engine only."
      }
     ],
     "out_of_scope_findings": [
      "class: a · reach: /en/blog/natural-language-to-app-metadata/ and /zh-Hans/… cover, and the blog list at 1440 — the Runtime card's gold ring (path 'M90 0 A90 90 0 1 1 89 0' at translate(44 82), stroke 5) reaches local y 262 on a 248px card, so it overruns the card's bottom edge by about 16px. Present before and after this PR; visible in shots/page-1440-ba-why.png and shots/pages-list/blog-list__en.after.1440.light.viewport.png · dedupe words: natural-language-to-app-metadata cover ring overflow, Runtime card arc, svg stroke past card edge",
      "carrier: none (承接者:无) · noted, not filed — svg-text-fit models fills, not strokes, so a stroked shape crossing its card is outside every rule; recorded in PR Acceptance notes"
     ]
    }
  3. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 224,
     "status": "staged",
     "addendum_to": "the os-dev-report at cee0607 (comment 6017035167); seat rulings applied: open question A (keep 3σ), and the Runtime ring finding folded in",
     "branch": "staged/issue-224",
     "worktree": "/home/user/www-issue-224",
     "base": "7f69d97 (local stack/www-wave1)",
     "final_commit": "69d9877",
     "commits": [
      "38305d2 build: fail a drop shadow cut off by its filter region; grow 27 regions to hold theirs",
      "cee0607 content(blog): translate the four concept-illustration alt texts into de, es, fr, ja and ko",
      "69d9877 content(blog/svg): keep the natural-language Runtime ring inside its card"
     ],
     "pr": null,
     "pr_body_draft": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-224/pr-body.md (first line: Fixes #224; new section 4, verification at 69d9877; the previous draft is kept as pr-body.v1.md)",
     "session": "session_01FeA1nwBz1ohH65dvffUGKr — the parent's id (this is a subagent run)",
     "premise_still_valid": true,
     "summary": "In natural-language-to-app-metadata's cover-en.svg and cover.svg, the Runtime card's gold ring (radius 90, stroke 5, centre 172px down the card) reached 264.5px down a 248px card, 16.5px past its bottom edge. The card is now 290px tall, which centres the ring under the card title with about 26px clear above and below. Ring, title, inner text and the card's top edge are unmoved: one attribute per file. In Chromium the before/after difference is confined to the card's lower edge and its shadow (x 794–1151, y 364–509). The shadow gate still passes; the card's margins are now top 7.3σ and bottom 8.5σ. No stroke rule was added. A naive prototype (a stroked, unfilled shape must stay inside the filled shape under its centre) flagged 20 shapes in 11 SVGs before the fix and 18 in 9 after. Most are connectors drawn across cards on purpose, so the rule needs a host model and is not small; the gap is written into the PR body.",
     "files": [
      "content/blog/natural-language-to-app-metadata/cover-en.svg (Runtime rect height 248 to 290)",
      "content/blog/natural-language-to-app-metadata/cover.svg (same)"
     ],
     "tests": "At 69d9877, clean tree. pnpm content:lint: '✓ content lint passed (339 files, 44 glossary terms checked)' exit 0. pnpm content:lint --published: same, exit 0. pnpm svg:lint: 'svg-text-fit: OK — no text box crosses the shape under it or runs under a shape painted over it, and no drop shadow is cut off by its filter region (112 SVGs, 2244 text boxes, 181 filtered elements, tolerance 2px, 1 allowlisted)' exit 0. pnpm check: 'Result (143 files): 0 errors, 0 warnings, 0 hints', exit 0. pnpm build: exit 0; '880 page(s) built', '✓ content lint --dist passed (478 built blog pages checked)', '✓ content lint --dist links passed (89567 internal links in 880 built pages resolve to built files)'. Each exit code was captured right after its own command, under os-verify-lock. pnpm seo:smoke: 'SEO smoke test passed (870 HTML pages checked)' exit 0. All 27 changed SVGs ship byte-identical in dist/_astro.",
     "screenshots": {
      "native": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-224/shots/r2-native: both covers before (cee0607) and after, light and dark page; ring-{cover,cover-en}-{light,dark}.png are before|after crops of the card",
      "pages": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-224/shots/r2-pages: blog-list card (en at 1440 and 360, light and dark; zh-Hans at 1440 and 360) and article cover (en at 1440 and 360, light and dark; zh-Hans at 1440 and 360), before and after. Sheets: shots/r2-list.png, shots/r2-article.png",
      "looked_at": "Before, the ring crosses the card's bottom edge in every view, including the cropped list card. After, it sits inside the card with even space above and below, and nothing else on the card or the page moves."
     },
     "deviations": [
      "Stroke rule not added (the dispatch allowed this): the prototype numbers above show it is not a small, well-bounded addition, and the PR body records the gap.",
      "The arrow into the Runtime card stays at y 292. It now points at the card's upper middle instead of near its centre; it is left in place to keep the change to one attribute."
     ],
     "mcp_calls": "0",
     "api_writes": "2 in this card — POST /repos/objectstack-ai/www.objectos.ai/issues/224/comments twice (the first report and this addendum), both via scripts/pm/post-stamped.mjs through the fleet-write relay",
     "open_questions": [],
     "out_of_scope_findings": [
      "carrier: none (承接者:无) · noted, not filed — the stroke-rule prototype's small hits (6.5px in the conversational-app-iteration covers, 5.9px in eu-ai-act-runtime-audit/cover-en.svg) were not looked at; they are listed in pr-body.md section 4"
     ]
    }
  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed and live · 2026-10-15T12:26Z


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions