Skip to content

core-quality-duplicate-string

Pack: core-quality Default severity: MINOR Languages: TypeScript, JavaScript Lifecycle: experimental Confidence: 0.7

What it catches

The same string literal repeated ≥ 3 times in the same file. Pivot-debris signal: the AI generated similar code in three places, the developer restructured one or two, and now the remaining copies drift independently.

Why this matters for vibe-coded apps

The next AI fix will see N copies of the same string and assume they represent the same concept, even after meaning has diverged. Extracting a constant makes the intent explicit and gives future changes one place to land.

Example — flagged

function emit(kind: string): string {
  if (kind === 'a') return 'pending-rls-review';   // ← 1st
  if (kind === 'b') return 'pending-rls-review';   // ← 2nd
  if (kind === 'c') return 'pending-rls-review';   // ← 3rd → flag
  return 'idle';
}

Example — not flagged

  • String appears only 1 or 2 times in the file (the rule's threshold is >= 3).
  • String is shorter than 4 characters after trim ('x', '/', '').
  • String is whitespace / punctuation only.
  • String is on the common-noise list: 'default', 'true', 'false', 'null', 'undefined', HTTP verbs ('GET', 'POST', …), 'utf-8'.
  • String is an import pathimport 'x', require('x'), import('x'), export from 'x'.
  • String is a JSX class-like attribute value — heuristic: contains a space AND parent is JsxAttribute (i.e. CSS class list).

Suggested fix

Hoist the literal to a single named constant:

const RLS_REVIEW_TAG = 'pending-rls-review';

function emit(kind: string): string {
  if (kind === 'a') return RLS_REVIEW_TAG;
  if (kind === 'b') return RLS_REVIEW_TAG;
  if (kind === 'c') return RLS_REVIEW_TAG;
  return 'idle';
}

If the three sites genuinely represent three DIFFERENT concepts that happen to share the same value today, give each its own named constant — that also resolves the rule.

Suppressing

// Reason: i18n keys kept inline by project convention; central catalog is generated, not authored.
// codemore-ignore-next-line: core-quality-duplicate-string
console.log('greeting.welcome');

The directive must be on the line immediately before the target. If you put a comment between them, the directive suppresses the comment instead.

Implementation

AST walk over every StringLiteral / NoSubstitutionTemplateLiteral. Tally by exact text. Emit a single finding pointing at the FIRST occurrence per duplicate group. Skip rules listed above.

Cross-file duplication is intentionally out of scope for v1 — see the rule source for the rationale.

Source: `shared/packs/core-quality/core-quality-duplicate-string.ts` Fixtures: `corpus/rules/core-quality-duplicate-string/`

Next →
Back to the catalog
See the other 57 rules — grouped by pack, with lifecycle gates.