Skip to content

feat(stdlib): Deno scripting surface — walk/args/exit/iso/regex/stderr (step #242) - #445

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/deno-scripting-externs-step3
May 30, 2026
Merged

feat(stdlib): Deno scripting surface — walk/args/exit/iso/regex/stderr (step #242)#445
hyperpolymath merged 1 commit into
mainfrom
feat/deno-scripting-externs-step3

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

Adds six externs to stdlib/Deno.affine to unblock the TS→AffineScript estate migration:

  • walkRecursive(root) -> [String] — depth-first file walk
  • args() -> [String]Deno.args
  • exit(code) -> IntDeno.exit (never returns)
  • dateNowIso() -> Stringnew Date().toISOString()
  • consoleError(s) -> Int — stderr; returns 0
  • regexMatch(s, pat) -> Boolnew 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.ts and standards/scripts/check-ts-allowlist.ts) become portable using only the existing Deno.affine surface plus these six additions.

Lowering

  • walkRecursive, regexMatch get tiny prelude shims (__as_walkRecursive recurses through Deno.readDirSync; __as_regexMatch wraps RegExp.test).
  • args, exit, dateNowIso, consoleError lower directly to host expressions.

Test plan

  • dune build — green
  • New fixture tests/codegen-deno/deno_scripting.{affine,harness.mjs} — in-memory FS for walk, captured stderr, intercepted exit. New harness passes.
  • All 12 codegen-deno harnesses pass
  • dune runtest — 346 tests green

Refs

Notes

This is the first-cut for step 3. Other gaps not addressed here (HTTP server, fetch, crypto.subtle, Deno.Command, recursive-stat with isFile/isDirectory flags) 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

…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
@hyperpolymath
hyperpolymath enabled auto-merge (squash) May 30, 2026 12:40
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 82 issues detected

Severity Count
🔴 Critical 4
🟠 High 10
🟡 Medium 68

⚠️ Action Required: Critical security issues found!

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

@hyperpolymath
hyperpolymath merged commit e4f8fbf into main May 30, 2026
26 of 27 checks passed
@hyperpolymath
hyperpolymath deleted the feat/deno-scripting-externs-step3 branch May 30, 2026 12:43
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)
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)
hyperpolymath added a commit that referenced this pull request Jul 7, 2026
…tale hypatia pin) (#679)

## Problem — every PR carries two red checks

1. **`analyze (actions, none)`** — CodeQL, and per the workflow's own
comment a **required** check:
   ```
##[error]Loaded a configuration file for version '4.36.2', but running
version '4.36.3'
   ```
Root cause: dependabot #670/#673 bumped `analyze`/`upload-sarif` to
v4.36.3 (`54f647b7`) but left **`init`** at v4.36.2 (`8aad20d1`) — a
partial bump (dependabot treats init/analyze as separate actions). init
writes a config the newer analyze rejects. The stale `# v3.28.1`
comments hid the split.

2. **`hypatia / Hypatia Neurosymbolic Analysis`** — `Cache not found for
input keys: hypatia-scanner-v2-… → exit 1`.
Root cause: the wrapper pinned the standards reusable at `d135b05` — the
known estate CI-red master-cause pin. Fixed upstream in standards
**#428** (GITHUB_TOKEN), **#441** (un-stale scanner cache — our exact
symptom), #445, #453.

## Fix (2 files, 3 lines)
- `codeql.yml`: `init` → `54f647b7 # v4.36.3` (same SHA as analyze);
comments corrected to the SHAs' real version.
- `hypatia-scan.yml`: reusable pin `d135b05` → `5fa7a834e` (#453, latest
commit touching the reusable).

YAML validated. This PR's own check run is the live test — `analyze
(actions, none)` and `hypatia` should both go green here.

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

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant