Skip to content

Commit 2b88f34

Browse files
ci(no-js): warn-first no-JS scan + canonical no-JavaScript-source policy (#347)
## Summary Estate-wide **no hand-authored JavaScript (source)** rule plus its warn-first CI enforcement. - `docs/NO-JAVASCRIPT-SOURCE-POLICY.adoc` — canonical statement: estate-authored logic is AffineScript→typed-wasm or Rust+Zig→wasm; hand-authored `.js/.jsx/.mjs/.cjs/.ts/.tsx` is not an accepted destination. Tightens the earlier "no *unnecessary* JS" stance (standards#254). - `.github/workflows/no-js-scan.yml` — **warn-first / non-blocking** companion that reports the hand-authored JS/TS surface to the run summary. Excludes generated (`*.res.js`, `*.res.mjs`, `lib/{js,es6,bs}`, `out`, `dist`, `.deno`, `generated/`), vendored (`node_modules`, `deps`), and declaration (`*.d.ts`) files. **Relationship to existing enforcement:** this defers to hypatia `cicd_rules/javascript_detected` as the authoritative hard-block — it does not override or duplicate it. hypatia blocks *new* JS in non-carve-out paths; this scan surfaces the *full existing* surface for migration visibility. Surface reported in this repo: **29** files. ## Notes - Warn-only: cannot fail the build. Flips to blocking per-repo, by owner decision, once a repo reaches zero. - Companion workflow also added to `burble` (14) and `gossamer` (0). `paint-type` pending write access (the `hyperpolymath` credential is read-only there). Draft for review. https://claude.ai/code/session_01CS2BLBL22WTTjmc1UmEGa2 --- _Generated by [Claude Code](https://claude.ai/code/session_01CS2BLBL22WTTjmc1UmEGa2)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9bdaff9 commit 2b88f34

2 files changed

Lines changed: 175 additions & 0 deletions

File tree

.github/workflows/no-js-scan.yml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
name: No-JS Scan (warn-first)
3+
4+
# Estate policy: no hand-authored JavaScript/TypeScript source
5+
# (see standards docs/NO-JAVASCRIPT-SOURCE-POLICY.adoc). This workflow is
6+
# WARN-FIRST: it reports the authored-JS surface for migration but never fails
7+
# the build. The authoritative hard-block for NEW JS in non-carve-out paths is
8+
# hypatia (cicd_rules/javascript_detected); this is an additive companion.
9+
10+
on:
11+
push:
12+
paths:
13+
- '**/*.js'
14+
- '**/*.jsx'
15+
- '**/*.mjs'
16+
- '**/*.cjs'
17+
- '**/*.ts'
18+
- '**/*.tsx'
19+
pull_request:
20+
paths:
21+
- '**/*.js'
22+
- '**/*.jsx'
23+
- '**/*.mjs'
24+
- '**/*.cjs'
25+
- '**/*.ts'
26+
- '**/*.tsx'
27+
28+
# Estate guardrail: cancel superseded runs (read-only check, no mutation).
29+
concurrency:
30+
group: ${{ github.workflow }}-${{ github.ref }}
31+
cancel-in-progress: true
32+
33+
permissions:
34+
contents: read
35+
36+
jobs:
37+
scan-authored-js:
38+
name: Scan for hand-authored JavaScript/TypeScript
39+
runs-on: ubuntu-latest
40+
steps:
41+
- name: Checkout
42+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
43+
44+
- name: Report authored JS/TS (warn-first, non-blocking)
45+
shell: bash
46+
run: |
47+
# Exclude only things that are NOT hand-authored source: vendored
48+
# (node_modules, deps, vendor, .git), generated/compiled (*.res.js,
49+
# *.res.mjs, lib/{js,es6,bs}, out, dist, .deno, generated/, *.min.js),
50+
# and declaration headers (*.d.ts). Everything else is reported.
51+
mapfile -t hits < <(
52+
find . \
53+
\( -path './.git' -o -name node_modules -o -path '*/deps/*' \
54+
-o -path '*/vendor/*' -o -path '*/lib/js/*' -o -path '*/lib/es6/*' \
55+
-o -path '*/lib/bs/*' -o -path '*/out/*' -o -path '*/dist/*' \
56+
-o -path '*/.deno/*' -o -path '*/generated/*' \) -prune -o \
57+
-type f \
58+
\( -name '*.js' -o -name '*.jsx' -o -name '*.mjs' -o -name '*.cjs' \
59+
-o -name '*.ts' -o -name '*.tsx' \) \
60+
! -name '*.res.js' ! -name '*.res.mjs' ! -name '*.min.js' ! -name '*.d.ts' \
61+
-print 2>/dev/null | sort || true
62+
)
63+
64+
count=${#hits[@]}
65+
66+
{
67+
echo "# No-JS scan (warn-first)"
68+
echo
69+
echo "Estate policy: **no hand-authored JavaScript/TypeScript source.**"
70+
echo "Destination is AffineScript -> typed-wasm, or Rust + Zig -> wasm."
71+
echo "This check is **non-blocking** — it reports the migration surface only."
72+
echo "Authoritative hard-block for new files: hypatia \`cicd_rules/javascript_detected\`."
73+
echo "Policy: standards \`docs/NO-JAVASCRIPT-SOURCE-POLICY.adoc\`."
74+
echo
75+
echo "**Hand-authored JS/TS files found: ${count}**"
76+
if [ "${count}" -gt 0 ]; then
77+
echo
78+
echo '| # | File |'
79+
echo '|---|------|'
80+
i=0
81+
for f in "${hits[@]}"; do
82+
i=$((i + 1))
83+
echo "| ${i} | \`${f#./}\` |"
84+
done
85+
else
86+
echo
87+
echo "No hand-authored JavaScript/TypeScript found. :white_check_mark:"
88+
fi
89+
} >> "${GITHUB_STEP_SUMMARY}"
90+
91+
if [ "${count}" -gt 0 ]; then
92+
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)."
93+
fi
94+
95+
# Warn-first: never fail the build.
96+
exit 0
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
= No Hand-Authored JavaScript (Source) Policy
3+
:revdate: 2026-06-02
4+
:status: ACTIVE (warn-first rollout)
5+
:issue-254: hyperpolymath/standards#254
6+
7+
This document is the canonical estate-wide statement that estate-authored code
8+
MUST NOT contain hand-authored JavaScript or TypeScript *source*. It complements
9+
rather than replaces the existing machinery:
10+
11+
* `docs/JS-RUNTIME-POLICY.adoc` — the JS *runtime & package-manager* hierarchy
12+
(Deno > Bun > pnpm > npm) and the lockfile / `node_modules` rules.
13+
* `.claude/CLAUDE.md` §JavaScript Exemptions / §TypeScript Exemptions — the
14+
human-readable mirror of the hypatia carve-outs.
15+
* hypatia `cicd_rules/javascript_detected`, `javascript_jsx_detected`,
16+
`typescript_detected` — the SINGLE SOURCE OF TRUTH for detection and the
17+
`path_allow_prefixes` carve-outs. This document does not override it.
18+
19+
== The rule
20+
21+
Estate-authored application logic is written in AffineScript (compiled to
22+
typed-wasm) or, for performance / systems / ABI / FFI work, in Rust + Zig
23+
compiled to WebAssembly with Idris2-proven ABIs. Hand-authored `.js`, `.jsx`,
24+
`.mjs`, `.cjs`, `.ts`, and `.tsx` source is therefore not an accepted
25+
destination for new estate code.
26+
27+
This is a deliberate tightening of the earlier "JavaScript banned where
28+
AffineScript cannot reach" stance ({issue-254}): the target is *no* hand-authored
29+
JavaScript, not merely *no unnecessary* JavaScript.
30+
31+
== What this does NOT cover (not "JavaScript source")
32+
33+
The following are out of scope and MUST NOT be treated as violations:
34+
35+
* *Generated / compiled output* — `*.res.js`, `*.res.mjs`, `lib/js/**`,
36+
`lib/es6/**`, `lib/bs/**`, `out/**`, `dist/**`, `.deno/**`, `generated/**`,
37+
`*.min.js`. Compiler output is not source.
38+
* *The build-time wasm host shim* — the generated wasm-bindgen-class glue that
39+
lets a wasm module reach the browser DOM/canvas. It is generated, not authored,
40+
and unavoidable in a browser host (wasm has no direct DOM access). "No
41+
JavaScript" means no hand-authored application JavaScript.
42+
* *Declaration headers* — `*.d.ts` (FFI / library type boundaries).
43+
* *Vendored / upstream* — `node_modules/**`, `deps/**`, and vendored forks.
44+
* *Documented carve-outs* — every `path_allow_prefixes` entry on the hypatia
45+
rules (MCP / plugin hosts, tooling configs, bootstrap shims, consumer-facing
46+
bindings, VSCode extension entry points, archived repos, etc.). hypatia is
47+
authoritative; the CLAUDE.md tables mirror it.
48+
49+
== Enforcement (two deliberately distinct layers)
50+
51+
[cols="1,3", options="header"]
52+
|===
53+
| Layer | Behaviour
54+
55+
| hypatia `cicd_rules/javascript_detected` (authoritative)
56+
| HARD-BLOCK for NEW `.js` / `.jsx` in non-carve-out paths, with an inline
57+
pragma escape (`// hypatia: allow cicd_rules/javascript_detected -- <reason>`).
58+
Existing files are grandfathered while the {issue-254} migration proceeds.
59+
60+
| `no-js-scan.yml` (this rollout — WARN-FIRST)
61+
| NON-BLOCKING companion. On any push / PR touching JS or TS it reports the full
62+
count and list of hand-authored JS/TS to the run summary, so each repo's
63+
migration surface is visible. It does not fail the build while a repo's surface
64+
is non-zero (once a repo reaches zero it flips to a blocking gate, e.g.
65+
`gossamer`). It excludes only the
66+
generated / vendored / declaration files listed above. By owner decision a
67+
repo flips from warn to block once its surface reaches zero.
68+
|===
69+
70+
== Rollout status (2026-06-02)
71+
72+
* `gossamer` — surface is zero, so `no-js-scan` runs as a **blocking gate**: any
73+
newly introduced hand-authored JS/TS fails CI.
74+
* `standards` (29) and `burble` (14) — **warn-first** (non-blocking) while their
75+
surfaces migrate; each flips to blocking once it reaches zero.
76+
* `paint-type` — joins when write access is available; its `src/ui/app.js` is the
77+
increment-0 MVP UI, slated to move into Rust/wasm per that repo's ADR-0002.
78+
79+
Other estate repos adopt via template propagation.

0 commit comments

Comments
 (0)