Skip to content

Layout design debt on the marketing template: CTA below the laptop fold on 14 of 15 pages, CJK headline word splits, lone cards in fixed 3-column grids, phone-width decision tables cut off #223

Closed

Description

@objectstack-fleet

Found by the seat's site-wide audit of www.objectos.ai on 2026-10-06. The audit ran 444 runs: 60 pages × 6 viewports, plus dark mode on 14 pages. Priority: P1 for items 1–3, P2 for the rest.

1. CTA below the laptop fold

  • At 1366×625 (en), the primary CTA is below the fold on 14 of 15 marketing pages, with its bottom at 626–867px.
  • The marketing h1 is 69.7px and wraps to 3–5 lines (MarketingEvidencePage.astro:233, clamp(2.9rem,5.1vw,4.45rem)).
  • Home:
    • The CTA sits at y=1207 at 1366 and at y=1256 at 1920×945. The fold shows only the H1 and the top half of the video poster, so the lead paragraph is off-screen too.
    • The sticky header CTA softens this but doesn't replace it.
  • Fix: add a vh term to the h1 size, as home already has. On home at short heights, move the lead paragraph and CTA above the video.

2. CJK headlines split words across lines

  • ja: the home H1 at 390 and 360 breaks 「ソフトウェ / ア」, and the eyebrow leaves 「境」 alone on a line.
  • zh-Hans:
    • the platform H1 breaks 「可投 / 入生产」 at 1366 and 360;
    • other splits: 「开 / 放」, 「一整 / 个」 and 「让人来 / 审」.
  • Contributing: .hero h1 strong{overflow-wrap:anywhere}.
  • Fix: word-break:auto-phrase for ja (with a fallback), keep-together spans or <wbr> in zh headline copy, and text-wrap:balance on h1/h2.

3. Lone cards in fixed 3-column grids

  • .item-grid is fixed at 3 columns (MarketingEvidencePage.astro:438-442).
  • 23 of 37 grids per locale end on an uneven row. Twelve of those are 4-card sections that leave one card with two-thirds of the row empty, e.g. FDE "4. Govern and leave" and platform "The review loop".
  • The glossary index has 22 terms, leaving "Vibe coding" alone.
  • Fix: choose columns by item count (4 → 2×2 or 4-up).

4. Decision table cut off on phones

5. One-word orphans in headings

  • "What changes, who reviews it, what runs" leaves "runs" alone at every desktop width on all 15 marketing pages.
  • Home has "agents." and "inside" alone on their lines.
  • Forced <br> in home h2s leaves lines like "model," on phones.

6. Home video poster

  • The poster text is English on zh, ja and de.
  • At 360 it is scaled to 0.26, so its sub-lines render at about 5px.

7. Typography

  • Display type mixes serif (home, marketing, glossary) with sans (pricing, security, legal, cluster).
  • Pick one per role and state it in the PR.

Done looks like

  • Each item:
    • fixed, or answered in one line in the PR;
    • before/after screenshots at the six viewports, light and dark, en, zh-Hans and ja, looked at.
  • Fold measurement at 1366×625: the primary CTA is above the fold on every marketing page and on home.
  • Gates: green.

Surface

