Skip to content

Commit 2f72900

Browse files
committed
ci(no-js): warn-first no-JS scan + canonical no-JavaScript-source policy
Adds a non-blocking no-js-scan.yml reporting the hand-authored JS/TS surface (excludes generated/vendored/declaration files) plus docs/NO-JAVASCRIPT-SOURCE-POLICY.adoc as the canonical estate statement. Complements hypatia cicd_rules/javascript_detected (authoritative hard-block) without overriding it. Warn-first rollout per standards#254. https://claude.ai/code/session_01CS2BLBL22WTTjmc1UmEGa2
1 parent fc7abf5 commit 2f72900

2 files changed

Lines changed: 170 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: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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 never fails the build. It excludes only the
64+
generated / vendored / declaration files listed above. By owner decision a
65+
repo flips from warn to block once its surface reaches zero.
66+
|===
67+
68+
== Rollout status (2026-06-02)
69+
70+
Warn-first `no-js-scan.yml` added to: `standards`, `burble`, `gossamer`.
71+
`gossamer` is already at zero hand-authored JS/TS. `paint-type` joins when write
72+
access is available — its `src/ui/app.js` is the increment-0 MVP UI, slated to
73+
move into Rust/wasm per that repo's ADR-0002. Other estate repos adopt via
74+
template propagation.

0 commit comments

Comments
 (0)