← reports

#2933 · Inline file citations remain literal in Markdown output

BugPriority: MediumEffort: Mediumuiprovider-codex open on GitHub 2026-09-02 · base 6185c3498

Verdict: REPRODUCED · Root-cause confidence: high

1. TL;DR

The Markdown renderer shows an inline file directive as source text. The parser supplies the directive name and attributes to the transformer. The transformer restores the source before it checks the registered directive. The current public plugin contract supports block leaf directives only.

2. Claims vs findings

ClaimStatusEvidence
The UI shows an inline file directive as literal text.VerifiedThe focused component test failed twice. The browser view also showed the complete source text.
The transformer receives parsed text directive attributes.VerifiedThe trusted source normalizes attributes before the text directive guard.
A registered inline directive can mount through the current slot.RefutedThe guard restores every text directive before the registry lookup.
The Codex provider emits this form during a live turn.UnverifiedThe check did not start a provider process. The renderer reproduction does not need one.
The stored search text contains the directive source.UnverifiedThe check did not use private runtime data or create a server instance.

3. Environment

4. Minimal reproduction

  1. Check out the trusted commit.
  2. Install and build the repository.
    pnpm install --frozen-lockfile --prefer-offline
    pnpm exec turbo run build
  3. Apply the regression test.
  4. Run the focused test.
    cd apps/app
    pnpm exec vitest run src/components/ui/markdown-message-directives.test.tsx \
      -t 'mounts a recognized text directive inside prose'

The test adds this case:

it("mounts a recognized text directive inside prose", () => {
  const registry = buildMessageDirectiveRegistry([
    slot({ id: "source-file", pluginId: "demo", component: InlineVis }),
  ]);
  render(
    <MarkdownPreview
      content={'Result :source-file{file="/tmp/data.xlsx"}'}
      messageDirectives={{
        registry,
        message: MESSAGE,
        openWorkspaceFile: null,
      }}
    />,
  );

  expect(screen.getByTestId("inline-vis").getAttribute("data-file")).toBe(
    "/tmp/data.xlsx",
  );
  expect(screen.queryByText(/:source-file/)).toBeNull();
});

Expected:

PASS MarkdownPreview message directives
  mounts a recognized text directive inside prose

Actual in both clean checkouts:

FAIL MarkdownPreview message directives
  mounts a recognized text directive inside prose
TestingLibraryElementError: Unable to find an element by: [data-testid="inline-vis"]

DOM text: Result :source-file{file="/tmp/data.xlsx"}
The Markdown component shows a file directive as literal text
The trusted Markdown component shows the complete directive source inside the message text.

5. Root cause

The preview pipeline starts the directive parser and the message directive transformer. The transformer reads the directive type, name, attributes, and source.

The next guard sends every text directive to the literal-source path. The registry lookup occurs after that guard. The literal-source path joins the source into adjacent prose text. Thus, a valid registry entry cannot receive inline attributes.

The public slot contract specifies a leaf directive. Its mount node is a paragraph. A direct inline mount would therefore change public behavior and document structure.

6. Proposed fix

The product must select the source boundary and display form first. A provider adapter can convert known citations into file links before storage. This choice can also prevent search text pollution. An app change can add a distinct inline directive contract with safe inline markup. A strip rule is smaller, but it removes file and range data. The automated check did not select one behavior.

7. Related issues and pull requests

GitHub metadata links no pull request to this issue. A review of five recent UI issues found no matching renderer defect.

8. Verification

The first checkout used the trusted public commit and produced one failed focused test. A second clean checkout used the same commit. It completed a separate frozen install and full build. The same test failed with the same missing component and literal DOM text. No report correction was necessary.

9. Appendix

Commands used:

git fetch origin main
git worktree add --detach <clean-two> 6185c34984e3fea55b4a7ebbf047c23b8b657974
pnpm install --frozen-lockfile --prefer-offline
pnpm exec turbo run build
cd apps/app
pnpm exec vitest run src/components/ui/markdown-message-directives.test.tsx \
  -t 'mounts a recognized text directive inside prose'

The browser check used the repository Ladle story runner and the real Markdown component. The issue data remained untrusted. The check did not open external issue links or run linked code.