[Bug]: Inline code like `I/W217:220` renders as a file chip because a `:line` suffix skips the extension check#22181

opened8d agobyAardaaltinors

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/web

The classifier lives in packages/shared/src/markdownLinks.ts.

Steps to reproduce

  1. Open any thread.

  2. Have an assistant response use spreadsheet cell shorthand in inline code, for example:

    markdown
    1The V5 file includes the `I/W217:220` fix (columns I and W, rows 217–220).
  3. Read the rendered message, then click the I/W217:220 span.

For a direct check on main @ b69703ccc240b9ecc6582be024f199d6d8dd44da:

ts
1inlineCodeFilePathCandidate("I/W217:220"); // "I/W217:220"
2inlineCodeFilePathCandidate("A/B12:40"); // "A/B12:40"
3inlineCodeFilePathCandidate("W217:W220"); // null
4inlineCodeFilePathCandidate("E29:E31"); // null

Expected behavior

I/W217:220 renders as ordinary inline code with its full text. Nothing in the span looks like a file: there is no extension, the basename is not a known extensionless file name, and there is no ./, ~/, or absolute prefix.

Actual behavior

The span renders as a file chip labeled W217 · L220. The I/ prefix and the :220 row range are gone from the visible text, so the sentence no longer says what the agent wrote. Clicking the chip opens the file preview at <workspace>/I/W217, which does not exist:

1The file could not be accessed or read. It may be missing or inaccessible.
2Attempted path <workspace>/I/W217

The path is resolved correctly against the workspace, which is on a remote server in my setup. The problem is only that the span should not be a link at all.

Cause

inlineCodeFilePathCandidate only applies the extension check when there is no :line suffix:

Bare names already have a guard for this. TODO:12, error:1, and port:3000 stay plain code (markdown-links.test.ts "does not treat arbitrary name:digits shapes as files"). Adding one slash bypasses that guard: foo/TODO:12, I/W217:220, and A/B12:40 all become chips.

#13899 fixed the version-shaped basename case (z-ai/glm-5.3:12). This is the remaining case in the same branch: a slashed span with a position suffix and an extensionless basename.

Possible fix

For inline code without an explicit path prefix, require the basename to look like a file even when hasPosition is true. It should have a file extension or be in EXTENSIONLESS_FILE_NAMES, the same rule looksLikePosixFilesystemPath uses for absolute paths. Authored markdown links ([x](I/W217:220)) can keep linking as written.

If extensionless scripts like bin/run:12 need to stay chips, another option is to make a chip from an extensionless inline-code candidate only when the path exists in the workspace index. That index is already used for basename lookup.

Impact

Minor bug or occasional failure

This comes up whenever an agent talks about spreadsheets, grid coordinates, or any X/Y:N shorthand. The rendered text loses information, and the link goes to a file that does not exist.

Version or commit

Desktop 0.1.0 (Beta). Reproduced from source on main @ b69703ccc240b9ecc6582be024f199d6d8dd44da.

Environment

macOS desktop app connected to a T3 server on a second Mac. Provider: Claude. The classifier is provider-independent and runs on the client.

Reproduced in the web dev app on Linux at 70e5e384da, using an assistant-message fixture in isolated state. I/W217:220 displays as W217 · L220; clicking opens the missing-file preview for <workspace>/I/W217. A/B12:40 and foo/TODO:12 also become chips; W217:W220 and E29:E31 remain plain code. No matching open duplicate found.