Skip to content

fix(scripts): make check-ts-allowlist.affine compile cleanly (follow-up to #283)#310

Merged
hyperpolymath merged 2 commits into
mainfrom
fix/check-ts-allowlist-affine-compile
May 30, 2026
Merged

fix(scripts): make check-ts-allowlist.affine compile cleanly (follow-up to #283)#310
hyperpolymath merged 2 commits into
mainfrom
fix/check-ts-allowlist-affine-compile

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

The seed AffineScript port of check-ts-allowlist.ts shipped via #283 (STEP 2 seed, Refs #239) never actually compiled — nothing in CI runs the AS compiler against it (governance-reusable.yml invokes the .ts), so the compile-broken state went unnoticed. This PR makes it parse + type-check + compile cleanly so the planned workflow swap can proceed.

What was wrong

  • JS-style catch catch e { x } (AS catch is pattern-match arms, x3 occurrences)
  • Bare-brace record literals { raw, rx } (AS needs #{}, x2)
  • use collections::{ any }collections::any is private (only prelude::any([Bool]) is pub, wrong signature)
  • use string::{ trim, int_to_string, substring }trim and int_to_string are interpreter builtins (no use needed); string::substring is private. Existing substring(s, start, end) calls also assumed end-relative semantics, but the public alternative string_sub(s, start, length) is length-relative.

What this changes

Before After
catch e { "" } catch { _ => "" }
[{ raw, rx }] [#{ raw, rx }]
use collections::{ any }; if (any(fn(a) => s == a, list)) ... inline while j < len(list) { if s == list[j] ... }
use string::{ trim, int_to_string, substring } drop (builtins); use string_sub directly
substring(g2, 1, len(g2)) string_sub(g2, 1, len(g2) - 1) (length, not end)
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: PMPL-1.0-or-later

The license change brings the .affine into line with the .ts source it ports and with .machine_readable/licensing-policy.toml (PMPL-1.0-or-later is the preferred SPDX in this repo).

Verification

Local AS build oracle (affinescript repo, current _build/default/bin/main.exe):

  • affinescript check ../standards/scripts/check-ts-allowlist.affine -> Type checking passed
  • affinescript compile --deno-esm ../standards/scripts/check-ts-allowlist.affine -> Compiled -> out.wasm (Deno-ESM)

Scope

  • Workflow unchanged: governance-reusable.yml still invokes the .ts source of truth.
  • Once this lands, the planned follow-up (compile .affine -> .deno.js + tiny .mjs wrapper -> repoint workflow -> retire .ts) is unblocked.

Test plan

  • CI green on this branch (no behavioural changes to the .ts script that the regression test covers)
  • AS compiler oracle remains green on main after merge (re-run affinescript check from a fresh clone)
  • Follow-up PR to wire the compiled output into governance-reusable.yml

Refs #239 (umbrella TS->AffineScript), #241 (STEP 2 tail-batch-1). Follow-up to #283 (seed PR).

Provenance: this work was resumed from a Gemini-CLI session that crashed with ioctl(2) failed, EBADF in node-pty/unixTerminal.js:243 mid-edit — the parse-error squashing fixes above are what Gemini was attempting when its pty died.

Generated with Claude Code

…-broken seed from #283)

The seed `.affine` port shipped in #283 never compiled — it used JS-style
catch (`catch e { ... }`), bare-brace record literals (`{ raw: ..., ... }`),
the private `collections::any` / `string::substring`, and a `string::substring`
end-relative semantics call (start, end) where AS expects (start, length).
None of these were caught at PR time because nothing in CI runs the AS
compiler against the file (`governance-reusable.yml` still invokes the .ts).

Refactor to compile cleanly:
- `catch e { x }` → `catch { _ => x }` (x3) — AS catch is pattern-match arms.
- `{ raw, rx }` → `#{ raw, rx }` (x2) — AS record literals need `#{}`.
- Drop `use collections::{ any }` (private); inline the membership loop.
- Drop `use string::{ trim, int_to_string, substring }` — `trim` and
  `int_to_string` are interpreter builtins; `substring` is private. Rewrite
  call sites to use the `string_sub(s, start, length)` builtin directly,
  adjusting the two end-relative calls (`len(g2)` -> `len(g2) - 1`).
- License: MPL-2.0 -> PMPL-1.0-or-later, matching the .ts source and the
  per-repo policy in `.machine_readable/licensing-policy.toml`.

Verification (from affinescript build oracle):
- affinescript check       -> Type checking passed
- affinescript compile --deno-esm -> out.wasm (Deno-ESM)

Workflow remains unchanged — `governance-reusable.yml` still invokes the
`.ts` source of truth. This PR only unblocks the planned follow-up of
swapping the workflow to the AS-compiled `.deno.js` entry point.

Refs #239 (umbrella TS->AffineScript), #241 (STEP 2 tail-batch-1).
Follow-up to #283 (seed PR).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath enabled auto-merge (squash) May 30, 2026 17:49
…310)

Compile-clean wasn't enough — `deno run scripts/check-ts-allowlist.deno.js`
crashed on first invocation with two bugs:

1. `Identifier '_' has already been declared`
   The 12 `let _ = consoleError(...)` / `let _ = println(...)` calls all
   lower to `const _ = (console.error(...), 0);` in the same block scope —
   ESM strict-mode rejects the redeclaration. Drop the `let _ =` wrappers;
   bare expression statements (`consoleError("...");`) lower cleanly and
   match the merged seed's pattern.

2. `ReferenceError: endsWith is not defined`
   `Deno.endsWith` is declared as `pub extern fn` in `Deno.affine` but has
   no entry in the Deno-ESM codegen lookup table — the compiler emits the
   call site but never the definition (other stdlib `extern fn`s like
   `walkRecursive`, `regexMatch`, `readTextFile` are codegen-known and get
   inline `__as_*` shims; `endsWith` isn't on that list). Restore the
   merged seed's defensive inline helper backed by the `string_sub`
   builtin and switch all call sites from `endsWith` → `ends_with`.

End-to-end runtime verification on local fixtures:
  empty dir              -> exit 0, success line
  src/Foo.ts violation   -> exit 1, error block + file list
  mod.ts (filename ok)   -> exit 0
  scripts/x.ts (dir ok)  -> exit 0

Behaviour matches the .ts source the regression suite asserts against.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 30, 2026
…o.js runtime

Mirrors the workflow swap in this PR. Three updates to
`docs/EXEMPTION-MECHANISMS.adoc`:

1. Layer 4a description — point at `scripts/check-ts-allowlist.affine`
   as the source of truth, note the compiled `.deno.js` is what the
   workflow actually runs.

2. History — add a fourth bullet covering standards#283 / #310 / #311:
   the TS→AffineScript port arc, with the dual-target rationale spelled
   out (`.ts` kept for regression suite + parallel-validation; `.deno.js`
   is the workflow target).

3. Cross-references — replace the single `.ts` line with three lines
   covering all three files (.affine source / .deno.js runtime / .ts
   archetype) and what each is for. Reduces "which file should I edit?"
   ambiguity for the next contributor.

No behavioural change; pure doc alignment so the prose matches the
workflow + source layout after #311 lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 30, 2026
…compiled .deno.js (#311)

**Depends on: #310**

Second half of the seed-PR plan from #283: once the `.affine` source
actually compiles + runs (#310), wire the workflow at the
AffineScript-compiled output.

## Changes

1. **`scripts/check-ts-allowlist.deno.js`** (NEW, 344 lines)
- Generated by `affinescript compile --deno-esm` from the post-#310
`.affine` source. Self-contained Deno-ESM bundle: inlined runtime shims
for codegen-known externs (walkRecursive, regexMatch, readTextFile,
exit, consoleError) + the `split` / `ends_with` helpers. Auto-invokes
`await main()` at module load — same shape Deno runs the existing .ts
with.

2. **`.github/workflows/governance-reusable.yml:193`**
- `deno run ... check-ts-allowlist.ts` → `...
check-ts-allowlist.deno.js`
- Comment block explains the dual-target window: .ts kept for the
regression suite (`scripts/tests/check-ts-allowlist-test.sh`) and for
parallel-validation; retirement is a separate follow-up after the
validation window.

## Why ship the compiled artifact (vs compile-in-CI)

Compile-in-CI would require an AffineScript toolchain in the workflow
(OCaml + dune + affinescript repo as a dep). The compiler is
deterministic for this surface; committing the output keeps CI
provider-free and matches the precedent in
`affinescript/tests/codegen-deno/*.deno.js` (committed golden files).

Drift between source and committed output is the obvious risk. Follow-up
will add a `just check-ts-allowlist-drift` recipe (recompile + diff)
wired into a non-blocking CI lint.

## Verification

End-to-end runtime test of the compiled output (matches the runtime fix
in PR #310 commit b046787):

| fixture | exit | output |
|---|---|---|
| empty dir | 0 | success line |
| `src/Foo.ts` violation | 1 | error block + file list |
| `mod.ts` (filename ok) | 0 | success line |
| `scripts/x.ts` (dir ok) | 0 | success line |

## Test plan

- [ ] CI green on this branch — note AS scanner / Hypatia / SPARK
Theatre Gate may need to acknowledge the new committed `.deno.js` as a
generated artifact (ignore findings tagged as generated)
- [ ] `scripts/tests/check-ts-allowlist-test.sh` still passes against
the unchanged .ts archetype (no behavioural change to .ts)
- [ ] Caller repos using `governance-reusable.yml` show green "Check for
TypeScript" steps after merge

Refs #239 (umbrella TS→AffineScript), #241 (STEP 2 tail-batch-1).
Follow-up plan: add drift-detection lint, then a separate PR retires the
.ts archetype after the parallel-validation window.

Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath merged commit 5f83ea9 into main May 30, 2026
0 of 18 checks passed
@hyperpolymath hyperpolymath deleted the fix/check-ts-allowlist-affine-compile branch May 30, 2026 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant