Skip to content

Commit 0cb45b7

Browse files
sametbasbugclaude
andcommitted
Keep agent markdown inside the record's own voice
Reported as an agent finding a way to write in huge text. It was not the agent -- it wrote an ordinary numbered list. Two of our own rules did it. .record-body styled only p. Every other element micromark can emit fell through to browser defaults, and the h1 default is 2em: an agent writing "# Baslik" got 32px inside a 16px body. Anyone could out-shout everyone else. Headings now separate by weight, and the largest sits one step above body text, because a heading in a short social record is emphasis and not hierarchy. Lists, quotes, code, rules and images get the same treatment, all on the scale. .reply-state p and .reply-state h2 matched by element with no depth limit, so they reached into every reply body. Agent paragraphs rendered at 12.64px while list items kept the body's 18px -- that gap is what made the list look enormous. The same markdown was 24px in the feed and 12.64px inside a reply. Both rules now target the heading block they were written for, which also restores .section-label and .record-meta to their own sizes. That heading also carried a hardcoded #4a546b with no dark counterpart: 2.49:1 against the dark background, under AA. On --muted it measures 8.18 dark and 5.28 light. Guarded by rendering rather than by scanning CSS, since the hole was a rule that was never written rather than one written wrongly: the test injects every CommonMark block into a real record body and fails on anything above the record's ceiling. Mutation-checked -- dropping the heading contract reports h1 36px, h2 27px, h3 21.06px, and restoring the leak reports 12.64px against 18px. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 216e06e commit 0cb45b7

4 files changed

Lines changed: 176 additions & 9 deletions

File tree

‎scripts/orbit-browser-tests.mjs‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,81 @@ if (errors.length === 0) {
791791
check(pageErrors.length === 0, `${label}: owner UI sayfa hatası: ${pageErrors.join(' | ')}`);
792792
await context.close();
793793
}
794+
795+
/*
796+
* Ajan yazısının tavanı.
797+
*
798+
* Gövde markdown'ı ajanın kendi metni ve kaydın ölçeğinin içinde kalmalı:
799+
* kimse başlık yazarak diğerlerinden iri görünememeli. .record-body altında
800+
* bir dönem yalnız p'nin kuralı vardı; kalan her eleman tarayıcı
801+
* varsayılanına düşüyor, h1 16px gövdenin içinde 32px çiziliyordu.
802+
*
803+
* Ölçüt CSS'e değil çıktıya bakıyor, çünkü asıl açık bir kuralın yanlış
804+
* yazılması değil, hiç yazılmamasıydı.
805+
*/
806+
{
807+
const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
808+
const page = await context.newPage();
809+
await page.goto(`${baseUrl}/posts/katki-kime-ait`, { waitUntil: 'load' });
810+
811+
const result = await page.evaluate(() => {
812+
const body = document.querySelector('.record-body');
813+
if (!body) return { hata: '.record-body bulunamadı' };
814+
const root = parseFloat(getComputedStyle(document.documentElement).fontSize);
815+
const step = (name) =>
816+
parseFloat(getComputedStyle(document.documentElement).getPropertyValue(name)) * root;
817+
const tavan = step('--text-lg');
818+
const taban = parseFloat(getComputedStyle(body).fontSize);
819+
820+
// micromark'ın CommonMark çıktısındaki her blok. GFM kapalı: tablo yok,
821+
// ham HTML kaçırılıyor.
822+
body.innerHTML = `
823+
<h1>bir</h1><h2>iki</h2><h3>uc</h3><h4>dort</h4><h5>bes</h5><h6>alti</h6>
824+
<p>paragraf <code>satirici kod</code> <em>vurgu</em> <strong>guclu</strong></p>
825+
<ul><li>madde</li></ul><ol><li>sirali</li></ol>
826+
<blockquote><p>alinti</p></blockquote>
827+
<pre><code>blok kod</code></pre>
828+
<hr /><p><a href="/">bag</a></p>`;
829+
830+
const asanlar = [...body.querySelectorAll('*')]
831+
.map((el) => ({ etiket: el.tagName.toLowerCase(), punto: parseFloat(getComputedStyle(el).fontSize) }))
832+
.filter((entry) => entry.punto > tavan + 0.01);
833+
834+
return { tavan, taban, asanlar };
835+
});
836+
837+
check(!result.hata, `Ajan yazısı tavanı: ${result.hata ?? ''}`);
838+
check(
839+
result.taban <= result.tavan,
840+
`Kayıt gövdesinin kendisi tavanı aşıyor: ${result.taban}px > ${result.tavan}px.`,
841+
);
842+
check(
843+
result.asanlar?.length === 0,
844+
`Ajan markdown'ı kaydın ölçeğini aşıyor (tavan ${result.tavan}px): `
845+
+ `${(result.asanlar ?? []).map((entry) => `${entry.etiket} ${entry.punto}px`).join(', ')}`,
846+
);
847+
848+
// .reply-state p bir dönem eleman üzerinden eşleşiyor ve yanıtlardaki her
849+
// ajan paragrafını 12.64px'e düşürüyordu: aynı markdown ana akışta ve
850+
// yanıt içinde farklı boyutta çiziliyordu.
851+
await page.goto(`${baseUrl}/posts/katki-kime-ait`, { waitUntil: 'load' });
852+
const yanit = await page.evaluate(() => {
853+
const body = document.querySelector('.reply-list .record-body');
854+
const paragraph = body?.querySelector('p');
855+
if (!body || !paragraph) return null;
856+
return {
857+
govde: parseFloat(getComputedStyle(body).fontSize),
858+
paragraf: parseFloat(getComputedStyle(paragraph).fontSize),
859+
};
860+
});
861+
check(Boolean(yanit), 'Yanıt gövdesi ölçülemedi.');
862+
check(
863+
!yanit || yanit.paragraf === yanit.govde,
864+
`Yanıttaki ajan paragrafı gövdeden farklı boyutta: ${yanit?.paragraf}px ≠ ${yanit?.govde}px.`,
865+
);
866+
867+
await context.close();
868+
}
794869
} finally {
795870
await browser.close();
796871
await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));

‎src/styles/components.css‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,100 @@
347347
margin-top: var(--space-8);
348348
}
349349

350+
/* Ajan yazısının tipografik sözleşmesi.
351+
*
352+
* Gövde, ajanın markdown'ı. Buraya kural yazılmadan önce yalnız p'nin stili
353+
* vardı; kalan her eleman tarayıcı varsayılanına düşüyordu. h1 varsayılanı 2em
354+
* olduğu için bir ajan "# Başlık" yazarak 16px'lik gövdenin içinde 32px metin
355+
* çizdirebiliyordu — yani diğerlerinden yüksek sesle konuşabiliyordu.
356+
*
357+
* Bu yüzden başlıklar boyutla değil ağırlıkla ayrışır ve en irisi gövdenin
358+
* yalnız bir kademe üstünde kalır. Kayıt kısa bir sosyal kayıt; başlık burada
359+
* hiyerarşi değil vurgudur.
360+
*
361+
* Kapsam micromark'ın CommonMark çıktısı: tablo yok, ham HTML kaçırılıyor.
362+
* GFM açılırsa tablo da buraya eklenmeli. */
363+
.record-body h1,
364+
.record-body h2,
365+
.record-body h3,
366+
.record-body h4,
367+
.record-body h5,
368+
.record-body h6 {
369+
margin: var(--space-12) 0 var(--space-4);
370+
color: var(--ink);
371+
font-size: var(--text-base);
372+
font-weight: var(--weight-extrabold);
373+
letter-spacing: var(--tracking-tight);
374+
line-height: var(--leading-snug);
375+
}
376+
377+
.record-body h1,
378+
.record-body h2 {
379+
font-size: var(--text-lg);
380+
}
381+
382+
.record-body ul,
383+
.record-body ol {
384+
margin: var(--space-8) 0;
385+
padding-left: var(--space-12);
386+
}
387+
388+
.record-body li + li {
389+
margin-top: var(--space-3);
390+
}
391+
392+
.record-body blockquote {
393+
margin: var(--space-8) 0;
394+
padding-left: var(--space-8);
395+
border-left: 2px solid light-dark(var(--agent-accent-strong), var(--agent-accent-soft));
396+
color: var(--muted);
397+
}
398+
399+
.record-body code {
400+
padding: var(--space-1) var(--space-2);
401+
border-radius: 0.35rem;
402+
background: var(--surface-sunken);
403+
font-family: var(--mono);
404+
font-size: var(--text-md);
405+
}
406+
407+
.record-body pre {
408+
overflow-x: auto;
409+
margin: var(--space-8) 0;
410+
padding: var(--space-6);
411+
border: 1px solid var(--line);
412+
border-radius: 0.6rem;
413+
background: var(--surface-sunken);
414+
}
415+
416+
/* Blok içindeki kod kendi kutusunu zaten aldı; ikinci bir dolgu ve zemin
417+
almasın. */
418+
.record-body pre code {
419+
padding: 0;
420+
border-radius: 0;
421+
background: none;
422+
}
423+
424+
.record-body hr {
425+
margin: var(--space-12) 0;
426+
border: 0;
427+
border-top: 1px solid var(--line);
428+
}
429+
430+
.record-body img {
431+
max-width: 100%;
432+
border-radius: 0.6rem;
433+
}
434+
435+
/* Gövdenin ilk ve son çocuğu kendi dış boşluğunu kaydın hizasına taşımasın. */
436+
.record-body > :first-child {
437+
margin-top: 0;
438+
}
439+
440+
.record-body > :last-child {
441+
margin-bottom: 0;
442+
}
443+
350444
.project-attachment {
351445
margin: var(--space-8) 0 0;
352446
display: grid;

‎src/styles/pages.css‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -619,22 +619,19 @@
619619
border: 0;
620620
}
621621

622-
.reply-state h2 {
622+
/* Bölümün kendi başlığı. Seçici .reply-state h2 idi ve eleman üzerinden
623+
eşleştiği için ajanın gövdesindeki her ## başlığını da yakalıyordu; aynı
624+
markdown ana akışta 24px, yanıt içinde 12.64px çiziliyordu. Sabit #4a546b
625+
de koyu temada karanlık zemine düşüyordu. */
626+
.reply-heading h2 {
623627
margin: 0;
624628
padding: 0 var(--space-3);
625-
color: #4a546b;
629+
color: var(--muted);
626630
font-size: var(--text-sm);
627631
letter-spacing: var(--tracking-none);
628632
text-transform: none;
629633
}
630634

631-
.reply-state p {
632-
margin: 0;
633-
color: var(--muted);
634-
font-family: var(--serif);
635-
font-size: var(--text-sm);
636-
}
637-
638635
.reply-list {
639636
display: grid;
640637
gap: var(--space-6);

‎src/styles/tokens.css‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
--scrollbar-thumb-hover: #6656ca;
4040
--sans: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
4141
--serif: var(--sans);
42+
--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
4243

4344
/* Tip skalası: 1rem tabanlı, 1.125 oranlı.
4445
*

0 commit comments

Comments
 (0)