feat(stdlib): Deno scripting surface — walk/args/exit/iso/regex/stderr (step #242)#445
Merged
Merged
Conversation
…r (step #242) Adds six externs to stdlib/Deno.affine to unblock the TS→AffineScript estate migration (campaign hyperpolymath/standards#239), step 3 (standards#242 — STDLIB FILL): - walkRecursive(root) -> [String] (depth-first file walk) - args() -> [String] (Deno.args) - exit(code) -> Int (Deno.exit; never returns) - dateNowIso() -> String (new Date().toISOString()) - consoleError(s) -> Int (stderr; returns 0) - regexMatch(s, pat) -> Bool (new RegExp(pat).test(s)) These were surfaced as the minimum-viable set by a survey of tail-batch-1 candidates (standards#241): two pure-logic Deno CLI scripts (panic-attack docs/campaigns/2026-05-26/01-triage.ts and standards/scripts/check-ts-allowlist.ts) become portable using only the existing Deno.affine surface plus these six additions. Other tail-batch candidates remain blocked on fetch / HTTP-server / crypto plumbing which is out of scope here. Lowering: - walkRecursive, regexMatch get tiny prelude shims (__as_walkRecursive recurses through Deno.readDirSync; __as_regexMatch wraps RegExp.test). - args, exit, dateNowIso, consoleError lower direct to host expressions (Deno.args / Deno.exit(c) / new Date().toISOString() / (console.error(s), 0)). Test: tests/codegen-deno/deno_scripting.{affine,harness.mjs} stubs the filesystem (in-memory fs for walk), captures stderr writes, and intercepts Deno.exit so the harness completes. All 12 codegen-deno harnesses pass; full dune runtest = 346 green. Refs hyperpolymath/standards#242 Refs hyperpolymath/standards#239
🔍 Hypatia Security ScanFindings: 82 issues detected
View findings[
{
"reason": "Action perpolymath/standards/.github/workflows/governance-reusable.yml@main\n needs attention",
"type": "unpinned_action",
"file": "governance.yml",
"action": "pin_sha",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Action ons/checkout@v6\n needs attention",
"type": "unpinned_action",
"file": "publish-jsr.yml",
"action": "pin_sha",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Action land/setup-deno@v2\n needs attention",
"type": "unpinned_action",
"file": "publish-jsr.yml",
"action": "pin_sha",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in affine-vscode-publish.yml",
"type": "unknown",
"file": "affine-vscode-publish.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in casket-pages.yml",
"type": "unknown",
"file": "casket-pages.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in casket-pages.yml",
"type": "unknown",
"file": "casket-pages.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in ci.yml",
"type": "unknown",
"file": "ci.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in ci.yml",
"type": "unknown",
"file": "ci.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in ci.yml",
"type": "unknown",
"file": "ci.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in ci.yml",
"type": "unknown",
"file": "ci.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
2 tasks
hyperpolymath
added a commit
to hyperpolymath/standards
that referenced
this pull request
May 30, 2026
…Refs #239) (#283) ## Summary AffineScript port of `scripts/check-ts-allowlist.ts` — the meta-tool that enforces the no-new-TypeScript estate policy. Self-referential: this script is itself one of the `.ts` files its policy applies to. Symbolic landing. Filed under the estate TS→AffineScript migration campaign: - Umbrella: #239 - Step: #241 (STEP 2 — tail-batch-1) - Unblocked by: hyperpolymath/affinescript#445 (Deno scripting externs — walk/args/exit/iso/regex/stderr) ## Pattern Adapted from phronesis#19 seed: adds `.affine` alongside the live `.ts`; CI keeps invoking the `.ts`. The follow-up PR can wire `.github/workflows/governance-reusable.yml` to the compiled `.deno.js` directly (no `.mjs` wrapper needed — `main()` now calls `Deno.exit()` itself, matching the TS original). ## Behaviour Faithful mirror of the TS: - Built-in directory + filename allowlist (`DIR_NAMES_ALLOWED`, `*.d.ts`, `mod.ts`, `lsp*.ts`, `*-lsp.ts`, `*.bench.ts`, `*_bench.ts`, segments containing `vscode` or starting with `deno-`) - **Layer 2** — `.claude/CLAUDE.md` TypeScript-exemption table parser (heading regex matches `TypeScript|JavaScript|TS|JS|.tsx?` `[Ee]xemption`, multi-table aware) - **Layer 2.5** — `.governance-allowlist` plain-text glob list - Glob→regex translation (`*` → `.*`, `?` → `.`, regex-escape chars) - Exempt fallback: regex match, then literal `bare` equality, then trailing-slash prefix match - Lex-sort of the violation list (manual `str_lt` via `char_to_int` — AffineScript has no `<` on String) - Same exit code (0 success / 1 violation via `Deno.exit`) and same output text ## Drive-by SPDX header normalised to `MPL-2.0` (per estate language-policy 2026-05-25; `PMPL-1.0-or-later` on the live `.ts` is the legacy form pending its own cleanup). ## Test plan - [x] `affinescript compile scripts/check-ts-allowlist.affine -o /tmp/check-ts-allowlist.deno.js --deno-esm` → exit 0, clean compile (oracle per campaign prompt) - [ ] Build-chain integration (workflow switch from `.ts` to compiled `.deno.js`) — deferred to follow-up PR per seed pattern ## Notes Branch was started by a parallel Claude session (479L draft); this PR added `str_lt` byte-wise lex compare (TS used `<` on String which AffineScript doesn't overload), SPDX header drive-by, and the exit()-direct refactor so no `.mjs` wrapper is needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
9 tasks
hyperpolymath
added a commit
to hyperpolymath/standards
that referenced
this pull request
May 30, 2026
…gn STEP 2 seed) (#284) ## Summary Adds `scripts/check-ts-allowlist.affine` as the AffineScript port of the existing `scripts/check-ts-allowlist.ts`, filed under the estate TS->AffineScript migration campaign (#239 umbrella, STEP 2 — tail-batch-1 per standards#241). **Self-referential**: the script that enforces the no-new-TypeScript policy is itself one of the TS files that policy applies to. Landing the AffineScript version is a symbolic milestone for the meta-tool. **Pattern**: phronesis#19 seed style. Add `.affine` alongside the live `.ts`. No workflow change in this PR; CI keeps invoking the `.ts` via `deno run --allow-read .standards-checkout/scripts/check-ts-allowlist.ts`. Workflow cutover (compile `.affine` to `.deno.js`, wire wrapper, retire `.ts`) is a follow-up issue. ## Behaviour equivalence The regression suite at `scripts/tests/check-ts-allowlist-test.sh` exercises 13 cases (builtin allowlist classes + Layer-2 CLAUDE.md exemptions + Layer-2.5 governance-allowlist + dotted-dir skip + multi-heading-table parsing). - **All 13 PASS** against the AffineScript-emitted `.deno.js` (verified locally with a sibling harness using the same fixtures) - **All 13 PASS** against the original `.ts` (no regression) Both implementations are behaviour-equivalent on the substring assertions the suite makes. Cosmetic difference: the `.affine` port uses ASCII `[FAIL]` / `[OK]` sentinels instead of the original emoji (see "Seam findings" below). ## Stdlib surface used All externs already shipped in `stdlib/Deno.affine` via affinescript#445: - `walkRecursive` (recursive file enumeration) - `regexMatch` (JS RegExp.test wrapper) - `readTextFile` (synchronous file read; throws on missing — wrapped in `try`/`catch`) - `args`, `exit`, `consoleError` Plus AffineScript builtins: `string_get`, `string_sub`, `string_find`, `char_to_int`, `int_to_string`, `len`. ## AffineScript seam findings surfaced by this port (Each would be a separate affinescript-repo PR — out of scope for this per-standards-repo PR per the campaign's ownership gate.) - **String less-than lex-compare**: not built-in. Implemented inline via byte-wise `str_lt` (calls `char_to_int(string_get(...))`). Bare `a < b` on `String` produces `TypeMismatch (String, Int)`. - **`break`/`continue` in `while`**: `BREAK`/`CONTINUE` tokens are reserved in `lib/parser.mly` but no production rule uses them yet. Refactored two natural occurrences (`s_trim` inner loops + `strip_leading_dot_slash`) to combined-guard / sentinel-boolean forms. - **Non-ASCII string literals**: lower to octal escape sequences (e.g. `"\\226\\157\\140"`) in `--deno-esm` output. Strict-mode ESM rejects octal escapes with `SyntaxError: Octal escape sequences are not allowed in strict mode.` Worked around with ASCII `[FAIL]` / `[OK]` sentinels. - **Stale installed binary**: the `affinescript` at `~/.local/bin/` predated PR #445 and silently emitted bare `walkRecursive(".")` calls (no `__as_walkRecursive` shim in the prelude). A trunk rebuild surfaces the new shims correctly. ## Sequencing follow-ups (NOT part of this PR) - [ ] Compile-time wiring: build `.affine` -> `.deno.js` in CI; commit `.deno.js` as a generated artefact OR add a precompile step. - [ ] Workflow cutover: update `.github/workflows/governance-reusable.yml` to invoke the `.deno.js` (with the existing `--allow-read` scope). - [ ] Retire `.ts`: delete `scripts/check-ts-allowlist.ts` and update `docs/EXEMPTION-MECHANISMS.adoc` references. - [ ] Upstream affinescript fixes for the 3 seam findings above (file as separate affinescript-repo issues). ## Test plan - [x] `affinescript check` type-checks the `.affine` - [x] `affinescript compile --deno-esm` emits `.deno.js` with no octal-escape errors - [x] 13/13 regression tests pass against the AffineScript-emitted `.deno.js` - [x] 13/13 regression tests pass against the original `.ts` (no regression) - [x] GPG-signed commits ## Refs - Refs #239 (campaign umbrella) - Refs #241 (STEP 2 — TAIL BATCH 1) - Pattern: hyperpolymath/phronesis#19 (STEP 1 seed) - Unblocked by: hyperpolymath/affinescript#445 (STEP 3 first-cut) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This was referenced May 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds six externs to
stdlib/Deno.affineto unblock the TS→AffineScript estate migration:walkRecursive(root) -> [String]— depth-first file walkargs() -> [String]—Deno.argsexit(code) -> Int—Deno.exit(never returns)dateNowIso() -> String—new Date().toISOString()consoleError(s) -> Int— stderr; returns 0regexMatch(s, pat) -> Bool—new RegExp(pat).test(s)These were surfaced by surveying the campaign #239 step 2 tail-batch-1 candidates: two pure-logic Deno CLI scripts (
panic-attack docs/campaigns/2026-05-26/01-triage.tsandstandards/scripts/check-ts-allowlist.ts) become portable using only the existingDeno.affinesurface plus these six additions.Lowering
walkRecursive,regexMatchget tiny prelude shims (__as_walkRecursiverecurses throughDeno.readDirSync;__as_regexMatchwrapsRegExp.test).args,exit,dateNowIso,consoleErrorlower directly to host expressions.Test plan
dune build— greentests/codegen-deno/deno_scripting.{affine,harness.mjs}— in-memory FS for walk, captured stderr, intercepted exit. New harness passes.dune runtest— 346 tests greenRefs
Notes
This is the first-cut for step 3. Other gaps not addressed here (HTTP server,
fetch,crypto.subtle,Deno.Command, recursive-stat withisFile/isDirectoryflags) remain on the step-3 docket — they block heavier ports (mock-echidna, civic-connect services, dotfiles health host) which are out of scope for tail-batch-1.🤖 Generated with Claude Code