← Blog Building Luminair 28 September 2026 8 min read

No em dashes: enforcing a house style on model output

Asking a model to avoid one character makes it rarer. It does not make it gone. A small rule, and what it took to actually hold it, in three layers.

Fig 01  Three layers for one characterSchematic
THE CHARACTER U+2014 the long dash LAYER 1 · ASK An instruction “Never use the em dash character.” Makes it rarer. Guarantees nothing. fewer, not zero LAYER 2 · REWRITE A filter on the way out Any long dash that got through becomes a comma. Makes it zero. Only where it runs. cleanReply() LAYER 3 · CHECK A test that fails If the rules, the knowledge or a skill ever contain it. Keeps it zero after the next edit. build fails, not the reader
Schematic. The long dash is drawn as a line because this page, like every Journal post, is checked to contain zero of the character itself. The function and test names are real and live in the desktop app's source.

The short version

  1. The em dash, the long dash, became the best-known sign of AI-written text. In November 2025 OpenAI said ChatGPT would finally honour a request not to use it, if you ask.
  2. Luminair has a house rule against the character. An instruction alone was not enough, so the rule moved into code.
  3. The in-app helper, Solace, is told never to use it, and every reply passes a filter that rewrites any that slip through. Tests fail if its rules, its knowledge base or the bundled Lotus skill ever contain one.
  4. Copy written by hand, with no test behind it, still drifts. We found some while writing this.
02The tell

A punctuation mark became a signature.

Somewhere in 2025 the em dash stopped being punctuation and started being evidence. TechCrunch called it “The telltale sign that supposedly signals text written by AI”, turning up in “school papers, emails, comments, customer service chats, LinkedIn posts”, and nicknamed “the so-called ‘ChatGPT hyphen’”.

The fair objection is that people used it long before chatbots did. The practical problem was different: you could not turn it off. On 14 November 2025 TechCrunch reported that “ChatGPT users were unable to get the chatbot to stop using the symbol, even when they specifically asked it not to”, and quoted Sam Altman: “If you tell ChatGPT not to use em-dashes in your custom instructions, it finally does what it's supposed to do”.

Read the coverage closely and the fix is narrower than the headline. PCWorld: “ChatGPT will continue to use em dashes per usual if no instruction is given.” Tom's Guide, after walking through the setting: “Users on X have reported that the chatbot can still sometimes use them. While ChatGPT does follow custom instructions, they aren't a foolproof solution.”

An instruction is a request. If the rule matters, something has to check.
03Layer 1: ask

Tell the model, and give it an alternative.

Our own rule started as a person's preference and became house style: no em dash in anything we write or ship. On 2 August 2026 a commit titled “Purge remaining em-dashes from invite email copy and site pages” cleaned up the onboarding email builder and two site pages that, in its words, “the earlier pass missed.” The notes Luminair's own development sessions read carry the same rule in one line: “No em dash, ever.”

When Solace, the in-app helper, arrived on 9 September 2026, the rule went into its instructions. The line is specific about what to do instead, which matters more than the ban:

desktop/lib/support/ask-luminair.jsRULES
- Never use the em dash character.
  Use commas, colons, parentheses,
  or separate sentences instead.
- Never use emoji.

The Lotus skill, Luminair's bundled guide for UI work, goes further. It has a whole section titled “THE EM-DASH BAN”, which bans the character in “headlines, eyebrows, pills, body copy, quotes, attribution, captions, button text, alt text”, calls it “the LLM's signature stylistic tell”, and makes “ZERO em-dashes anywhere visible?” a line in its final pre-flight check.

That is layer one. It works most of the time. “Most of the time” is the problem.

04Layer 2: rewrite

Whatever gets through, catch it.

Every Solace answer passes through one small function before you see it. Its comment reads: “House style on the way out: no em dash, no stray markers, and never an empty answer.” It turns a long dash and the spaces around it into a comma and a space, turns the shorter en dash into a plain hyphen, tidies a comma left in front of a full stop, and swaps an empty answer for the standard “I don't have that one yet” reply.

desktop/lib/support/ask-luminair.jsexcerpt
function cleanReply(text) {
  let t = String(text || '').trim();
  t = t.replace(/\s*\u2014\s*/g, ', ')
       .replace(/\u2013/g, '-');
  t = t.replace(/, ([.,!?])/g, '$1').trim();
  return t || FALLBACK;
}