src/components/{MarketingEvidencePage,MarketingHome}.astro, the CSS for the glossary index and pricing/security/legal headings, and the headline copy in content/home.ts and content/pages/zh-Hans/*.ts (keep-together markup only).

Order

Last in this batch, after the copy sweep.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 7, 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. This card is last in the batch, as its Order section says. It is staged on the top of the seat's local linear stack, lin/issue-222 (3a531e1): wave 1, then #218, #219, #220, #224, #225, #226, #227 and #222, replayed in push order, each tip built and gated. At push time only this card's own commits are replayed.
    Worktree: www-issue-223
    Domain: n/a (single-lane repo)
    Seat: repo:www.objectos.ai (objectstack-ai/objectstack#12224)
    File surface: the card's own Surface section:

    • src/components/{MarketingEvidencePage,MarketingHome}.astro;
    • the CSS for the glossary index and the pricing, security and legal headings;
    • the headline copy in content/home.ts and content/pages/zh-Hans/*.ts, keep-together markup only.

    Two defects found by the stack's integration run are added:

    • the near-white media-note box over the home hero image in dark mode;
    • the video's play button covering the poster title at 390 and 360 (src/components/VideoEmbed.astro).

    No wording changes.
    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, and this card is last.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 223,
      "status": "staged",
      "branch": "lin/issue-223",
      "worktree": "/home/user/www-issue-223",
      "base_sha": "3a531e1f8ea22622f2567f5829d6c02debfc8751",
      "final_commit": "42d55d9a699f63dac008f592eabb0cfa7cf288ef",
      "commits": [
        "af4d37c site(layout): primary CTA above the laptop fold on every marketing page and home",
        "e30eb2b site(layout): card grids lay out by count, so no section ends on a lone card",
        "a1ad8a6 site(layout): CJK headings break between phrases, never inside a word",
        "1303e30 site(layout): balanced headings, no one-word last lines",
        "ef80a08 site(layout): one face per heading role",
        "f63896e site(home): the hero image's media note takes the dark surface in dark mode",
        "42d55d9 site(home): video poster words are localized HTML, legible on phones and clear of the play button"
      ],
      "pr": null,
      "pr_body_draft": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/pr-body.md (first line: Fixes #223)",
      "session": "session_01FeA1nwBz1ohH65dvffUGKr",
      "premise_still_valid": true,
      "summary": "All seven items and the two Claim-added defects are answered on lin/issue-223, in 7 commits over 3a531e1, no copy changed. At 1366x625 the primary CTA is above the fold on all 15 marketing pages and home in en, zh-Hans and ja (worst 613, was 899; home 550/550/609, was 1207/1175/1271). CJK headings break only between phrases (wbr element plus word-break: keep-all, built from Intl.Segmenter): ja word splits 61 -> 0, zh-Hans 241 -> 0, CJK one-character last lines 120 -> 0, en one-word last lines 146 -> 0. Card grids lay out by count (no lone card). Item 4 was already done in the stack by c9d4d71. One face per heading role. The poster title is localized from existing captions and legible on phones, with the play badge clear of it. Premise holds, with two drifts: the card's content/home.ts is src/lib/home.ts (no copy file was edited), and item 4 needed no change.",
      "files_changed": [
        "src/components/MarketingEvidencePage.astro",
        "src/components/MarketingHome.astro",
        "src/components/VideoEmbed.astro",
        "src/components/SecurityPage.astro",
        "src/components/GlossaryIndex.astro",
        "src/components/Pricing.astro",
        "src/components/LegalPage.astro",
        "src/components/ClusterPage.astro",
        "src/lib/cjk-phrases.ts (new)",
        "src/lib/card-rows.ts (new)",
        "public/images/objectstack-overview-poster.svg -> public/images/objectstack-overview-poster-art.svg (text removed, artwork only)"
      ],
      "items": {
        "1_cta_fold": "FIXED (af4d37c). Marketing: h1 clamp(2.9rem, min(5.1vw, 7.4vh), 4.45rem); copy column 1.15fr vs 0.75fr; desktop-only tighter hero top padding, lead leading 1.72, CTA margin. Home: wide-short screens keep the source order (lead and CTA above the video); the video-first reorder now fires on max-width 900px only; desktop lead 40rem at 1.72. The vh term alone (the card's route) measured 682 worst at 1366x625, so the column and spacing changes were added. Trade-off, per the card: at 1366x625 the visible slice of the home video drops from 320px to 43px in en and 0 in ja; 314px still shows at 1920x945.",
        "2_cjk_splits": "FIXED (a1ad8a6). src/lib/cjk-phrases.ts: ICU word segments merged into phrases. ja: particles, okurigana and auxiliaries attach to their word, katakana splits rejoin. zh: suffix characters join backward, a clause-final single character joins backward, other single characters join forward. Rendered as a wbr element between phrases with word-break: keep-all and overflow-wrap: anywhere as a last resort, on h1/h2 of the marketing template, home (plus the eyebrow), security and the glossary index. auto-phrase was not used: it is Chromium-only and ja-only. ja headings got sizes that fit their longest phrase: home section h2 at most 2.5rem, home h1 2.15rem on phones, security h1 2rem and h2 1.6rem on phones.",
        "3_lone_cards": "FIXED (e30eb2b). Six-track grids with row sizes from the card count (src/lib/card-rows.ts): 4=2+2, 5=3+2, 7=3+2+2, 22=6x3+2x2; at two columns an odd last card takes the row. Applied to the marketing item grid and the glossary index. Measured: every grid's last row is full at 1366, 800 and 390.",
        "4_phone_tables": "ALREADY FIXED in the stack by c9d4d71 (the #213 phone-width table commit; git merge-base --is-ancestor c9d4d71 HEAD exit 0). Measured at 3a531e1: 60/60 decision tables (15 pages, en and zh-Hans, at 360 and 390) fit, the third column is visible, 0 scroll, 0 page overflow. No change.",
        "5_orphans": "FIXED (1303e30). text-wrap: balance on h1/h2 (marketing, home, security, pricing, legal, cluster, glossary index). Home two-part headings and the security h1 render as two block lines, each balanced, instead of text around a forced break. 'What changes, who / reviews it, what runs' at every desktop width.",
        "6_video_poster": "FIXED (42d55d9). The poster SVG is now artwork only; VideoEmbed draws the words as HTML in the old positions, face and colours, sized in container units. The title is the locale's existing videoCaption, split after 'ObjectStack' (ja keeps the particle on line one). Below 560px of frame the sub-line is omitted and the eyebrow keeps a 9.5-10px floor (from about 5.9px), wrapping only between its parts. The eyebrow stays English everywhere and the English sub-line is shown on en only (see open_questions).",
        "7_typography": "FIXED (ef80a08). Display (--font-display: Iowan Old Style / Palatino / Songti SC / Noto Serif CJK SC) for page titles (h1) and section headings (h2) on every page: pricing, security, legal and cluster switched to it. Text (--font-sans) for card and item titles: glossary index term cards switched to it. Mono stays the label face. Audit after: h1 display on all 13 page types, h2 display on all; card titles text except the blog list (ArticleCard, outside the surface, noted).",
        "a_dark_media_note": "FIXED (f63896e). Dark theme: --ink text on --surface at 90% with a --line border, replacing the rgba(255,255,255,0.92) box.",
        "b_play_button_overlap": "FIXED (42d55d9). Below 560px of frame the badge sits at top 40% and is 52px, between the eyebrow and the title. It covered the title's first line at 390 and 360 before."
      },
      "fold_table_1366x625": {
        "metric": "primary CTA bottom y in px at scrollY 0; fold = 625; before at 3a531e1 -> after at 42d55d9",
        "below_fold_before": {
          "en": "15 of 16 (home + 14 marketing)",
          "zh-Hans": "12 of 16",
          "ja": "15 of 16"
        },
        "below_fold_after": {
          "en": 0,
          "zh-Hans": 0,
          "ja": 0
        },
        "worst_after": {
          "en": 613,
          "zh-Hans": 554,
          "ja": 613
        },
        "rows": [
          "en home: 1207 -> 550 (above)",
          "en product-tour: 626 -> 446 (above)",
          "en platform: 763 -> 505 (above)",
          "en ai: 690 -> 535 (above)",
          "en mcp: 795 -> 565 (above)",
          "en permissions: 763 -> 505 (above)",
          "en data-modeling: 795 -> 535 (above)",
          "en app-ui: 690 -> 535 (above)",
          "en automation: 763 -> 583 (above)",
          "en approvals: 795 -> 535 (above)",
          "en analytics: 795 -> 583 (above)",
          "en trust-center: 867 -> 583 (above)",
          "en templates: 594 -> 446 (above)",
          "en agent-developer: 835 -> 554 (above)",
          "en forward-deployed-engineers: 899 -> 613 (above)",
          "en customer-stories: 626 -> 476 (above)",
          "zh-Hans home: 1175 -> 550 (above)",
          "zh-Hans product-tour: 522 -> 446 (above)",
          "zh-Hans platform: 658 -> 476 (above)",
          "zh-Hans ai: 586 -> 476 (above)",
          "zh-Hans mcp: 658 -> 505 (above)",
          "zh-Hans permissions: 658 -> 476 (above)",
          "zh-Hans data-modeling: 658 -> 505 (above)",
          "zh-Hans app-ui: 658 -> 476 (above)",
          "zh-Hans automation: 658 -> 505 (above)",
          "zh-Hans approvals: 658 -> 476 (above)",
          "zh-Hans analytics: 658 -> 476 (above)",
          "zh-Hans trust-center: 658 -> 505 (above)",
          "zh-Hans templates: 562 -> 416 (above)",
          "zh-Hans agent-developer: 626 -> 476 (above)",
          "zh-Hans forward-deployed-engineers: 763 -> 554 (above)",
          "zh-Hans customer-stories: 522 -> 398 (above)",
          "ja home: 1271 -> 609 (above)",
          "ja product-tour: 626 -> 446 (above)",
          "ja platform: 763 -> 505 (above)",
          "ja ai: 690 -> 535 (above)",
          "ja mcp: 795 -> 565 (above)",
          "ja permissions: 763 -> 505 (above)",
          "ja data-modeling: 795 -> 535 (above)",
          "ja app-ui: 690 -> 535 (above)",
          "ja automation: 763 -> 583 (above)",
          "ja approvals: 795 -> 535 (above)",
          "ja analytics: 795 -> 583 (above)",
          "ja trust-center: 867 -> 583 (above)",
          "ja templates: 594 -> 446 (above)",
          "ja agent-developer: 835 -> 554 (above)",
          "ja forward-deployed-engineers: 899 -> 613 (above)",
          "ja customer-stories: 626 -> 476 (above)"
        ],
        "other_viewports_after": "worst marketing CTA bottom: 699 at 1440x750, 724 at 1512x830, 832 at 1920x945; worst home: ja 628 at 1440x750, 641 at 1512x830, 658 at 1920x945; all above their folds",
        "file": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/fold-table.md"
      },
      "cjk_split_table": {
        "scope": "h1/h2 plus the home eyebrow, six viewports, home, platform, pricing, security, FDE, glossary index and all 15 marketing pages, rendered lines captured in Chromium 141 on the built site",
        "counts_before": {
          "ja word-split": 61,
          "ja particle-split": 14,
          "ja one-char last line": 17,
          "zh-Hans word-split": 241,
          "zh-Hans one-char last line": 103,
          "en one-word last line": 146,
          "ja (English fallback pages) one-word last line": 134
        },
        "counts_after": "0 in every category (3348 headings judged)",
        "named_cases": [
          "390,360 ja home h1: オントロジーが / ソフトウェ / アである。 -> オントロジーが / ソフトウェアである。",
          "390 ja home eyebrow: ObjectStack の上に構築された商用ランタイム環 / 境 -> ObjectStack の上に / 構築された商用ランタイム環境",
          "1366 zh-Hans platform h1: 一个业务系统需要 / 的一切,随时可投 / 入生产。 -> 一个业务系统需要的 / 一切,随时可投入生产。",
          "360 zh-Hans platform h1: 一个业务系统需要 / 的一切,随时可投 / 入生产。 -> 一个业务系统 / 需要的一切,随时 / 可投入生产。",
          "360 zh-Hans platform h2: 六大能力域,一个开 / 放、受治理的运行时 -> 六大能力域,一个 / 开放、受治理的运行时",
          "360 zh-Hans platform h2: 一份定义,背后是一整 / 个应用。 -> 一份定义,背后 / 是一整个应用。",
          "360 zh-Hans platform h2: 让 Agent 来写,让人来 / 审 -> 让 Agent 来写, / 让人来审",
          "1366 ja home h2: データは自社ネ / ットワーク内 / に。 / AI は権限の内 / 側で動く。 -> データは自社 / ネットワーク内に。 / AI は権限の / 内側で動く。",
          "1366 zh-Hans home h2: 让 Agent 生成软 / 件, / 让人保留审阅 / 权。 -> 让 Agent / 生成软件, / 让人保留 / 审阅权。"
        ],
        "file": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/cjk-split-table.md (277 rows: every ja/zh-Hans heading flagged before, with its after lines)"
      },
      "gates": [
        "pnpm check (under lock, at 42d55d9): Result (154 files): 0 errors, 0 warnings, 0 hints · os-verify-lock: VERDICT command-exit 0 · held the lock 14s",
        "pnpm build (under lock, at 42d55d9): 880 page(s) built; svg-text-fit OK; share images 87 rendered, no uncovered glyph; content lint --dist passed (CJK line breaks: 0 segment breaks between CJK characters; quotation marks: 0 wrong; links: 93151 resolve) · os-verify-lock: VERDICT command-exit 0 · held the lock 91s",
        "pnpm content:lint: EXIT 0, content lint passed (339 files, 44 glossary terms checked)",
        "pnpm content:lint --published: EXIT 0, content lint passed (339 files, 44 glossary terms checked)",
        "pnpm seo:smoke: EXIT 0, SEO smoke test passed (870 HTML pages checked; 569 sitemap URLs against 301 noindexed pages; 5424 canonical/hreflang annotations)",
        "pnpm svg:lint: EXIT 0, svg-text-fit: OK (112 SVGs, 2244 text boxes, 181 filtered elements, tolerance 2px, 1 allowlisted)",
        "control-byte self-scan over the 11 changed files: no matches",
        "baseline build at 3a531e1 before any edit: VERDICT command-exit 0"
      ],
      "tests": "No unit-test framework in this repo, so the proof is a before/after measurement on two real builds (3a531e1, served from scratchpad dist-before; 42d55d9, dist-after) with one script and one judge for both: tools/measure.mjs (fold, rendered heading lines, grid rows, table fit, computed faces) and tools/judge.mjs. Before: 14/15 en marketing pages plus home below the fold, 302 CJK word splits, 24/37 uneven item grids, a lone glossary card. After at 42d55d9: 0, 0, 0, 0. Item 4 measured green at 3a531e1 (60/60). Ablation and reverse verification are not applicable: no test file to mutate; the before build is the control.",
      "screenshots": {
        "count": "1152 JPEGs: 576 full page + 576 first screen",
        "matrix": "home, platform, pricing, security, FDE, glossary index, data-modeling, permissions x 1366x625, 1440x750, 1512x830, 1920x945, 390x844, 360x800 x light/dark x en/zh-Hans/ja x before/after; lazy images scrolled into view",
        "paths": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/shots/{before,after}/{en,zh-Hans,ja}/{light,dark}/{full,fold}/PAGE-WxH.jpg",
        "looked_at": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/sheets/ (fold-1366-en-light, fold-1366-zh-dark-after, fold-phone-home, fold-phone-platform, grids, tables, table-1366, strip-* full-page strips) and /tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/shots/focus/ (poster before/after at 1366/390/360 in en/zh-Hans/ja, dark media note, grid and table crops)",
        "font_caveat": "container Chromium 141 has no Iowan/Avenir/Songti; Latin renders in DejaVu/Liberation and CJK in WenQuanYi/IPA Gothic, so line counts are pessimistic for macOS"
      },
      "deviations": [
        "Status 'staged', no push, no PR, no labels, worktree kept: per the dispatch's staged mode, which overrides the contract's push/PR/cleanup steps. The only GitHub write is this comment.",
        "Commit trailers use the model-free pair (Co-Authored-By: Claude; Claude-Session: session URL) per the agent contract and the stack's own commits, not the harness reminder's model-named form.",
        "Surface: no copy file was edited (keep-together is rendered markup). The card's content/home.ts is src/lib/home.ts. Files beyond the card's Surface list: ClusterPage.astro (item 7 names cluster; item 5 balance), VideoEmbed.astro and the poster SVG (Claim-added (b), plus item 6), two new helpers in src/lib.",
        "Item 1 route: the vh term alone was insufficient (682 worst), so a column ratio and desktop spacing were added. Home reverses the earlier max-height 1040px video-first rule, as the card asked; this is documented in the code.",
        "Item 2 route: wbr plus keep-all instead of auto-phrase (cross-browser). ja headings are visibly smaller where their phrases did not fit: home h2 at most 2.5rem, phone h1 sizes.",
        "Item 6: the English sub-line is not drawn on non-en pages or on frames under 560px; the eyebrow stays English everywhere. Open question below.",
        "content:lint 'both modes' read as default plus --published; --dist ran inside pnpm build.",
        "Worktree cap observation: at creation the only other www worktree was www-lin; at report time 7 non-main worktrees exist (www-issue-209-grid, -223, -228, -229, -230, www-lin, www-postbase), five of them agent worktrees.",
        "A hung screenshot helper of mine was stopped with TaskStop. Another agent's Chromium (issue-229) was seen and left alone. All servers I started (static 4801, 4802, 4803; astro dev 4811) were stopped by PID."
      ],
      "mcp_calls": "0",
      "api_writes": "1 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay) carrying one comment op: POST /repos/objectstack-ai/www.objectos.ai/issues/223/comments (this os-dev-report)",
      "open_questions": [
        {
          "question": "The video poster's eyebrow ('OPEN PROTOCOL & RUNTIME · APACHE-2.0') and sub-line ('Data model · UI · workflows · permissions — one context window.') have no translations. Should they get translated copy?",
          "options": [
            "A: keep as staged (eyebrow English everywhere, sub-line on en only; title localized from the existing captions)",
            "B: add translated eyebrow and sub-line strings to homeCopy in a copy card, then pass them as posterText",
            "C: drop the eyebrow on non-en pages too"
          ],
          "recommendation": "A now, B as a follow-up copy card: the eyebrow names the licence, and its English reads as a badge; the sub-line is a sentence and should not appear untranslated."
        }
      ],
      "out_of_scope_findings": [
        "class: a · reach: public door, zh-Hans blog list at 1366x625 renders the card title 开放与封闭企业本体:谁拥有业 / 务语义层 (a word split across lines); measured on this build, 157 of 450 zh/ja h1/h2 split a word: 125 blog list (src/components/ArticleCard.astro), 26 blog posts (src/layouts/BlogPost.astro), 6 cluster pages (src/components/ClusterPage.astro); glossary term pages 0 · evidence: tools/extra-cjk.mjs, extra-cjk.txt · same family as this card's item 2, so name it for the family closure or a seat-filed card; the fix is phraseHtml plus keep-all per template; not done in place because exemption condition ③ (no other claim holds the file) could not be verified · dedupe words: CJK heading word split, blog card title, wbr keep-all, BlogPost h1",
        "carrier: none · noted, not filed · zh-Hans approvals heroTitle uses straight ASCII quotes around 同意 (copy typography)",
        "carrier: none · noted, not filed · zh-Hans automation h2 has a space between 10 and 万, a break opportunity markup cannot remove (copy)"
      ]
    }

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report-addendum

    {
      "issue": 223,
      "report": "os-dev-report-addendum",
      "status": "staged",
      "branch": "lin/issue-223",
      "worktree": "/home/user/www-issue-223",
      "base_sha": "3a531e1f8ea22622f2567f5829d6c02debfc8751",
      "previous_tip": "42d55d9a699f63dac008f592eabb0cfa7cf288ef",
      "final_commit": "0424a432fadb708942e5c8cf9223003ce40896aa",
      "commits": [
        "0424a43 site(blog): CJK headings break between phrases on the blog list, posts and cluster pages"
      ],
      "pr": null,
      "pr_body_draft": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/pr-body.md (updated; first line: Fixes #223; previous version kept as pr-body.v1.md)",
      "session": "session_01FeA1nwBz1ohH65dvffUGKr",
      "summary": "Extension from the seat's review is done. zh/ja headings on the blog list (ArticleCard), posts (BlogPost title, related heading and MDX body headings) and cluster pages (ClusterPage h1, h2 and card titles) now break only between phrases. tools/extra-cjk.mjs measures 157 of 450 -> 2 of 450; the 2 are one ja card title whose 15-character katakana word is wider than a card line. The byte diff against 42d55d9 shows every Latin page unchanged apart from asset hashes, and wbr only inside headings, never in code. All gates are green at 0424a43.",
      "files_changed": [
        "src/lib/cjk-phrases.ts -> scripts/lib/cjk-phrases.mjs (moved, now JS; adds wbrOffsets, localeOfPath, rehypeCjkHeadingPhrases; Astro-identical escaping; ja okurigana-suffix rule)",
        "astro.config.mjs (rehypeCjkHeadingPhrases added to markdown.rehypePlugins after rehypeCjkSegmentBreaks)",
        "src/components/ArticleCard.astro (card title via phraseHtml; keep-all and balance on zh/ja h2)",
        "src/layouts/BlogPost.astro (post h1 and related h2 via phraseHtml; keep-all on zh/ja h1/h2; .prose h1-h6 keep-all for zh/ja)",
        "src/components/ClusterPage.astro (h1, h2, card h3 via phraseHtml; keep-all on zh/ja; ja h1 size)",
        "src/components/{MarketingEvidencePage,MarketingHome,SecurityPage,GlossaryIndex}.astro (import path only)"
      ],
      "mdx_body_headings_choice": "A step in the existing markdown pipeline, not left alone. rehypeCjkHeadingPhrases is added to the one markdown.rehypePlugins list in astro.config.mjs (MDX inherits it). It reads the locale from the source file name (index.zh-Hans.mdx, index.ja.mdx; index.mdx = en is untouched), works only inside h1-h6, never descends into code, pre, kbd or samp, and adds a wbr only beside text nodes (never inside an opaque element). Heading ids are unchanged: Astro derives them from the text, and wbr has none.",
      "byte_diff_proof": {
        "compared": "dist at 42d55d9 vs dist at 0424a43, all 893 html/xml/txt files, hashed /_astro asset names and data-astro-cid values normalized",
        "identical": "255 (every locale's unchanged pages plus 404.html, index.html, llms.txt, robots.txt, rss.xml, sitemaps)",
        "asset_hash_only": "440 (en 95, de 75, es 75, fr 75, ko 75, ja 15, zh-Hans 15, zh-Hant 15)",
        "wbr_only": "198 (ja 60, zh-Hans 69, zh-Hant 69); 6713 wbr, all inside an h1-h6, 0 outside a heading, 0 inside code/pre/kbd/samp",
        "any_other_difference": 0,
        "latin_pages": "every en, de, es, fr and ko page is identical or differs only by asset hashes",
        "tool": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/tools/dist-diff.mjs, output /tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/dist-diff.txt"
      },
      "measurement": {
        "tool": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/tools/extra-cjk.mjs (zh-Hans and ja, 1366/390/360; 3 cluster pages, 3 glossary terms, the blog list, 2 posts; h1/h2)",
        "before_42d55d9": "157 of 450 flagged: blog list 125, blog posts 26, cluster 6, glossary terms 0",
        "after_0424a43": "2 of 450 flagged: blog list 2, posts 0, cluster 0",
        "what_remains": "The ja card title 'FDE(フォワードデプロイドエンジニア)は何を使うのか?…' at 1366 and 360. フォワードデプロイドエンジニア is one 15-character katakana word (ICU treats it as one word), and with its brackets and FDE it is one unbreakable unit of about 390px against a card line of about 330px, so the last-resort overflow-wrap breaks inside it. Removing it would take a smaller title for one card, or a copy change (a middle dot in the loanword); neither is done.",
        "regression_check": "the card's original set (3348 headings, six viewports) re-judged on 0424a43: 0 flags; fold at 1366x625: 0 below, worst 613 (unchanged)",
        "examples": [
          "1366 zh blog card: 开放与封闭企业本体:谁拥有业 / 务语义层 -> 开放与封闭企业本体: / 谁拥有业务语义层",
          "1366 zh post h1: AI Agent 数据安全边界:如何在企 / 业权限内工作 -> AI Agent 数据安全边界: / 如何在企业权限内工作",
          "390 zh post body h2: 第一层边界:agent 必须继承用户身 / 份 -> 第一层边界:agent / 必须继承用户身份",
          "1366 ja cluster h1 (self-hosted-ai): 企業アプリケー / ション向けセ / ルフホスト AI -> 企業アプリケーション / 向けセルフホスト AI",
          "390 ja blog card: オープンな企業オントロジー:業務セマ / ンティックレイヤー… -> オープンな企業オントロジー: / 業務セマンティックレイヤーは / 誰が所有すべきか"
        ]
      },
      "gates": [
        "pnpm build (under lock, at 0424a43): 880 page(s) built; svg-text-fit OK; share images 87, no uncovered glyph; content lint --dist passed (CJK line breaks 0 segment breaks; quotation marks 0 wrong; 93151 links resolve) · os-verify-lock: VERDICT command-exit 0 · held the lock 98s",
        "pnpm check (under lock, at 0424a43): Result (154 files): 0 errors, 0 warnings, 0 hints · os-verify-lock: VERDICT command-exit 0 · held the lock 13s",
        "pnpm content:lint: EXIT 0, content lint passed (339 files, 44 glossary terms checked)",
        "pnpm content:lint --published: EXIT 0, content lint passed (339 files, 44 glossary terms checked)",
        "pnpm seo:smoke: EXIT 0, SEO smoke test passed (870 HTML pages; 569 sitemap URLs; 5424 canonical/hreflang annotations; 15865 prose blocks language-checked)",
        "pnpm svg:lint: EXIT 0, svg-text-fit: OK (112 SVGs, 2244 text boxes)"
      ],
      "screenshots": {
        "count": "96 JPEGs (full page + first screen)",
        "matrix": "/zh-Hans/blog/, /ja/blog/, zh-Hans post ai-agent-business-data-security-boundaries, zh-Hans cluster ai-native-app-platform x 1366x625, 390x844, 360x800 x light/dark x before (42d55d9) / after (0424a43)",
        "paths": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/shots-ext/{before,after}/{zh-Hans,ja}/{light,dark}/{full,fold}/{blog,post,cluster}-WxH.jpg",
        "looked_at": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-223/sheets-ext/ (zh-blog-1366, phones, dark-1366, ja-cluster) and the ja cluster h1 crops in shots-ext/"
      },
      "deviations": [
        "The phrase module moved from src/lib/cjk-phrases.ts to scripts/lib/cjk-phrases.mjs, so the rehype step and the components share one definition (the cjk-segment-break.mjs pattern; BlogPost already imports from scripts/lib). Import paths in the four earlier components changed; nothing else in them did.",
        "phraseHtml now escapes as Astro does (html-escaper, apostrophe as &#39;). The byte diff shows no Latin page change from it.",
        "New ja rule: a one-kanji suffix after a stem with okurigana stays with it (働き方). The phrase dump of every earlier marketing/home/security heading is unchanged by it.",
        "ja cluster h1 is smaller: clamp(1.8rem, 4.4vw, 4.2rem), 60px at desktop against 89.6px, so the eleven-character katakana word アプリプラットフォーム fits. Before, this title happened to break at the compound boundary (アプリ / プラットフォーム) at these widths; with keep-all, the alternative is a break inside the word.",
        "On ArticleCard, BlogPost and ClusterPage, keep-all and text-wrap: balance apply to zh/ja headings only, so Latin headings there render as before.",
        "ArticleList.astro (the blog list's own h1/h2) is untouched: it is held by the Recommended-grid item, and none of its headings measured a split.",
        "Three builds ran under the lock for the extension (an intermediate one exposed the self-closing wbr form from the MDX pipeline, the okurigana-suffix case and the cluster h1 overflow). All servers I started (4802, 4803) were stopped by PID."
      ],
      "mcp_calls": "0",
      "api_writes": "1 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay) carrying one comment op: POST /repos/objectstack-ai/www.objectos.ai/issues/223/comments (this addendum)",
      "open_questions": [],
      "out_of_scope_findings": [
        "the earlier class-a finding (CJK word splits on ArticleCard, BlogPost, ClusterPage) is resolved in this card by 0424a43; nothing new filed"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

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


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

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


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions