Skip to content

docs(fields): blank line before ## Field Schema on four field pages (objectui#9427) - #9435

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-9427-field-page-blank-line
Sep 13, 2026
Merged

docs(fields): blank line before ## Field Schema on four field pages (objectui#9427)#9435
baozhoutao merged 1 commit into
mainfrom
claude/issue-9427-field-page-blank-line

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #9427

Four field pages placed a self-closing SchemaExample tag immediately before
## Field Schema, with no blank line between them. This adds that one blank
line to each — the diff is four empty lines and nothing else.

Why a four-line docs patch needed measuring

Two real pipelines in this monorepo disagree on that exact shape, so the fix
had to be proved on both.

Renderer 1 — packages/plugin-markdown's MarkdownImpl (react-markdown;
the chain apps/console's DocPage pairs with extractToc). It follows
CommonMark's HTML-block rule: an HTML block ends only at a blank line, so the
tag's block swallowed the heading and no field-schema anchor was ever
rendered — while extractToc still listed one, so the ToC's #field-schema
link resolved to nothing.

Renderer 2 — @mdx-js/mdx (the compiler fumadocs-mdx uses for
apps/site). A self-closing MDX JSX flow element closes immediately, so it
never swallowed the heading. apps/site was already correct.

The renderer that disagreed is the one reading CommonMark correctly, so the
docs are what change. packages/plugin-markdown is untouched (objectui#7666's
triage already priced and rejected teaching extractToc the HTML-block
continuation rule — that would fix the MarkdownImpl pairing and flip the
divergence for every MDX consumer).

Measurements (all re-derived on this branch's base, 7098eed365)

Population, named once: 223 files — content/docs/**/*.{md,mdx} (184) plus
packages/*/README.md (39), YAML frontmatter stripped (console serves doc
bodies without it). Reader: MarkdownImpl rendered with
renderToStaticMarkup, ids read off the id attribute of each rendered h1–h6
element — the same reader
packages/plugin-markdown/src/toc-anchor-parity.test.tsx already uses, because
reading the rendered attribute proves the anchor exists rather than re-deriving
the slug rules.

Divergence set re-derived, not inherited. Files where the rendered heading
ids differ from extractToc's: PRE exactly the four named in the card, POST
empty. Rendered headings across the corpus 2984 → 2988 (+4, the four new
headings). The card read 2981 on the same corpus a few hours earlier — the
corpus moved again by +3 headings, and the divergence set did not.

Renderer 1 — the diagnostic flip (absent → present, all four):

file field-schema PRE POST position POST extractToc position
content/docs/fields/date.mdx absent present 2 2
content/docs/fields/rich-text.mdx absent present 2 2
content/docs/fields/text.mdx absent present 4 4
content/docs/fields/textarea.mdx absent present 3 3

date.mdx rendered ids, both sides:

PRE  ["basic-usage","with-default-value","date-formats","use-cases","cell-renderer","datetime-variant"]
POST ["basic-usage","with-default-value","field-schema","date-formats","use-cases","cell-renderer","datetime-variant"]

extractToc agreement — the point of the fix: POST, the rendered id list
equals extractToc's id list exactly (h1–h6, document order) for all four
files. That is the same comparison whose failure defined the divergence set,
so the empty POST set is that agreement measured corpus-wide.

Renderer 2 — the non-change, with both sides printed. Compiled in situ
through @mdx-js/mdx 3.1.1 + rehype-slug, rendered with the same
renderToStaticMarkup reader. Id lists are identical PRE and POST for all
four files — the insertion costs apps/site nothing:

date.mdx      PRE == POST  ["basic-usage","with-default-value","field-schema","date-formats","use-cases","cell-renderer","datetime-variant"]
rich-text.mdx PRE == POST  ["basic-usage","html-editor","field-schema","supported-formats","markdown","html","cell-renderer","use-cases","features","editor-modes"]
text.mdx      PRE == POST  ["basic-usage","with-placeholder","required-field","read-only","field-schema","use-cases","cell-renderer"]
textarea.mdx  PRE == POST  ["basic-usage","custom-rows","required-field","field-schema","use-cases","cell-renderer"]

Controls. Every zero above is paired with a control of known direction that
hits in the same run:

  • Lit control for the id string under test: 26 field pages carry a
    ## Field Schema heading; the 22 that already had the blank line emitted
    field-schema through MarkdownImpl in the same PRE run (22/26 → 26/26
    POST). A run where those also read absent would be a broken instrument.
  • Nonsense control: field-schema-zzz-not-a-real-anchor — 0 hits in every
    run, both renderers, both phases.
  • Byte-level second reader: a scan for the defect shape (a self-closing JSX
    tag line immediately followed by an ATX heading) over the same 223 files:
    4 hits PRE → 0 POST, while the healthy shape (tag + true blank line +
    heading) reads 250 PRE → 254 POST.

Inserted bytes verified, not just "the file changed." A
trailing-whitespace-only line is not a blank line to CommonMark's HTML-block
rule, so the bytes were read back: each file grew by exactly 1 byte, and the
separator between tag and heading is b'\n\n' in all four. git diff --check
is clean, and cat -A shows $ with nothing before it on each inserted line.

Gates (redirect-then-capture; PRE = the four files reverted to base, POST = as committed)

gate PRE POST
docs:check-links 0 0 — "Links are valid across 17 scan roots."
check:doc-fences 0 0 — 227 documents
check:doc-snippets 0 0 — "650 of 650 block(s) judged, 0 failed"
check:doc-examples 0 0 — 124 blocks, all as the ledger declares
check:doc-types 0 0 — 188 doc files, 1106 code blocks
check:doc-example-ids 0 0 — 414 references resolve
check:control-bytes 0 0 — 7601 tracked text files
check:new-line-citations 0
vitest packages/plugin-markdown/ apps/console/src/pages/docs-portal.test.tsx 0 — 6 files, 55 tests
vitest packages/types/ (these read content/docs) 0 — 185 files, 4252 tests

check:doc-snippets and check:doc-examples first exited 2, which their
own output spells out as PRECONDITION NOT MET — the snippet program was NOT run; that is "could not run", not a red gate. After the scoped build they
name (turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2, 35/35 successful) both read 0 in both phases.

node scripts/check-changeset-presence.mjs exits 0 and prints "no changeset is
owed" — 4 files changed, 0 of them published source of a released package.
node scripts/check-governed-queue-guard.mjs --test on the four paths prints
NOT GOVERNED.

Draft on purpose: the PM seat lands this. Bundle Analysis is red across this
lane and is not related to this change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr


Generated by Claude Code

`date.mdx`, `rich-text.mdx`, `text.mdx` and `textarea.mdx` each placed a
self-closing `SchemaExample` tag immediately before `## Field Schema`, with
no blank line between them.

`packages/plugin-markdown`'s `MarkdownImpl` (react-markdown; the chain
`apps/console`'s `DocPage` uses) follows CommonMark's HTML-block rule — an
HTML block ends only at a blank line — so the tag's block swallowed the
heading and no `field-schema` anchor was rendered, while `extractToc` still
listed one. The `#field-schema` ToC link therefore resolved to nothing.

`@mdx-js/mdx` (the compiler `fumadocs-mdx` uses for `apps/site`) closes a
self-closing MDX JSX flow element immediately, so it never swallowed the
heading; its emitted id lists are byte-identical before and after this
change. The renderer that disagreed is the one reading CommonMark
correctly, so the fix is in the docs, not in either renderer.

Measured on this base over 223 files (`content/docs/**/*.{md,mdx}` plus
`packages/*/README.md`, frontmatter stripped): the rendered-vs-`extractToc`
divergence set was exactly these four files and is now empty; rendered
headings 2984 -> 2988; the 22 field pages that already carried the blank
line emitted `field-schema` throughout, as the lit control.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

PM pre-merge probe — domain:devx @ objectui seat (#5748)

⚠️ Posted BEFORE the merge. Re-taken on the ACTUAL base 7098eed365 (ERRATA 62b).

Leg 1 — the defect shape, re-derived INDEPENDENTLY by this seat

⚠️ Read at the byte level, multiline — ⛔ a line-oriented grep cannot express 「tag on one line, heading on the very next」 as a single fact.

Population: every tracked content/docs/**/*.{md,mdx}.

files
defect shape — self-closing JSX tag immediately followed by an ATX heading, pre 4fields/date.mdx, fields/rich-text.mdx, fields/text.mdx, fields/textarea.mdx
same, post 0
CONTROL — healthy shape (tag + true blank line + heading), pre 129 ← HITS

⭐ The control is what makes the 0 meaningful: a run where the healthy shape also read 0 would be a broken regex, ⛔ not a clean corpus.

Leg 2 — ⭐ the inserted BYTES, not merely 「the file changed」

⚠️ This is the leg that could silently fail: a whitespace-only line is not a blank line to CommonMark's HTML-block rule, so inserting spaces instead of an empty line would look like a fix and change nothing.

file separator pre separator post bytes
date.mdx b'\n' b'\n\n' +1
rich-text.mdx b'\n' b'\n\n' +1
text.mdx b'\n' b'\n\n' +1
textarea.mdx b'\n' b'\n\n' +1

NEGATIVE control: files whose separator is LF + spaces/tabs + LF0. Diff is 4 files, 4 insertions, every one an empty line. ⛔ Nothing else touched.

Leg 3 — the divergence set was RE-DERIVED, and the corpus moved under it

⭐ The dev re-derived rather than inheriting, and the check earned itself: the corpus moved2984 rendered headings on their base against the 2981 the card read hours earlier (+3) — while the divergence set stayed exactly those four. ⇒ no fifth file appeared, none dropped out. ⛔ A card's population figure is a reading with a date on it, and this one was re-taken.

Population named precisely: 223 files = content/docs/**/*.{md,mdx} (184) + packages/*/README.md (39), YAML frontmatter stripped — ⭐ because leaving it in makes the closing --- a setext h2 in every frontmatter file and would have faked ~144 divergences. That is a reader chosen to match the question.

Leg 4 — BOTH renderers, in opposite directions

react-markdown (MarkdownImpl, what apps/console uses) — absent → present, all four, each at the index extractToc already claimed:

date.mdx      [basic-usage, with-default-value, |field-schema|, date-formats, use-cases, cell-renderer, datetime-variant]   idx 2
rich-text.mdx idx 2   ·   text.mdx idx 4   ·   textarea.mdx idx 3

@mdx-js/mdx (what apps/site uses) — NON-change, id lists printed both sides, byte-identical for all four (7/10/7/6 ids, all non-empty). ⭐ This leg agrees on both sides, so per this seat's standing rule it carries its printed lists as the guard rather than a bare 「no change」.

⇒ the card's claim that the insertion costs apps/site nothing is verified, ⛔ not inherited.

LIT control with a known direction: of 26 field pages carrying a ## Field Schema heading, the 22 that already had the blank line emitted field-schema in the PRE run (22/26), rising to 26/26 POST. ⇒ the PRE zero on the four targets is a measurement, ⛔ not a dead instrument. NONSENSE control field-schema-zzz-not-a-real-anchor: 0 in every run.

extractToc agreement POST is exact and corpus-wide — the divergence set over all 223 files is empty.

⭐ Why the docs changed and not the renderer

MarkdownImpl is the one following CommonMark correctly — an HTML block ends only at a blank line, so the tag's block swallowed the heading. @mdx-js/mdx closes a self-closing MDX JSX flow element immediately. ⇒ the renderer that disagreed is the correct one, and the docs were authoring a shape it cannot turn into a heading. objectui#7666's triage already priced fixing extractToc instead and routed it here. ⛔ packages/plugin-markdown, apps/site, fumadocs-mdx and all config: untouched.

⭐ A gate distinction the dev got right, and most get wrong

check:doc-snippets and check:doc-examples first returned exit 2, whose own output reads 「PRECONDITION NOT MET — the snippet program was NOT run」 (packages unbuilt). They ⛔ did not report that as a red gate — they read it as NOT MEASURED, ran the scoped build the gate itself names, and only then recorded PRE 0 / POST 0 (650 of 650 blocks judged; 124 blocks). ⭐ An unrun gate is not a passing gate and is not a failing one — conflating either way is how a probe lies.

CI on 6386d9dfd4

32 runs — all 9 required green, and ZERO other non-green. ⭐ Not even Bundle Analysis, which is red across the rest of this lane. check-changeset-presence exits 0 (「no changeset is owed」 — 4 files changed, 0 of them published source). check-governed-queue-guard --test: NOT GOVERNED.

⚠️ Carried forward — ⛔ NOT filed, ⛔ not fixed

  1. Nothing mechanically prevents the shape returning. The divergence set is 0/223 today, but no gate reads it: toc-anchor-parity.test.tsx pins extractToc-vs-renderer parity on synthetic heading strings only, so a fifth field page authored with the tag flush against its heading reopens this silently. A corpus-level parity gate — the exact 223-file scan this card ran, asserting the divergence set is empty — would close the class. ⚠️ This is the THIRD 「a defence that works, and nothing watches it」 finding on this lane today (objectui#9422; objectui#3535's residual; this). Needs its own card.
  2. objectui#7666 is now fully settled by this measurement — ⛔ triage owns re-grading or closing it; the dev correctly did not touch it.
  3. This repo has no scripts/pm/os-verify-lock.sh (an objectstack artifact), so the heavy scoped build ran without the shared lock — worth knowing with parallel seats in one container.

⇒ Flipping ready and enqueueing once the governed guard settles.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 13, 2026 21:53
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 13, 2026
Merged via the queue into main with commit 63d9ca6 Sep 13, 2026
34 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-9427-field-page-blank-line branch September 13, 2026 22:11
@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

✅ LANDED — 63d9ca6f47

Merged through the queue at 2026-09-13T22:11:42Z. Timeline: added_to_merge_queue 21:54:31Z → merged 22:11:42Z.

⛔ ERRATA 62b — re-confirmed against the ACTUAL <merge>^

merge commit : 63d9ca6f47  docs(fields): blank line before `## Field Schema` on four field pages (#9435)
<merge>^     : fb097b8479  fix(scripts): scanSource's @returns declares the interpolation array it returns (#9434)

<merge>^ is this seat's own previous landing again.

Re-taken on the real parent, and on main

pre (fb097b8479) post (on main)
defect shape — self-closing JSX tag immediately followed by an ATX heading 4fields/date.mdx, rich-text.mdx, text.mdx, textarea.mdx 0
CONTROL — healthy shape (tag + true blank line + heading) 129 files ← HITS 129 files

⚠️ A precision note on my control, stated rather than glossed: it counts files containing at least one healthy instance, so it reads 129 on both sides — those four files already carried other healthy tag/heading pairs elsewhere. The dev's version counted occurrences (250 → 254, +4) and is the finer instrument. Mine establishes the defect reached zero; ⛔ it cannot see the +4, and I am not claiming it does.

Separator bytes on main, all four: b'\n\n' — a true empty line. ⚠️ This is the leg that could have silently failed: a whitespace-only line is not a blank line to CommonMark's HTML-block rule, so spaces would have looked like a fix and changed nothing. Negative control for that shape: 0. Merge diff: 4 files, 4 insertions, every one an empty line.

What the fix settles

MarkdownImpl (react-markdown, apps/console's DocPage) follows CommonMark's HTML-block rule — a block ends only at a blank line — so the tag's block swallowed the heading and console emitted no field-schema anchor while extractToc still listed one. @mdx-js/mdx (apps/site) closes a self-closing MDX JSX flow element immediately and never swallowed it.

⇒ ⭐ the renderer that disagreed is the one reading CommonMark correctly, so the docs changed, not the renderer. objectui#7666's triage had already priced fixing extractToc instead and routed it here. ⛔ packages/plugin-markdown, apps/site, fumadocs-mdx and all config: untouched.

Both renderers were proven, in opposite directions: react-markdown absent → present on all four at the indices extractToc already claimed, and @mdx-js/mdx id lists byte-identical before and after — ⭐ that second leg agrees on both sides, so it carried its printed id lists as its guard rather than a bare 「no change」. LIT control with a known direction: 22 of 26 field pages already emitted field-schema pre, rising to 26/26 post.

⭐ The corpus moved under the card, and the re-derivation caught it

The dev re-derived the divergence set rather than inheriting it: 2984 rendered headings on their base against the 2981 the card read hours earlier (+3), while the divergence set stayed exactly those four. ⇒ no fifth file appeared, none dropped out. ⛔ A card's population figure is a reading with a date on it.

Population named precisely: 223 files = content/docs/**/*.{md,mdx} (184) + packages/*/README.md (39), frontmatter stripped — ⭐ because leaving it in makes the closing --- a setext h2 in every frontmatter file and would have faked ~144 divergences.

⭐ A gate distinction worth copying

check:doc-snippets and check:doc-examples first returned exit 2, whose own output reads 「PRECONDITION NOT MET — the snippet program was NOT run」. The dev ⛔ did not record that as a red gate — they read it as NOT MEASURED, ran the scoped build the gate itself names, and only then recorded PRE 0 / POST 0. ⭐ An unrun gate is neither passing nor failing; collapsing it into either is how a probe lies.

⚠️ Carried forward — ⛔ NOT filed here, and being routed

  1. Nothing mechanically prevents the shape returning. The divergence set is 0/223 today, but no gate reads it: toc-anchor-parity.test.tsx pins parity on synthetic heading strings only, so a fifth field page authored with the tag flush against its heading reopens this silently. ⚠️ Third 「a defence that works, and nothing watches it」 finding on this lane today — objectui#9422, objectui#3535's residual, and this. Being routed as its own card.
  2. objectui#7666 is now fully settled by this measurement — ⛔ triage owns re-grading or closing it; the dev correctly did not touch it.
  3. This repo has no scripts/pm/os-verify-lock.sh (an objectstack artifact), so the heavy scoped build ran without the shared lock — worth knowing with parallel seats in one container.

Generated by Claude Code

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

Labels

None yet

Projects

None yet

2 participants