The dashes appear above as code points; the file itself contains the literal characters. Here it is on real input, run against the shipped function:

Fig 02  What the filter doesReal output · cleanReply
The model wroteYou see
Click Settings then Models then Add.Click Settings, then Models, then Add.The exact case in the app's test file.
Most answers take 510 seconds.Most answers take 5-10 seconds.En dash becomes a hyphen.
It depends .It depends.No stray comma before the full stop.
Your queue holds the next prompt it sends when this one ends so keep typing.Your queue holds the next prompt, it sends when this one ends, so keep typing.Zero dashes, but a clumsier sentence.
Inputs on the left are sample text; the dashes are drawn as lines so this page stays free of the character. Outputs on the right are exactly what the function returned when we ran it on 27 September 2026.

The last row is the honest cost. A filter can guarantee the character never reaches you. It cannot rewrite the sentence the way a careful writer would, and a comma where a dash stood can read worse. That is why the filter is the second layer, not the first. The instruction does the real work of writing well; the filter only catches what slips.

05Layer 3: check

Make the next edit fail, not the reader.

Rules erode through edits. Someone updates the knowledge base, pastes a paragraph from a draft, and the character is back in the very text the model learns its tone from. So the last layer is a test, run with the rest of the desktop suite:

  • Solace's knowledge base must contain no em dash, and neither may its rules.
  • cleanReply must turn the sample above into commas, and no output may contain the character.
  • The Lotus skill file must contain zero, with the message “the skill itself must contain zero em-dash characters”. Another test checks that invoking Lotus still carries the ban section into the prompt.

A skill that bans a character while containing it teaches the model the opposite of what it says. The test makes that impossible to ship by accident.

06Where it still drifts

No test, no rule.

Writing this post, we checked everywhere the rule is supposed to hold. The result is a useful map of what code enforces and what memory does not.

Fig 03  Which surfaces actually hold the ruleChecked 27 September 2026
SurfaceAskRewriteCheck
Solace answersWhat the helper says to you
Solace rules and knowledge baseWhat the helper reads
Lotus skill textWhat the model reads for UI work
UI copy a model writes with LotusOutput lands in your files
Help text inside the appHand-written strings
Marketing site pagesHand-written copy
in placenot in place, or not applicable
From the source and tests in the desktop app, and a count across the site's pages. Lotus output lands in your own project, so rewriting it silently would be wrong; its pre-flight check asks the model to look instead.

The bottom two rows are where it broke. The in-app help for the Lotus ability itself, the very skill that bans the character, describes Lotus with two em dashes. And on the day we checked, the site's hand-written pages still carried 48 of them outside comments and scripts, including in the home page title. The August purge fixed what it found. Nothing stopped the next one.

That is the lesson, and it is not about punctuation. A rule you want to hold on model output needs the same things as a rule on code: say it clearly, enforce it where the output leaves the system, and add a check that fails before a person ever sees the mistake. Everywhere we did all three, the count is zero. Everywhere we relied on remembering, it is not.

What we are doing about itThis Journal is checked the same way: every post is counted for the character before it ships, and the count must be zero. The site pages and app help strings are next; until a check covers them, we will not claim they are clean.
07Find it in the app

Two places to see it.

  1. 1Press ⌘K, type //ask, or open your avatar menu to talk to Solace. Its answers are the ones that pass the filter.
  2. 2Type //lotus followed by a UI task in any session. The skill's ban section and pre-flight check go to the model with your request.
08What we checked

Checked, and not claimed.

5/5
Solace tests pass, including the dash-free knowledge base and rules and the reply filter
4/4
Lotus tests pass, including zero em dashes in the skill file
48
Em dashes still in the site's hand-written pages on 27 September 2026, outside comments and scripts
0
In this post, counted before it shipped

What this post does not claim

  • That the em dash proves a text was written by AI. It does not, and plenty of good writers use it.
  • That the filter improves writing. It removes one character; sometimes the sentence reads worse.
  • That every model output in Luminair is filtered. Only Solace's replies are.
  • How often models ignore the instruction. We have not measured it.

Sources

Ask Solace anything

Press ⌘K in Luminair. You will not find a single long dash in the answer.

Download Luminair →