Skip to content

Editing a document that contains an indented MDX component block…#177

Merged
inkeep-oss-sync[bot] merged 1 commit into
mainfrom
copybara/sync
Jun 17, 2026
Merged

Editing a document that contains an indented MDX component block…#177
inkeep-oss-sync[bot] merged 1 commit into
mainfrom
copybara/sync

Conversation

@inkeep-oss-sync

Copy link
Copy Markdown
Contributor

Editing a document that contains an indented MDX component block (fumadocs <Steps>/<Step>, <Tabs>/<Tab>, or any component authored with indented markdown children) no longer corrupts the document. The serializer used to rewrite an edited block's children flush-left with extra blank lines, a shape the collaboration bridge could not reconcile with the on-disk source, so under concurrent editing the document could grow without bound until it crossed the open-size limit and refused to reopen. Edited blocks now preserve the canonical two-spaces-per-level indentation (the fumadocs/Obsidian convention) exactly, and an unchanged structured attribute such as items={["npm", "bun"]} is no longer rewritten with different whitespace. Blocks authored with a different indentation width normalize to the two-space form on the first edit and then stay stable — a one-time change, not the previous unbounded growth.

* [wip] claim PRD-7110: CRDT bridge bloat on indented MDX <Steps> (fix-bug)

* test(open-knowledge): RED tests for indented-JSX bridge fixed-point (PRD-7110)

Pin the contract that the gamma dirty serializer must reach a byte-stable
fixed point WITHIN normalizeBridge tolerance for indented-children MDX JSX
blocks (Steps/Step, Tabs/Tab, custom components).

The existing I13 idempotence PBT misses this class: flush-left output is
already a serializer fixed point while still being beyond normalizeBridge
tolerance, and loadBuiltInFixtures carries no indented-children JSX shape
(the Steps fixture was rewritten flush-left). The three within-tolerance
assertions fail now and should pass once the serializer reaches a
within-tolerance fixed point. The byte-stable fixed-point guards pass now and
must stay green. No production code changed.

* fix(open-knowledge): keep indented MDX JSX children within bridge tolerance on edit

The mdxJsxFlowElement dirty-path serializer rebuilt a component block's
children flush-left with blank-line wrapping, dropping the authored
2-space-per-depth indentation of fumadocs <Steps>/<Step>, <Tabs>/<Tab>,
and any component with indented markdown children. The flush form is
beyond normalizeBridge tolerance versus the indented source, so the
server-authoritative bridge never settled per drain and re-derived under
divergence, growing the Y.Doc past the 512 KiB open limit until the
document refused to load.

Changes:
- Group 1: delegate the children-bearing dirty path to the upstream
  mdast-util-mdx-jsx flow serializer for depth-based indentation and
  single-newline wrapping (the authoring form the comparator accepts).
  Pre-normalize the one attribute case where upstream diverges from OK
  (a double quote inside a string value) through a shared helper so
  attribute bytes stay identical to serializeMdxJsxAttrs. Correct the
  obsolete docstring: micromark-extension-mdx-jsx consumes the
  container-relative leading whitespace, so indented children are not
  read as a CommonMark indented code block.
- Group 2: in reconstructAttrs, keep a preserved raw expression
  attribute when the structured prop value is parse-equal to it, so
  re-serializing an unchanged attribute (items={["npm", "bun"]}) does
  not mint a within-meaning-but-different byte form.

Pinned by the indented-children bridge fixed-point tests in
invariant-i13.test.ts.

Refs PRD-7110.

* fixup! local-review: address findings (pass 1)

* fixup! local-review: address findings (pass 2)

* test(open-knowledge): lock in <details> serializer safety as PRD-7110 sibling

The html-boundary <details> (HtmlDetailsAccordion) path shares the gamma
path's synthetic-root-flush shape but is NOT subject to the bug: canonical
<details> bodies are flush-left, so depth-0 flush is the correct, within-
normalizeBridge-tolerance form. Applying the gamma indent fix here would be
harmful (serializes '### H' -> '  ### H', which normalize.ts step 7f does not
fold). Adds green coverage + anti-regression guards so a future refactor can't
wrongly route <details> through the indenter.

* test(open-knowledge): address PR 1904 cloud-review polish (PRD-7110)

Non-blocking suggestions from the PR 1904 review (APPROVE WITH SUGGESTIONS,
1 Minor + 6 Consider). No behavior change; the landed fix stays as-is.

Substantive item (Consider 1): empirically resolved whether the upstream
mdast-util-mdx-jsx delegation path entity-encodes more than the double quote.
It does not. The full divergence set between OK's serializeMdxJsxAttrs and the
upstream handler is exactly the double quote, confirmed three ways: upstream
calls stringifyEntitiesLight(value, subset:['"']) (default quote, no options),
stringify-entities only escapes the subset, and direct probes show &, <, >, ',
braces, backslash, backtick, newline all pass through byte-identical. Kept a
regression guard (string-attr entity divergence on the delegation path) that
fails loud if a library bump ever widens the subset, and tightened the
normalizeAttrForUpstream docstring to state the verified set. No fix extension.

Other items:
- Minor: explicit positive-path test for expressionMatchesValue (an unchanged
  JSON-array expression attr is kept verbatim, not recompacted), instead of
  only transitive coverage via the Tabs fixture.
- Consider 2: reuse the TO_MARKDOWN_EXT singleton (now exported from
  remark-mdx-agnostic.ts) for the dirty-path delegation instead of minting a
  fresh mdxToMarkdown() instance, so the dirty path and the pipeline can no
  longer drift. Cycle-safe; remark-mdx-agnostic is a leaf module.
- Consider 3: added a depth-3 nested-component fixture (6-space indent) to
  exercise upstream inferDepth past the existing depth-2 fixtures.
- Consider 4: restated the line-309 idempotence comment as a standing invariant
  and dropped the now-stale flush-left wording.
- Consider 5: removed PRD-7110 ticket refs from pure comments per the repo
  comment-discipline rule, keeping them only in describe() names (load-bearing
  test selector).
- Consider 6: rewrote the "reported trigger" fixture comment as a behavioral
  description.

Verification: invariant-i13 PRD-7110 scope 19 pass / 0 fail (was 6); full file
47 pass; bun run check green (23/23, fidelity 1302, md-conformance 1490, 0 fail);
bun run lint clean. No STOP-ruled files touched (built-ins.json, normalize.ts,
server-observers.ts); no normalizeBridge tolerance change; no test weakened.
GitOrigin-RevId: c8fb42ed509587f044bee39016b5d9f2f2e1af55

@inkeep-internal-ci inkeep-internal-ci Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated approval from agents-private public-mirror-sync (run: https://github.com/inkeep/agents-private/actions/runs/27677717636). Source of truth is the monorepo; direct edits on inkeep/open-knowledge are overwritten on next sync.

@inkeep-oss-sync inkeep-oss-sync Bot merged commit 53f3528 into main Jun 17, 2026
@inkeep-oss-sync inkeep-oss-sync Bot deleted the copybara/sync branch June 17, 2026 09:02
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