fix(title): avoid restoring emphasized words as acronyms

Narrow acronym restoration so plain all-caps English words such as FIX
and WORK do not get restored when the model naturally capitalizes the
first title word. Restorable all-caps source tokens now need a stronger
acronym signal: a common technical acronym allowlist, digits, or a
consonant-only shape.

This keeps CNPG, ETL, JWT, SQL, and API restoration while preserving the
anti-shout behavior for single emphatic words.

Fixes #4220
This commit is contained in:
roboomp
2026-07-02 07:02:26 +00:00
parent 76ac419aaa
commit bc5fbb4eb6
2 changed files with 63 additions and 14 deletions
+51 -14
View File
@@ -152,6 +152,33 @@ const FILLER_TITLE_TOKENS = new Set<string>([
]);
const TITLE_WORD = /[\p{L}\p{N}]+/gu;
const COMMON_TITLE_ACRONYMS = new Set<string>([
"API",
"CLI",
"CPU",
"CRUD",
"CSS",
"DNS",
"GPU",
"HTML",
"HTTP",
"HTTPS",
"ID",
"JSON",
"LLM",
"REST",
"SDK",
"SSH",
"TCP",
"TLS",
"TUI",
"UI",
"URI",
"URL",
"UX",
"XML",
"YAML",
]);
/**
* True when a first user message is too low-signal to title (greeting, ack,
@@ -200,19 +227,18 @@ export function normalizeGeneratedTitle(value: string | null | undefined, source
* 2. else the message has the same word with *distinctive* mixed casing
* (`TinyVMM`, `iOS`, `IDs`) → adopt the user's casing (restoration);
* 3. else the model produced a plain title-cased artifact (`Cnpg`) whose
* lowercased form is an ALL-CAPS acronym in a non-shouty source → restore
* the source acronym;
* lowercased form is a likely ALL-CAPS acronym in a non-shouty source
* (`CNPG`, `API`, `ETL`) → restore the source acronym;
* 4. else it's a camelCase artifact (lowercase word + stray interior capital,
* `dAemon`) the user never wrote → lowercase it;
* 5. else leave it — preserves model-cased proper nouns like `GitHub`, `OAuth`.
*
* Restoration is limited to avoid two failure modes: a sentence that merely
* Restoration is limited to avoid three failure modes: a sentence that merely
* *starts* with `For` can't force a mid-title `for` to `For` (distinctive
* requires interior mixed casing), and emphatic all-caps input (`ALL ERROR
* HANDLING`, `FIX the BUG NOW`) is never re-shouted — see {@link isShoutySource}.
* ALL-CAPS restoration also requires the model to have produced a title-cased
* shape (`Cnpg`), never a lowercase one, so we don't re-shout an isolated
* single-word emphasis (`WORK`) that the model correctly de-shouted.
* requires interior mixed casing); emphatic all-caps input (`ALL ERROR
* HANDLING`, `FIX the BUG NOW`) is never re-shouted — see {@link isShoutySource};
* and ordinary all-caps English words (`FIX`, `WORK`, `BUG`) are not treated as
* restorable acronyms unless they carry a stronger acronym signal.
*/
function reconcileTitleCasing(title: string, sourceText: string): string {
const verbatim = new Set<string>();
@@ -249,12 +275,23 @@ function isDistinctiveCasing(token: string): boolean {
return /\p{Ll}/u.test(token) && /\p{L}\p{Lu}/u.test(token);
}
/** Multi-letter ALL-CAPS token in the source (`CNPG`, `API`, `JWT`). Candidate
* for acronym restoration; whether it's actually restored depends on
* {@link isShoutySource} (skips restoration on shouty input) and on the model
* having produced a {@link isTitleCasedArtifact} (`Cnpg`) — never a lowercase
* form, so isolated emphasis (`WORK` → model `work`) is left alone. */
/** Multi-letter ALL-CAPS source token with a stronger acronym signal than a
* plain emphasized word. Consonant-only tokens (`CNPG`, `SQL`, `JWT`) are
* restored, digit-bearing identifiers are restored, and common technical
* acronyms (`API`, `JSON`, `URL`) are allowlisted. Ordinary emphasized words
* (`FIX`, `WORK`, `BUG`) contain vowels and are not restored from source. */
function isAllCapsAcronym(token: string): boolean {
if (!isAllCapsWord(token)) return false;
const upper = token.toUpperCase();
if (COMMON_TITLE_ACRONYMS.has(upper)) return true;
if (/\p{N}/u.test(token)) return true;
return !/[AEIOU]/.test(upper);
}
/** Multi-letter ALL-CAPS word in the source. Used for shout detection, not for
* acronym restoration — shouted English words (`FIX`, `WORK`) still count as
* shouty even though they are not restorable acronyms. */
function isAllCapsWord(token: string): boolean {
const letters = token.match(/\p{L}/gu);
if (!letters || letters.length < 2) return false;
return !/\p{Ll}/u.test(token);
@@ -278,7 +315,7 @@ function isTitleCasedArtifact(token: string): boolean {
function isShoutySource(sourceText: string): boolean {
let run = 0;
for (const [token] of sourceText.matchAll(TITLE_WORD)) {
if (isAllCapsAcronym(token)) {
if (isAllCapsWord(token)) {
run += 1;
if (run >= 2) return true;
} else {
@@ -187,6 +187,18 @@ describe("normalizeGeneratedTitle source-aware casing", () => {
expect(normalizeGeneratedTitle("make it work", "just make it WORK already")).toBe("make it work");
});
it("does not restore a single emphatic ALL-CAPS word from sentence-case title start", () => {
// Normal sentence capitalization produces `Fix`/`Work` at title start.
// Those words have no acronym signal, so they must not be restored as
// `FIX`/`WORK` just because the source had one emphasized word.
expect(normalizeGeneratedTitle("Fix login crash", "FIX login crash")).toBe("Fix login crash");
expect(normalizeGeneratedTitle("Work around bug", "please WORK around the bug")).toBe("Work around bug");
});
it("restores common vowel-bearing technical acronyms via the acronym allowlist", () => {
expect(normalizeGeneratedTitle("Api timeout", "API timeout")).toBe("API timeout");
});
it("still declines to restore ALL-CAPS when the source is shouty", () => {
// `FIX the BUG NOW` has BUG↔NOW consecutive → shouty. Even though the
// model title-cased `Fix` at the start, we must not restore `FIX`.