Skip to content

Commit f12a509

Browse files
committed
ci(antipattern): TS check reads .claude/CLAUDE.md exemption table
1 parent c59cb98 commit f12a509

1 file changed

Lines changed: 79 additions & 32 deletions

File tree

.github/workflows/rsr-antipattern.yml

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,38 +33,85 @@ jobs:
3333

3434
- name: Check for TypeScript
3535
run: |
36-
# Allowlist (TS legitimate as a bridge/adapter to a non-ReScript ecosystem):
37-
# bindings/ - language bindings (Deno/TS/AssemblyScript FFI)
38-
# *.d.ts - TypeScript type declarations for ReScript FFI
39-
# tests/, test/ - Deno test runners
40-
# scripts/ - Deno build scripts
41-
# mcp-adapter/ - MCP server adapters (MCP is Deno/TS-typed by spec)
42-
# *vscode* - VSCode extensions (TS is the ecosystem default)
43-
# cli/ - CLI entry points (Deno scripts)
44-
# mod.ts - canonical Deno module entrypoint
45-
# *lsp-server.ts, *lsp.ts - Language Server Protocol implementations
46-
# deno-*/ - subprojects explicitly named for Deno
47-
TS_FILES=$(find . \( -name "*.ts" -o -name "*.tsx" \) \
48-
| grep -v node_modules \
49-
| grep -v '/bindings/' \
50-
| grep -v '\.d\.ts$' \
51-
| grep -v '/tests/' \
52-
| grep -v '/test/' \
53-
| grep -v '/scripts/' \
54-
| grep -v '/mcp-adapter/' \
55-
| grep -Ev '/[^/]*vscode[^/]*/' \
56-
| grep -v '/cli/' \
57-
| grep -v '/mod\.ts$' \
58-
| grep -Ev 'lsp[-_]?server\.ts$' \
59-
| grep -Ev '[/-]lsp\.ts$' \
60-
| grep -Ev '/deno-[^/]+/' \
61-
|| true)
62-
if [ -n "$TS_FILES" ]; then
63-
echo "❌ TypeScript files detected - use ReScript instead"
64-
echo "$TS_FILES"
65-
exit 1
66-
fi
67-
echo "✅ No TypeScript files outside allowlisted bridge/adapter paths"
36+
python3 << 'PYEOF'
37+
import re, sys, fnmatch, pathlib
38+
39+
# Universal builtin allowlist — bridges that need no per-repo declaration.
40+
# Files matching any of these patterns are always allowed.
41+
BUILTIN_GLOBS = [
42+
'*.d.ts',
43+
'**/bindings/**',
44+
'**/tests/**', '**/test/**',
45+
'**/scripts/**',
46+
'**/mcp-adapter/**',
47+
'**/*vscode*/**',
48+
'**/cli/**',
49+
'**/mod.ts',
50+
'**/lsp-server.ts', '**/lsp_server.ts', '**/lsp.ts', '**/*-lsp.ts',
51+
'**/deno-*/**',
52+
'**/node_modules/**',
53+
'**/vendor/**',
54+
'**/examples/**',
55+
'**/ffi/**',
56+
]
57+
58+
# Per-repo exemptions parsed from .claude/CLAUDE.md "TypeScript Exemptions" table.
59+
# Single source of truth — adding a row here unblocks CI for that path.
60+
# Format expected:
61+
# ### TypeScript Exemptions ...
62+
# | Path | Files | Rationale | Unblock condition |
63+
# |---|---|---|---|
64+
# | `path/to/file.ts` | 1 | ... | ... |
65+
# | `dir/*.ts` | 6 | ... | ... |
66+
exemptions = []
67+
claude_md = pathlib.Path('.claude/CLAUDE.md')
68+
if claude_md.exists():
69+
in_table = False
70+
for line in claude_md.read_text(encoding='utf-8').splitlines():
71+
if re.search(r'TypeScript [Ee]xemptions', line):
72+
in_table = True
73+
continue
74+
if in_table and line.startswith(('### ', '## ', '# ')):
75+
break
76+
if in_table and line.startswith('|'):
77+
m = re.match(r'\|\s*`([^`]+)`', line)
78+
if m:
79+
exemptions.append(m.group(1))
80+
81+
# Find all .ts and .tsx files
82+
found = []
83+
for ext in ('ts', 'tsx'):
84+
found.extend(str(p) for p in pathlib.Path('.').rglob(f'*.{ext}'))
85+
86+
def allowed(path):
87+
p = path.lstrip('./')
88+
for g in BUILTIN_GLOBS + exemptions:
89+
if fnmatch.fnmatchcase(p, g):
90+
return True
91+
# also treat glob ending with / as a directory prefix
92+
base = g.rstrip('/').rstrip('*').rstrip('/')
93+
if base and (p == base or p.startswith(base + '/')):
94+
return True
95+
return False
96+
97+
bad = sorted(f for f in found if not allowed(f))
98+
if bad:
99+
print("❌ TypeScript files detected outside the allowlist.\n")
100+
for f in bad:
101+
print(f" {f}")
102+
print()
103+
print("To resolve, either:")
104+
print(" (a) migrate the file to AffineScript")
105+
print(" (see Human_Programming_Guide.adoc migration chapter), OR")
106+
print(" (b) move it to an allowlisted bridge path")
107+
print(" (bindings/, tests/, scripts/, mcp-adapter/, *vscode*/, cli/, deno-*/, etc.), OR")
108+
print(" (c) add an entry to the 'TypeScript Exemptions' table in .claude/CLAUDE.md")
109+
print(" with rationale + unblock condition.")
110+
if exemptions:
111+
print(f"\n(Currently {len(exemptions)} exemption(s) parsed from .claude/CLAUDE.md.)")
112+
sys.exit(1)
113+
print(f"✅ No TypeScript files outside allowlist ({len(exemptions)} per-repo exemption(s) parsed).")
114+
PYEOF
68115
69116
- name: Check for Go
70117
run: |

0 commit comments

Comments
 (0)