|
1 | | -# SPDX-License-Identifier: PMPL-1.0-or-later |
2 | | -# SPDX-FileCopyrightText: 2024 Hyperpolymath |
3 | | -# |
4 | | -# Language Policy Enforcement Workflow |
5 | | -# Rejects PRs that violate the Hyperpolymath Language Standard |
6 | | - |
7 | | -permissions: |
8 | | - contents: read |
9 | | - |
10 | | -name: Language Policy |
| 1 | +# SPDX-License-Identifier: PMPL-1.0 |
| 2 | +name: Language Policy Enforcement |
11 | 3 |
|
12 | 4 | on: |
13 | | - pull_request: |
14 | | - branches: [main, develop] |
15 | 5 | push: |
16 | | - branches: [main, develop] |
| 6 | + branches: [main, master] |
| 7 | + pull_request: |
| 8 | + branches: [main, master] |
| 9 | + |
| 10 | +# Estate guardrail: cancel superseded runs so re-pushes / rebased PR |
| 11 | +# updates do not pile up queued runs against the shared account-wide |
| 12 | +# Actions concurrency pool. Applied only to read-only check workflows |
| 13 | +# (no publish/mutation), so cancelling a superseded run is always safe. |
| 14 | +concurrency: |
| 15 | + group: ${{ github.workflow }}-${{ github.ref }} |
| 16 | + cancel-in-progress: true |
| 17 | + |
| 18 | +permissions: |
| 19 | + contents: read |
17 | 20 |
|
18 | 21 | jobs: |
19 | | - enforce-policy: |
20 | | - name: Enforce Language Policy |
| 22 | + check-banned-languages: |
| 23 | + name: Check for Banned Languages |
21 | 24 | runs-on: ubuntu-latest |
22 | 25 | steps: |
23 | | - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 |
| 26 | + - name: Checkout |
| 27 | + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 |
| 28 | + |
| 29 | + # TypeScript check delegated to rsr-antipattern.yml (which honours the |
| 30 | + # universal allowlist and the .claude/CLAUDE.md exemptions table). The |
| 31 | + # blunt `find -name "*.ts"` form previously here false-positived on |
| 32 | + # legitimate bridge files (e.g. tests/*.vitest.config.ts under the |
| 33 | + # *vscode*/tests/ allowlist). |
24 | 34 |
|
25 | | - - name: Check for banned TypeScript files |
| 35 | + - name: Check for ReScript files |
26 | 36 | run: | |
27 | | - if find . -name "*.ts" -not -path "./node_modules/*" | grep -q .; then |
28 | | - echo "❌ ERROR: TypeScript files detected (BANNED)" |
29 | | - echo "TypeScript is not allowed in this project. Use AffineScript instead." |
30 | | - find . -name "*.ts" -not -path "./node_modules/*" |
| 37 | + # Estate policy: RS/TS/JS -> AffineScript -> typed-wasm. |
| 38 | + # ReScript (.res) is no longer the TS replacement. |
| 39 | + if find . -name "*.res" | grep -v node_modules | head -1 | grep -q .; then |
| 40 | + echo "::error::ReScript files found. Use AffineScript instead." |
| 41 | + find . -name "*.res" | grep -v node_modules |
31 | 42 | exit 1 |
32 | 43 | fi |
33 | | - echo "✅ No TypeScript files found" |
| 44 | + echo "✓ No ReScript files found" |
34 | 45 |
|
35 | | - - name: Check for banned TSX files |
| 46 | + - name: Check for Go files |
36 | 47 | run: | |
37 | | - if find . -name "*.tsx" -not -path "./node_modules/*" | grep -q .; then |
38 | | - echo "❌ ERROR: TSX files detected (BANNED)" |
39 | | - echo "TSX is not allowed in this project. Use AffineScript instead." |
40 | | - find . -name "*.tsx" -not -path "./node_modules/*" |
| 48 | + if find . -name "*.go" | head -1 | grep -q .; then |
| 49 | + echo "::error::Go files found. Use Rust instead." |
| 50 | + find . -name "*.go" |
41 | 51 | exit 1 |
42 | 52 | fi |
43 | | - echo "✅ No TSX files found" |
| 53 | + echo "✓ No Go files found" |
44 | 54 |
|
45 | | - - name: Check for package.json (npm) |
| 55 | + - name: Check for Python files (except Ansible) |
46 | 56 | run: | |
47 | | - if [ -f package.json ]; then |
48 | | - echo "❌ ERROR: package.json detected (BANNED)" |
49 | | - echo "npm is not allowed. Use deno.json for dependencies." |
| 57 | + # Allow Python only in ansible/ directories or for Ansible-specific files |
| 58 | + PYTHON_FILES=$(find . -name "*.py" | grep -v __pycache__ | grep -v ".venv" | grep -v "ansible" | grep -v "molecule" || true) |
| 59 | + if [ -n "$PYTHON_FILES" ]; then |
| 60 | + echo "::error::Python files found outside Ansible context. Rewrite in Rust/AffineScript." |
| 61 | + echo "$PYTHON_FILES" |
50 | 62 | exit 1 |
51 | 63 | fi |
52 | | - echo "✅ No package.json found" |
| 64 | + echo "✓ No unauthorized Python files found" |
53 | 65 |
|
54 | | - - name: Check for package-lock.json |
| 66 | + - name: Check for Makefiles |
55 | 67 | run: | |
56 | | - if [ -f package-lock.json ]; then |
57 | | - echo "❌ ERROR: package-lock.json detected (BANNED)" |
| 68 | + MAKEFILES=$(find . -name "Makefile" -o -name "Makefile.*" -o -name "*.mk" | grep -v ".github" || true) |
| 69 | + if [ -n "$MAKEFILES" ]; then |
| 70 | + echo "::error::Makefiles found. Use Mustfile/justfile instead." |
| 71 | + echo "$MAKEFILES" |
58 | 72 | exit 1 |
59 | 73 | fi |
60 | | - echo "✅ No package-lock.json found" |
| 74 | + echo "✓ No Makefiles found" |
61 | 75 |
|
62 | | - - name: Check for node_modules |
| 76 | + - name: Check for package.json (npm/node) |
63 | 77 | run: | |
64 | | - if [ -d node_modules ]; then |
65 | | - echo "❌ ERROR: node_modules detected (BANNED)" |
66 | | - echo "node_modules is not allowed. Use Deno imports." |
67 | | - exit 1 |
| 78 | + if [ -f "package.json" ]; then |
| 79 | + # Allow if it only contains devDependencies for tooling |
| 80 | + if grep -q '"dependencies"' package.json; then |
| 81 | + echo "::error::package.json with runtime dependencies found. Use deno.json instead." |
| 82 | + exit 1 |
| 83 | + fi |
68 | 84 | fi |
69 | | - echo "✅ No node_modules found" |
| 85 | + echo "✓ No npm runtime dependencies found" |
70 | 86 |
|
71 | | - - name: Check for yarn.lock |
| 87 | + - name: Check for Java/Kotlin files |
72 | 88 | run: | |
73 | | - if [ -f yarn.lock ]; then |
74 | | - echo "❌ ERROR: yarn.lock detected (BANNED)" |
| 89 | + if find . -name "*.java" -o -name "*.kt" -o -name "*.kts" | head -1 | grep -q .; then |
| 90 | + echo "::error::Java/Kotlin files found. Use Rust/Tauri/Dioxus instead." |
| 91 | + find . -name "*.java" -o -name "*.kt" -o -name "*.kts" |
75 | 92 | exit 1 |
76 | 93 | fi |
77 | | - echo "✅ No yarn.lock found" |
| 94 | + echo "✓ No Java/Kotlin files found" |
78 | 95 |
|
79 | | - - name: Check for pnpm-lock.yaml |
| 96 | + - name: Check for Swift files |
80 | 97 | run: | |
81 | | - if [ -f pnpm-lock.yaml ]; then |
82 | | - echo "❌ ERROR: pnpm-lock.yaml detected (BANNED)" |
| 98 | + if find . -name "*.swift" | head -1 | grep -q .; then |
| 99 | + echo "::error::Swift files found. Use Tauri/Dioxus instead." |
| 100 | + find . -name "*.swift" |
83 | 101 | exit 1 |
84 | 102 | fi |
85 | | - echo "✅ No pnpm-lock.yaml found" |
| 103 | + echo "✓ No Swift files found" |
86 | 104 |
|
87 | | - - name: Check for bun.lockb |
| 105 | + - name: Check for V-lang code |
88 | 106 | run: | |
89 | | - if [ -f bun.lockb ]; then |
90 | | - echo "❌ ERROR: bun.lockb detected (BANNED)" |
| 107 | + # V-lang is banned as of 2026-04-10. Migration target: Zig. |
| 108 | + # We detect by v.mod (V-lang's module manifest) rather than *.v |
| 109 | + # extension because .v collides with Verilog hardware sources. |
| 110 | + V_MOD_FILES=$(find . -name "v.mod" -not -path "*/node_modules/*" -not -path "*/.git/*" || true) |
| 111 | + if [ -n "$V_MOD_FILES" ]; then |
| 112 | + echo "::error::V-lang code found (detected via v.mod). V-lang is banned since 2026-04-10. Migrate to Zig." |
| 113 | + echo "$V_MOD_FILES" |
91 | 114 | exit 1 |
92 | 115 | fi |
93 | | - echo "✅ No bun.lockb found" |
94 | | -
|
95 | | - - name: Check for Makefile |
96 | | - run: | |
97 | | - if [ -f Makefile ] || [ -f makefile ] || [ -f GNUmakefile ]; then |
98 | | - echo "❌ ERROR: Makefile detected (BANNED)" |
99 | | - echo "Makefile is not allowed. Use justfile instead." |
| 116 | + # Also check for vpkg.json (alternative V package manifest) |
| 117 | + VPKG_FILES=$(find . -name "vpkg.json" -not -path "*/node_modules/*" -not -path "*/.git/*" || true) |
| 118 | + if [ -n "$VPKG_FILES" ]; then |
| 119 | + echo "::error::V-lang code found (detected via vpkg.json). V-lang is banned since 2026-04-10. Migrate to Zig." |
| 120 | + echo "$VPKG_FILES" |
100 | 121 | exit 1 |
101 | 122 | fi |
102 | | - echo "✅ No Makefile found" |
| 123 | + echo "✓ No V-lang code found" |
103 | 124 |
|
104 | | - - name: Check for Go files |
| 125 | + - name: Check for Flutter/Dart files |
105 | 126 | run: | |
106 | | - if find . -name "*.go" | grep -q .; then |
107 | | - echo "❌ ERROR: Go files detected (BANNED)" |
108 | | - echo "Go is not allowed. Use Rust instead." |
109 | | - find . -name "*.go" |
| 127 | + if find . -name "*.dart" -o -name "pubspec.yaml" | head -1 | grep -q .; then |
| 128 | + echo "::error::Flutter/Dart code found. Use Tauri/Dioxus instead (Google lock-in policy)." |
| 129 | + find . -name "*.dart" -o -name "pubspec.yaml" |
110 | 130 | exit 1 |
111 | 131 | fi |
112 | | - echo "✅ No Go files found" |
| 132 | + echo "✓ No Flutter/Dart code found" |
113 | 133 |
|
114 | | - - name: Verify deno.json exists |
| 134 | + - name: Check for ATS2 files |
115 | 135 | run: | |
116 | | - if [ ! -f deno.json ]; then |
117 | | - echo "❌ ERROR: deno.json not found" |
118 | | - echo "This project requires Deno configuration." |
| 136 | + # ATS2 is rejected in favour of Idris2 (formal verification) and |
| 137 | + # Rust/SPARK (safety-critical operational code). See LANGUAGE-POLICY.adoc §Amendments v1.1.0. |
| 138 | + if find . -name "*.dats" -o -name "*.sats" -o -name "*.hats" | head -1 | grep -q .; then |
| 139 | + echo "::error::ATS2 files found. Use Idris2 (formal verification) or Rust/SPARK (safety-critical code) instead." |
| 140 | + find . -name "*.dats" -o -name "*.sats" -o -name "*.hats" |
119 | 141 | exit 1 |
120 | 142 | fi |
121 | | - echo "✅ deno.json found" |
| 143 | + echo "✓ No ATS2 files found" |
122 | 144 |
|
123 | | - - name: Verify bsconfig.json exists |
| 145 | + check-required-files: |
| 146 | + name: Check Required Files |
| 147 | + runs-on: ubuntu-latest |
| 148 | + steps: |
| 149 | + - name: Checkout |
| 150 | + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 |
| 151 | + |
| 152 | + - name: Check for .machine_readable directory |
124 | 153 | run: | |
125 | | - if [ ! -f bsconfig.json ]; then |
126 | | - echo "❌ ERROR: bsconfig.json not found" |
127 | | - echo "This project requires ReScript configuration." |
128 | | - exit 1 |
| 154 | + if [ ! -d ".machine_readable" ]; then |
| 155 | + echo "::warning::.machine_readable/ directory not found" |
| 156 | + else |
| 157 | + echo "✓ .machine_readable/ directory exists" |
| 158 | + # Per estate policy: .a2ml is canonical; .scm is reserved for Guix. |
| 159 | + for a2ml in STATE META ECOSYSTEM AGENTIC NEUROSYM PLAYBOOK; do |
| 160 | + if [ ! -f ".machine_readable/6a2/${a2ml}.a2ml" ] && [ ! -f ".machine_readable/${a2ml}.a2ml" ]; then |
| 161 | + echo "::warning::Missing .machine_readable/6a2/${a2ml}.a2ml (or top-level fallback)" |
| 162 | + else |
| 163 | + echo "✓ ${a2ml}.a2ml present" |
| 164 | + fi |
| 165 | + done |
129 | 166 | fi |
130 | | - echo "✅ bsconfig.json found" |
131 | 167 |
|
132 | | - - name: Verify justfile exists |
| 168 | + - name: Check for Mustfile/justfile |
133 | 169 | run: | |
134 | | - if [ ! -f justfile ]; then |
135 | | - echo "❌ ERROR: justfile not found" |
136 | | - echo "This project requires a justfile for task running." |
137 | | - exit 1 |
| 170 | + if [ ! -f "Mustfile" ] && [ ! -f "justfile" ]; then |
| 171 | + echo "::warning::Neither Mustfile nor justfile found" |
| 172 | + else |
| 173 | + echo "✓ Build system files present" |
138 | 174 | fi |
139 | | - echo "✅ justfile found" |
140 | 175 |
|
141 | | - - name: Language policy check passed |
| 176 | + - name: Check SPDX headers |
142 | 177 | run: | |
143 | | - echo "✅ All language policy checks passed!" |
144 | | - echo "" |
145 | | - echo "Allowed: ReScript, Deno, Rust, Bash, Nickel" |
146 | | - echo "Banned: TypeScript, Node.js, npm, Go, Makefile" |
| 178 | + MISSING_SPDX=0 |
| 179 | + for ext in rs affine js jsx mjs ts tsx py go java kt swift sh bash; do |
| 180 | + while IFS= read -r file; do |
| 181 | + if [ -n "$file" ] && ! head -5 "$file" | grep -q "SPDX-License-Identifier"; then |
| 182 | + echo "::warning::Missing SPDX header: $file" |
| 183 | + MISSING_SPDX=$((MISSING_SPDX + 1)) |
| 184 | + fi |
| 185 | + done < <(find . -name "*.$ext" -type f 2>/dev/null | grep -v node_modules | grep -v .git | head -50) |
| 186 | + done |
| 187 | + if [ $MISSING_SPDX -gt 0 ]; then |
| 188 | + echo "::warning::$MISSING_SPDX files missing SPDX headers" |
| 189 | + fi |
0 commit comments