diff --git a/.github/workflows/no-js-scan.yml b/.github/workflows/no-js-scan.yml new file mode 100644 index 00000000..93d61f00 --- /dev/null +++ b/.github/workflows/no-js-scan.yml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: MPL-2.0 +name: No-JS Scan (warn-first) + +# Estate policy: no hand-authored JavaScript/TypeScript source +# (see standards docs/NO-JAVASCRIPT-SOURCE-POLICY.adoc). This workflow is +# WARN-FIRST: it reports the authored-JS surface for migration but never fails +# the build. The authoritative hard-block for NEW JS in non-carve-out paths is +# hypatia (cicd_rules/javascript_detected); this is an additive companion. + +on: + push: + paths: + - '**/*.js' + - '**/*.jsx' + - '**/*.mjs' + - '**/*.cjs' + - '**/*.ts' + - '**/*.tsx' + pull_request: + paths: + - '**/*.js' + - '**/*.jsx' + - '**/*.mjs' + - '**/*.cjs' + - '**/*.ts' + - '**/*.tsx' + +# Estate guardrail: cancel superseded runs (read-only check, no mutation). +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + scan-authored-js: + name: Scan for hand-authored JavaScript/TypeScript + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 + + - name: Report authored JS/TS (warn-first, non-blocking) + shell: bash + run: | + # Exclude only things that are NOT hand-authored source: vendored + # (node_modules, deps, vendor, .git), generated/compiled (*.res.js, + # *.res.mjs, lib/{js,es6,bs}, out, dist, .deno, generated/, *.min.js), + # and declaration headers (*.d.ts). Everything else is reported. + mapfile -t hits < <( + find . \ + \( -path './.git' -o -name node_modules -o -path '*/deps/*' \ + -o -path '*/vendor/*' -o -path '*/lib/js/*' -o -path '*/lib/es6/*' \ + -o -path '*/lib/bs/*' -o -path '*/out/*' -o -path '*/dist/*' \ + -o -path '*/.deno/*' -o -path '*/generated/*' \) -prune -o \ + -type f \ + \( -name '*.js' -o -name '*.jsx' -o -name '*.mjs' -o -name '*.cjs' \ + -o -name '*.ts' -o -name '*.tsx' \) \ + ! -name '*.res.js' ! -name '*.res.mjs' ! -name '*.min.js' ! -name '*.d.ts' \ + -print 2>/dev/null | sort || true + ) + + count=${#hits[@]} + + { + echo "# No-JS scan (warn-first)" + echo + echo "Estate policy: **no hand-authored JavaScript/TypeScript source.**" + echo "Destination is AffineScript -> typed-wasm, or Rust + Zig -> wasm." + echo "This check is **non-blocking** — it reports the migration surface only." + echo "Authoritative hard-block for new files: hypatia \`cicd_rules/javascript_detected\`." + echo "Policy: standards \`docs/NO-JAVASCRIPT-SOURCE-POLICY.adoc\`." + echo + echo "**Hand-authored JS/TS files found: ${count}**" + if [ "${count}" -gt 0 ]; then + echo + echo '| # | File |' + echo '|---|------|' + i=0 + for f in "${hits[@]}"; do + i=$((i + 1)) + echo "| ${i} | \`${f#./}\` |" + done + else + echo + echo "No hand-authored JavaScript/TypeScript found. :white_check_mark:" + fi + } >> "${GITHUB_STEP_SUMMARY}" + + if [ "${count}" -gt 0 ]; then + echo "::warning title=No-JS (warn-first)::${count} hand-authored JS/TS file(s) present. Estate target is AffineScript->typed-wasm / Rust+Zig->wasm. See standards docs/NO-JAVASCRIPT-SOURCE-POLICY.adoc (non-blocking)." + fi + + # Warn-first: never fail the build. + exit 0 diff --git a/docs/NO-JAVASCRIPT-SOURCE-POLICY.adoc b/docs/NO-JAVASCRIPT-SOURCE-POLICY.adoc new file mode 100644 index 00000000..25bde700 --- /dev/null +++ b/docs/NO-JAVASCRIPT-SOURCE-POLICY.adoc @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MPL-2.0 += No Hand-Authored JavaScript (Source) Policy +:revdate: 2026-06-02 +:status: ACTIVE (warn-first rollout) +:issue-254: hyperpolymath/standards#254 + +This document is the canonical estate-wide statement that estate-authored code +MUST NOT contain hand-authored JavaScript or TypeScript *source*. It complements +rather than replaces the existing machinery: + +* `docs/JS-RUNTIME-POLICY.adoc` — the JS *runtime & package-manager* hierarchy + (Deno > Bun > pnpm > npm) and the lockfile / `node_modules` rules. +* `.claude/CLAUDE.md` §JavaScript Exemptions / §TypeScript Exemptions — the + human-readable mirror of the hypatia carve-outs. +* hypatia `cicd_rules/javascript_detected`, `javascript_jsx_detected`, + `typescript_detected` — the SINGLE SOURCE OF TRUTH for detection and the + `path_allow_prefixes` carve-outs. This document does not override it. + +== The rule + +Estate-authored application logic is written in AffineScript (compiled to +typed-wasm) or, for performance / systems / ABI / FFI work, in Rust + Zig +compiled to WebAssembly with Idris2-proven ABIs. Hand-authored `.js`, `.jsx`, +`.mjs`, `.cjs`, `.ts`, and `.tsx` source is therefore not an accepted +destination for new estate code. + +This is a deliberate tightening of the earlier "JavaScript banned where +AffineScript cannot reach" stance ({issue-254}): the target is *no* hand-authored +JavaScript, not merely *no unnecessary* JavaScript. + +== What this does NOT cover (not "JavaScript source") + +The following are out of scope and MUST NOT be treated as violations: + +* *Generated / compiled output* — `*.res.js`, `*.res.mjs`, `lib/js/**`, + `lib/es6/**`, `lib/bs/**`, `out/**`, `dist/**`, `.deno/**`, `generated/**`, + `*.min.js`. Compiler output is not source. +* *The build-time wasm host shim* — the generated wasm-bindgen-class glue that + lets a wasm module reach the browser DOM/canvas. It is generated, not authored, + and unavoidable in a browser host (wasm has no direct DOM access). "No + JavaScript" means no hand-authored application JavaScript. +* *Declaration headers* — `*.d.ts` (FFI / library type boundaries). +* *Vendored / upstream* — `node_modules/**`, `deps/**`, and vendored forks. +* *Documented carve-outs* — every `path_allow_prefixes` entry on the hypatia + rules (MCP / plugin hosts, tooling configs, bootstrap shims, consumer-facing + bindings, VSCode extension entry points, archived repos, etc.). hypatia is + authoritative; the CLAUDE.md tables mirror it. + +== Enforcement (two deliberately distinct layers) + +[cols="1,3", options="header"] +|=== +| Layer | Behaviour + +| hypatia `cicd_rules/javascript_detected` (authoritative) +| HARD-BLOCK for NEW `.js` / `.jsx` in non-carve-out paths, with an inline + pragma escape (`// hypatia: allow cicd_rules/javascript_detected -- `). + Existing files are grandfathered while the {issue-254} migration proceeds. + +| `no-js-scan.yml` (this rollout — WARN-FIRST) +| NON-BLOCKING companion. On any push / PR touching JS or TS it reports the full + count and list of hand-authored JS/TS to the run summary, so each repo's + migration surface is visible. It does not fail the build while a repo's surface + is non-zero (once a repo reaches zero it flips to a blocking gate, e.g. + `gossamer`). It excludes only the + generated / vendored / declaration files listed above. By owner decision a + repo flips from warn to block once its surface reaches zero. +|=== + +== Rollout status (2026-06-02) + +* `gossamer` — surface is zero, so `no-js-scan` runs as a **blocking gate**: any + newly introduced hand-authored JS/TS fails CI. +* `standards` (29) and `burble` (14) — **warn-first** (non-blocking) while their + surfaces migrate; each flips to blocking once it reaches zero. +* `paint-type` — joins when write access is available; its `src/ui/app.js` is the + increment-0 MVP UI, slated to move into Rust/wasm per that repo's ADR-0002. + +Other estate repos adopt via template propagation.