SIGN IN SIGN UP

feat(docs): gate MDX docs on Crowdin-safe placeholders (#24540)

Tracked as `DOCS-01` on twentyhq/core-team-issues#2784.

### Problem

Crowdin parses `<foo>` in prose as a tag rather than literal text, so an
angle bracket placeholder is dropped or mangled in every translated
page. Curly braces `{foo}` survive the round trip.

A `no-angle-bracket-placeholders` rule used to guard against this. It
ran as `'error'` on `**/*.mdx` in the root `eslint.config.mjs` from
#15751 (Nov 2025) until #18443 migrated us to oxlint. oxlint has no
`eslint-plugin-mdx` equivalent, so the whole `**/*.mdx` block went with
it and the rule stopped running. The file was carried into
`twenty-oxlint-rules` wired to nothing, and #24535 removed it as dead
code. So the guard has been silently off for about five months, not
missing by design.

This restores it in a form that runs without a JSX AST: a plain text
scan, no new dependencies.

### Change

- `scripts/lint-mdx.ts` scans the 204 English source pages and skips
`l/` (the 2613 generated Crowdin files, not hand-editable). A violation
there is only ever a symptom of one in the source.
- The `lint` target was `{}`, an empty no-op, while the `docs-lint` CI
job invoked `nx lint twenty-docs`. So the job has been passing without
running anything. The target now runs the package's own `.oxlintrc.json`
(nothing was running that either, so `scripts/*.ts` is linted for the
first time) followed by the MDX scan.
- A `test` target plus a vitest config for the package, wired into CI.
14 tests.

### On the backtick handling

The scanner skips angle brackets inside code spans, since they are
legitimate there. The first version did this with a running backtick
parity count over the whole file, which is unsound: one unpaired
backtick in prose flips parity and silently suppresses every finding
after it in the file, while the run still prints clean.
`docker-compose.mdx` was already in that state from line 210.

Fixed in 54dff6e4: backtick runs are now paired within a line, by run
length, which is how inline code actually delimits. Three tests pin it,
all of which fail against the old counter.

### Verification

```
$ npx nx test twenty-docs
Tests  14 passed (14)
```

End to end on the real CI command. Seeding two violations into
`getting-started/introduction.mdx`:

```
$ npx nx lint twenty-docs
getting-started/introduction.mdx:78:5    <workspace-slug> reads as a tag in Crowdin, use {workspace-slug} instead
getting-started/introduction.mdx:78:34   <span> reads as a tag in Crowdin, use {span} instead
2 angle bracket placeholder(s) found in 204 MDX files.
nx lint exit=1
```

Unseeded, the tree is clean: `No angle bracket placeholders in 204 MDX
files.`

To be clear about scope: the gate catches nothing in the tree today. Of
the 83 angle brackets in the English source, 81 sit inside code spans
and 2 are a legitimate `kbd` element. It is a preventive guard, plus the
CI wiring fix above. The live corruption (a backslash before angle
brackets inside inline code in the translated output, 494 across 156
files) is a `crowdin-normalizer` concern, not something a source-side
gate can see, and is handled in a separate PR.

### Not in this PR

`DOCS-01` also covered `mdx-component-newlines` (component tags sharing
a line with prose). Deferring it, but not because the current
occurrences are settled idiom: blame shows 137 of them were authored
while that rule was live at `'error'` and only 68 after it went dark, so
it was never really enforcing. Its opening-tag branch early-returns
whenever the following text is adjacent, so a component wrapping text on
one line never tripped it. Porting it faithfully buys nothing without
redesigning what it checks, which is a separate call that stays on
#2784.


<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/24540?utm_source=github"
rel="nofollow noreferrer noopener" target="_blank">``&lt;img alt="Review
in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"&gt;``</a>

---------

Co-authored-by: Abdul Rahman <81605929+abdulrahmancodes@users.noreply.github.com>
Co-authored-by: Abdul Rahman <ar5438376@gmail.com>
F
Félix Malfait committed
9618231dfc8d6d63fa99a7d3fefbcbfe13cd004f
Parent: 0c193d8
Committed by GitHub <noreply@github.com> on 8/24/2026, 8:52:23 AM