From 46ca5009b0fe57f0829db552714ba518e1beac77 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sat, 30 May 2026 14:29:15 +0100 Subject: [PATCH] feat(cicd_rules): Layer-1 path_allow_prefixes for RS/npm/JS bans (batch) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the TS Layer-1 batch (#375 + #378) for the three estate campaigns that all need parallel path_allow_prefixes extensions on their respective banned-language rules. Single PR vs three because the changes are non-conflicting and the carve-out classes share structural overlap (vscode class, bootstrap shims, upstream forks, archived, vendored deps). ## Rule changes `:rescript_detected` + `:rescript_interface_detected` — gain 8-class path_allow_prefixes: bsconfig.json tooling, upstream forks (rescript/, servers/, repos-monorepo/, linguist/), archived, vendored deps (/deps/, /node_modules/), vscode, compiled output (/lib/js/, /lib/es6/, /lib/bs/), bootstrap shims (affinescript-deno-test/, affinescript-cli/), Telegraf carve-out. (Layer-1 for hyperpolymath/standards#260.) `:nodejs_detected` (matches package-lock.json) — gains 6-class path_allow_prefixes: vscode host-required, bootstrap shims, upstream forks, archived, vendored deps, example/test fixtures. (Layer-1 for hyperpolymath/standards#261.) `:javascript_detected` (NEW, glob *.js) + `:javascript_jsx_detected` (NEW, glob *.jsx) — created with 8-class path_allow_prefixes: host-required (mcp-bridge/, /plugins/), tooling configs (.config.{js,cjs, mjs}), bootstrap shims, upstream forks, archived, vendored deps, compiled output (/out/, /lib/js/, /.deno/), vscode editor-host. Ship mode: HARD-BLOCK + extensive carve-outs (umbrella#254 STEP 1 design question decided in favour of HARD-BLOCK; WARNING mode preserved as inline pragma suppression path). (Layer-1 for hyperpolymath/standards#263.) ## Tests New `test/rules/cicd_rules_rescript_npm_js_test.exs` with 15 representative cases across all four rules (flag + per-carve-out exemption). Mirrors the shape of `cicd_rules_typescript_test.exs`. Verified locally via standalone `elixirc` + sanity-check script (15/15 pass). (Local `mix test` blocked by Elixir 1.14 vs Phoenix dep mismatch unrelated to this PR; CI will run on a compatible toolchain.) ## Seam: batched per owner directive Memory: `[[feedback_fanout_and_seam_analyst_meander]]` — three sibling campaigns identified as Layer-1 batch candidate during the umbrella filings session 2026-05-30. Three STEP 1 sub-issues (#260/#261/#263) all cross-reference each other and this batch. Refs hyperpolymath/standards#260 Refs hyperpolymath/standards#261 Refs hyperpolymath/standards#263 Refs hyperpolymath/standards#252 Refs hyperpolymath/standards#253 Refs hyperpolymath/standards#254 Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/rules/cicd_rules.ex | 801 ++++++++++++++---- .../rules/cicd_rules_rescript_npm_js_test.exs | 310 +++++++ 2 files changed, 948 insertions(+), 163 deletions(-) create mode 100644 test/rules/cicd_rules_rescript_npm_js_test.exs diff --git a/lib/rules/cicd_rules.ex b/lib/rules/cicd_rules.ex index 860bd7a2..9c9805b3 100644 --- a/lib/rules/cicd_rules.ex +++ b/lib/rules/cicd_rules.ex @@ -108,8 +108,11 @@ defmodule Hypatia.Rules.CicdRules do # `extensions/vscode/`, or `clients/vscode/`). Blocked on the # AffineScript VSCode-extension API binding (top-50 roadmap); # carve-out unblocks the policy gate until bindings ship. - %{id: :typescript_detected, glob: "*.ts", - reason: "TypeScript banned in NEW code -- use AffineScript (org policy 2026-04-30; existing TS grandfathered while in-flight migration proceeds, see project_estate_ts_to_affinescript_2026_05_28)", + %{ + id: :typescript_detected, + glob: "*.ts", + reason: + "TypeScript banned in NEW code -- use AffineScript (org policy 2026-04-30; existing TS grandfathered while in-flight migration proceeds, see project_estate_ts_to_affinescript_2026_05_28)", # check_pattern uses String.contains?/2 so these entries match as # substrings anywhere in the file path — both directory prefixes # (e.g., "/bindings/deno/") and suffix patterns (e.g., ".d.ts", @@ -153,10 +156,223 @@ defmodule Hypatia.Rules.CicdRules do # `*/clients/vscode/`. The bare `/vscode/` substring suffices since # `String.contains?/2` matches any path containing it. "/vscode/" - ]}, - %{id: :rescript_detected, glob: "*.res", reason: "ReScript banned -- use AffineScript (org policy 2026-05-25; see #57 migration assistant)"}, - %{id: :rescript_interface_detected, glob: "*.resi", reason: "ReScript banned -- use AffineScript (org policy 2026-05-25; see #57 migration assistant)"}, - %{id: :nodejs_detected, glob: "package-lock.json", reason: "Node.js banned -- use Deno"}, + ] + }, + # ReScript ban (org policy 2026-05-25). In-flight estate migration + # tracked under hyperpolymath/standards#252 + STEPS #260-#280. + # Path-prefix allowlist covers eight classes of legitimate `.res` / + # `.resi` presence (mirrors TS 9-class canon adapted for ReScript): + # + # (1) Tooling configs — `bsconfig.json`, `*.config.res` are build + # orchestration, not application code. + # (2) Upstream forks not estate-authored — `rescript/` (compiler), + # `servers/`, `repos-monorepo/`, `linguist/` (samples are ML + # training data). + # (3) Archived repos — `hyperpolymath-archive/**` cannot accept PRs. + # (4) Vendored package-manager deps — `**/deps/**`, `**/node_modules/**`. + # (5) Editor-host extensions — `**/vscode/**` blocked on the + # AffineScript VSCode-extension API binding (top-50 roadmap). + # (6) Compiled output — `**/lib/js/**`, `**/lib/es6/**`, `**/lib/bs/**` + # are bsc compilation targets, not source. + # (7) Bootstrap shims — `affinescript-deno-test/`, `affinescript-cli/` + # parallel to TS class 5. + # (8) Telegraf carve-out — `avow-protocol/telegram-bot/avow-telegram-bot/` + # parallel to TS class 3 if a `.res` file appears for the bot. + %{ + id: :rescript_detected, + glob: "*.res", + reason: + "ReScript banned -- use AffineScript (org policy 2026-05-25; see #57 migration assistant; in-flight migration tracked under standards#252)", + path_allow_prefixes: [ + # (1) Tooling configs + "bsconfig.json", + ".config.res", + # (2) Upstream forks + "rescript/", + "servers/", + "repos-monorepo/", + "linguist/", + # (3) Archived + "hyperpolymath-archive/", + # (4) Vendored deps + "/deps/", + "/node_modules/", + # (5) Editor-host extensions + "/vscode/", + # (6) Compiled output + "/lib/js/", + "/lib/es6/", + "/lib/bs/", + # (7) Bootstrap shims + "affinescript-deno-test/", + "affinescript-cli/", + # (8) Telegraf carve-out + "avow-protocol/telegram-bot/avow-telegram-bot/" + ] + }, + %{ + id: :rescript_interface_detected, + glob: "*.resi", + reason: + "ReScript banned -- use AffineScript (org policy 2026-05-25; see #57 migration assistant; in-flight migration tracked under standards#252)", + path_allow_prefixes: [ + # Same eight classes as :rescript_detected + "bsconfig.json", + ".config.res", + "rescript/", + "servers/", + "repos-monorepo/", + "linguist/", + "hyperpolymath-archive/", + "/deps/", + "/node_modules/", + "/vscode/", + "/lib/js/", + "/lib/es6/", + "/lib/bs/", + "affinescript-deno-test/", + "affinescript-cli/", + "avow-protocol/telegram-bot/avow-telegram-bot/" + ] + }, + # Node.js / npm ban (org policy 2026-05-25). Matches `package-lock.json` + # as the canonical npm-toolchain signal. In-flight estate migration to + # Deno tracked under hyperpolymath/standards#253 + STEPS #261-#275. + # Path-prefix allowlist covers six classes of legitimate lockfile + # presence: + # + # (1) VSCode extension host-required — `**/vscode/**`; VSCode + # extension toolchain runs under Node, so its lockfile is + # contractually required by the host (parallel to TS class 9). + # (2) Bootstrap shims — `affinescript-deno-test/`, `affinescript-cli/` + # parallel to TS class 5. + # (3) Upstream forks not estate-authored — `rescript/`, `servers/`, + # `repos-monorepo/`, `linguist/`. + # (4) Archived repos — `hyperpolymath-archive/**`. + # (5) Vendored package-manager deps — `**/deps/**`, `**/node_modules/**`. + # (6) Example/test fixtures — `**/example/**`, `**/examples/**`, + # `**/test-fixtures/**`, `**/fixtures/**` may legitimately ship + # a lockfile demonstrating an npm consumer. + %{ + id: :nodejs_detected, + glob: "package-lock.json", + reason: + "Node.js banned -- use Deno (org policy 2026-05-25; in-flight migration tracked under standards#253)", + path_allow_prefixes: [ + # (1) VSCode extension host-required + "/vscode/", + # (2) Bootstrap shims + "affinescript-deno-test/", + "affinescript-cli/", + # (3) Upstream forks + "rescript/", + "servers/", + "repos-monorepo/", + "linguist/", + # (4) Archived + "hyperpolymath-archive/", + # (5) Vendored deps + "/deps/", + "/node_modules/", + # (6) Example / test fixtures + "/example/", + "/examples/", + "/test-fixtures/", + "/fixtures/" + ] + }, + # Unnecessarily-JavaScript ban (org policy 2026-05-25). Per policy, + # JavaScript is *allowed* where AffineScript cannot reach, so this rule + # targets the gap between current AS bindings and current JS usage — + # i.e., JS that COULD be AS today but isn't. In-flight estate migration + # tracked under hyperpolymath/standards#254 + STEPS #263-#277. + # + # SHIP MODE: HARD-BLOCK at rule level; per-PR exemption via inline + # `// hypatia: allow cicd_rules/javascript_detected -- ` + # pragmas. The WARNING-first option (umbrella#254 STEP 1 design + # question) was decided in favour of HARD-BLOCK + extensive carve-outs; + # see #263 for rationale. + # + # Path-prefix allowlist covers eight classes of legitimate `.js` / `.jsx`: + # + # (1) Host-required by ecosystem — MCP servers, plugin entry points + # where JS is the host contract. Exemplars: `mcp-bridge/`, + # `**/plugins/**`. + # (2) Tooling configs — `*.config.js`, `*.config.cjs`, `*.config.mjs` + # are build orchestration. + # (3) Bootstrap shims — `affinescript-deno-test/`, `affinescript-cli/` + # parallel to TS class 5. + # (4) Upstream forks — `rescript/`, `servers/`, `repos-monorepo/`, + # `linguist/` (samples are ML training data). + # (5) Archived repos — `hyperpolymath-archive/**`. + # (6) Vendored package-manager deps — `**/deps/**`, `**/node_modules/**`. + # (7) Compiled output — `**/out/**`, `**/lib/js/**`, `**/.deno/**` + # are AS / RS compile targets. + # (8) Host extension entry — `**/vscode/**`, `**/extensions/vscode/**` + # parallel to TS class 9 (blocked on AS VSCode-extension API). + %{ + id: :javascript_detected, + glob: "*.js", + reason: + "Unnecessarily-JavaScript banned in NEW code -- use AffineScript where bindings exist; existing JS grandfathered while in-flight migration proceeds (org policy 2026-05-25; tracked under standards#254). To suppress for an MCP/plugin-host case, use inline pragma.", + path_allow_prefixes: [ + # (1) Host-required by ecosystem + "mcp-bridge/", + "/plugins/", + # (2) Tooling configs + ".config.js", + ".config.cjs", + ".config.mjs", + # (3) Bootstrap shims + "affinescript-deno-test/", + "affinescript-cli/", + # (4) Upstream forks + "rescript/", + "servers/", + "repos-monorepo/", + "linguist/", + # (5) Archived + "hyperpolymath-archive/", + # (6) Vendored deps + "/deps/", + "/node_modules/", + # (7) Compiled output + "/out/", + "/lib/js/", + "/.deno/", + # (8) Host extension entry + "/vscode/", + "/extensions/vscode/" + ] + }, + %{ + id: :javascript_jsx_detected, + glob: "*.jsx", + reason: + "Unnecessarily-JavaScript (JSX) banned in NEW code -- use AffineScript where bindings exist (org policy 2026-05-25; tracked under standards#254).", + path_allow_prefixes: [ + # Same eight classes as :javascript_detected + "mcp-bridge/", + "/plugins/", + ".config.js", + ".config.cjs", + ".config.mjs", + "affinescript-deno-test/", + "affinescript-cli/", + "rescript/", + "servers/", + "repos-monorepo/", + "linguist/", + "hyperpolymath-archive/", + "/deps/", + "/node_modules/", + "/out/", + "/lib/js/", + "/.deno/", + "/vscode/", + "/extensions/vscode/" + ] + }, # Added 2026-05-28 (audit gap §5.8): npx / npm-run in CI run-blocks # bypasses the lockfile-based npm detection. `npx ` downloads # the package fresh each run; `npm run