Skip to content

Commit 6dcb372

Browse files
enkoclaude
andcommitted
feat(dx): Relax kebab-case rule for markdown and impeccable artifacts
Allow fully UPPER_CASE markdown names (README.md, AGENTS.md, ...) but not mixed case, replacing the per-file markdown allowlist with a generic rule. Also ignore the generated .impeccable/ directory, whose critique files use ISO-8601 timestamp prefixes (e.g. 2026-06-14T08-26-30Z__<slug>.md) that can never be kebab-case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d64d338 commit 6dcb372

2 files changed

Lines changed: 24 additions & 14 deletions

File tree

packages/danger/src/rules/kebab-case-files.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import { isExemptFile, isIgnoredPath } from '../utils';
77
// and names like 2fa-setup are valid kebab-case.
88
const KEBAB_CASE_STEM = /^[a-z0-9]+(-[a-z0-9]+)*$/;
99

10+
// Markdown docs may use a fully UPPER_CASE name (e.g. README.md, AGENTS.md,
11+
// CODE_OF_CONDUCT.md) — but not mixed case (Readme.md still fails). Segments
12+
// are uppercase alphanumerics separated by single hyphens or underscores.
13+
const SCREAMING_CASE_STEM = /^[A-Z0-9]+([_-][A-Z0-9]+)*$/;
14+
1015
// Migration files use a "<timestamp>_<name>" pattern — strip the prefix before checking
1116
const MIGRATION_PREFIX = /^\d+_/;
1217

@@ -30,6 +35,10 @@ const kebabCaseFiles: DangerRule = () => {
3035
// Strip migration timestamp prefix (e.g. "1774799536929_fix-bug" -> "fix-bug")
3136
stem = stem.replace(MIGRATION_PREFIX, '');
3237

38+
// Markdown docs may instead be fully UPPER_CASE (README.md, AGENTS.md, ...).
39+
const isMarkdown = basename.toLowerCase().endsWith('.md');
40+
if (isMarkdown && SCREAMING_CASE_STEM.test(stem)) continue;
41+
3342
if (!KEBAB_CASE_STEM.test(stem)) {
3443
const suggestion = toKebabCase(stem);
3544
fail(

packages/danger/src/utils.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import * as path from 'path';
22

3-
export const EXEMPT_BASENAMES = new Set([
4-
'README.md',
5-
'LICENSE',
6-
'Dockerfile',
7-
'Makefile',
8-
'AGENTS.md',
9-
'CHANGELOG.md',
10-
'CONTRIBUTING.md',
11-
'SECURITY.md',
12-
'CODEOWNERS',
13-
'MEMORY.md',
14-
]);
15-
16-
const IGNORED_DIRS = ['node_modules/', 'dist/', '.svelte-kit/', 'vendor/', 'build/'];
3+
// All-uppercase markdown docs (README.md, AGENTS.md, CHANGELOG.md, ...) are
4+
// allowed by the kebab-case rule's SCREAMING_CASE handling, so they don't need
5+
// listing here — only non-markdown exceptions belong below.
6+
export const EXEMPT_BASENAMES = new Set(['LICENSE', 'Dockerfile', 'Makefile', 'CODEOWNERS']);
7+
8+
const IGNORED_DIRS = [
9+
'node_modules/',
10+
'dist/',
11+
'.svelte-kit/',
12+
'vendor/',
13+
'build/',
14+
// Generated impeccable-design artifacts (e.g. timestamped critique files
15+
// like 2026-06-14T08-26-30Z__<slug>.md) are not hand-authored source.
16+
'.impeccable/',
17+
];
1718

1819
export function isExemptFile(filePath: string): boolean {
1920
const basename = path.basename(filePath);

0 commit comments

Comments
 (0)