Skip to content

fix(ai): validate generated MDX syntax#158

Merged
iipanda merged 1 commit into
mainfrom
codex/fix-ai-mdx-proposal-validation
May 28, 2026
Merged

fix(ai): validate generated MDX syntax#158
iipanda merged 1 commit into
mainfrom
codex/fix-ai-mdx-proposal-validation

Conversation

@iipanda
Copy link
Copy Markdown
Collaborator

@iipanda iipanda commented May 28, 2026

Summary

  • Validate AI-generated MDX body fragments with the same micromark/mdast parser family Studio uses.
  • Reject parser failures as MDX_PARSE_FAILED before proposals are marked valid or applied.
  • Add a regression for single-quoted JSX attributes containing JSON-style backslash apostrophe escapes and document the pitfall.

Test Plan

  • bun test --cwd packages/modules ./core.ai/src/server/validate-proposal.test.ts
  • bun test --cwd packages/modules ./core.ai/src
  • bun run typecheck
  • bun run format:check
  • Production draft b34e07a9-f21c-498c-860b-5e729b90ae35 re-fetched and parsed locally after manual repair.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Enhanced AI proposal validation to properly parse and validate MDX content, preventing invalid proposals from being accepted.
    • Proposals with unparseable MDX now receive rejection with detailed error messages.
  • Tests

    • Added test coverage for MDX validation in proposal operations.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

This PR adds Studio MDX parser-based validation to the AI proposal validator. Proposals with body MDX content are now parsed using the same parser Studio uses, rejecting proposals with syntax errors. Validation applies to all operation types (create_document, delete_document, replace_selection, insert_block). Three MDX parsing libraries are added as dependencies.

Changes

MDX Parsing Validation in Proposals

Layer / File(s) Summary
Dependencies and imports
packages/modules/package.json, packages/modules/core.ai/src/server/validate-proposal.ts
Runtime dependencies for MDX parsing (mdast-util-from-markdown, mdast-util-mdx, micromark-extension-mdx) are added; the validator imports the new parsing utilities.
MDX parsing validation pipeline
packages/modules/core.ai/src/server/validate-proposal.ts
Introduces validationFromErrors, validateMdxTargetsAgainstStudioParser, and formatMdxParseFailure to parse MDX, catch parser errors, and translate them into structured ValidationError entries with line/column context.
Validator integration for all operation types
packages/modules/core.ai/src/server/validate-proposal.ts
Updates create_document to merge schema validation with MDX parsing validation; changes delete_document, replace_selection, and insert_block to perform MDX parsing checks instead of unconditionally returning valid; adds clarifying comment on validation layering.
Test coverage for MDX validation
packages/modules/core.ai/src/server/validate-proposal.test.ts
Test case verifies that insert_block proposals with unparseable MDX are rejected, with error code MDX_PARSE_FAILED targeting operations[0].bodyMdx.
Design rationale documentation
.ai/memory/lessons.md
Lessons entry documents why lightweight prop/tag scanning is insufficient and prescribes full Studio MDX parser validation, noting JSX escaping edge cases (e.g., single-quoted attributes with apostrophes) that lightweight checks miss.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 With whiskers twitching, I parse and frown,
MDX escapes that break Studio down,
No more lightweight checks will do—
The Studio parser sees what's true,
Apostrophes escaped, proposals blessed! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly reflects the main purpose of the changes: adding MDX syntax validation to AI proposal validation logic.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-ai-mdx-proposal-validation

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/modules/core.ai/src/server/validate-proposal.test.ts (1)

433-467: ⚡ Quick win

Broaden MDX parse-failure regression coverage across MDX-writing operations.

This new test is solid for insert_block; please add matching parse-failure cases for create_document.body and replace_selection.replacementText so the new cross-operation contract is locked in.

Suggested test pattern
+  test("rejects create_document body MDX that the Studio parser cannot parse", async () => {
+    const validator = createSchemaAwareProposalValidator({ schemaLookup: lookup });
+    const result = await validator(
+      createCandidate({
+        type: "page",
+        operations: [
+          {
+            op: "create_document",
+            path: "pages/feature-grid",
+            format: "mdx",
+            frontmatter: { title: "Feature Grid" },
+            body: '<FeatureGrid features=\'[{"title":"Drop","description":"Once they\\\'re gone, they\\\'re gone."}]\' />',
+          },
+        ],
+      }),
+    );
+    assert.equal(result.status, "invalid");
+    if (result.status === "invalid") {
+      assert.ok(result.errors.some((e) => e.code === "MDX_PARSE_FAILED" && e.path === "operations[0].body"));
+    }
+  });
+
+  test("rejects replace_selection replacementText MDX that the Studio parser cannot parse", async () => {
+    const validator = createSchemaAwareProposalValidator({ schemaLookup: lookup });
+    const result = await validator({
+      proposalId: "p1",
+      kind: "replace_selection",
+      project: "demo",
+      environment: "draft",
+      type: "page",
+      locale: "en",
+      summary: "Replace block",
+      operations: [
+        {
+          op: "replace_selection",
+          selectionId: "sel_1",
+          originalText: "old",
+          replacementText:
+            '<FeatureGrid features=\'[{"title":"Drop","description":"Once they\\\'re gone, they\\\'re gone."}]\' />',
+        },
+      ],
+      expiresAt: "2026-05-15T00:05:00.000Z",
+      provider: { providerId: "echo", model: "echo-1", promptTemplateId: "chat_tools.v1" },
+    });
+    assert.equal(result.status, "invalid");
+    if (result.status === "invalid") {
+      assert.ok(result.errors.some((e) => e.code === "MDX_PARSE_FAILED" && e.path === "operations[0].replacementText"));
+    }
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/modules/core.ai/src/server/validate-proposal.test.ts` around lines
433 - 467, Add two more tests mirroring the existing "rejects insert_block
MDX..." case: create one that builds a proposal with kind "create_document" (or
operation.op "create_document") using an invalid MDX string in
create_document.body and another with kind "replace_selection" (operation.op
"replace_selection") using an invalid MDX string in
replace_selection.replacementText; call the same
createSchemaAwareProposalValidator/validator and assert result.status ===
"invalid", then assert the first error has code "MDX_PARSE_FAILED", path
"create_document.body" (for the create_document case) or
"operations[0].replacementText" (for replace_selection) and that the
error.message matches /Studio MDX parser/ to match the existing pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/modules/core.ai/src/server/validate-proposal.test.ts`:
- Around line 433-467: Add two more tests mirroring the existing "rejects
insert_block MDX..." case: create one that builds a proposal with kind
"create_document" (or operation.op "create_document") using an invalid MDX
string in create_document.body and another with kind "replace_selection"
(operation.op "replace_selection") using an invalid MDX string in
replace_selection.replacementText; call the same
createSchemaAwareProposalValidator/validator and assert result.status ===
"invalid", then assert the first error has code "MDX_PARSE_FAILED", path
"create_document.body" (for the create_document case) or
"operations[0].replacementText" (for replace_selection) and that the
error.message matches /Studio MDX parser/ to match the existing pattern.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 91a076f6-ac06-444a-8c58-8aac5041e59c

📥 Commits

Reviewing files that changed from the base of the PR and between 19cafb3 and 250c309.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • .ai/memory/lessons.md
  • packages/modules/core.ai/src/server/validate-proposal.test.ts
  • packages/modules/core.ai/src/server/validate-proposal.ts
  • packages/modules/package.json

@iipanda iipanda merged commit ee1789d into main May 28, 2026
6 checks passed
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