Skip to content

fix(ci): foundational CI/CD audit — bytesLength + STEP 4-B stdlib decls + governance allowlist + Pages precheck + Scorecard caller perms - #511

Merged
hyperpolymath merged 2 commits into
mainfrom
fix/cicd-foundational-2026-06-01
Jun 1, 2026
Merged

fix(ci): foundational CI/CD audit — bytesLength + STEP 4-B stdlib decls + governance allowlist + Pages precheck + Scorecard caller perms#511
hyperpolymath merged 2 commits into
mainfrom
fix/cicd-foundational-2026-06-01

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

Foundational CI/CD audit of main (HEAD 319bc84). Four red lanes had four distinct root causes; this PR closes all four + adds a regression test that catches the most pernicious one before merge.

Workflow Status before Root cause Fix
CI / build red, every push bytesLength / math_random etc. wired in codegen + fixture but missing pub extern fn in stdlib/Deno.affine (PR #504 + #509 partial landings) 7 decls added; regression test test/test_deno_builtins_consistency.ml enforces subset across all stdlib/*.affine
Governance / Language anti-pattern red, every push check-ts-allowlist scanner's glob⇒regex doesn't anchor against the ./-prefixed paths from walkRecursive(".") — all 3 CLAUDE.md exemptions matched zero files in practice added .governance-allowlist (Layer 2.5) with a leading-wildcard pattern that works against the prefixed paths; underlying scanner bug deferred to upstream
GitHub Pages red, every push Pages not enabled on the repo; configure-pages@v6 enablement: true can't escalate to admin via GITHUB_TOKEN precheck job probes gh api .../pages and short-circuits build/deploy when disabled; emits a clear unblock notice
Scorecards startup_failure since adoption permissions: read-all at workflow level diverged from the canonical caller block (contents: read); SHA pin was the first cut of the reusable aligned to canonical form + bumped reusable SHA to standards/main HEAD

Verification

  • dune build bin/main.exe: clean.
  • dune runtest: 357/357 tests green (including the new consistency test).
  • tools/run_codegen_deno_tests.sh: all 30 harnesses pass.
  • check-ts-allowlist locally: ✅ "No TypeScript files outside allowlist (4 per-repo exemption(s) parsed across CLAUDE.md + .governance-allowlist)."

Owner action (one-time, optional, separate)

To unblock the Pages workflow's deploy lane: enable Pages once via Settings → Pages (source: GitHub Actions). The new precheck job's notice will flip from "Pages not enabled" to "Pages enabled — proceeding with build + deploy" on the next push.

What was NOT changed

  • Other repos in the estate (owner directive: don't go estate-wide; the scanner bug is filed-as-comment as belonging upstream at standards).
  • .hypatia-ignore — governs a different rule.
  • .claude/CLAUDE.md TypeScript Exemptions table — left as Layer-2 reference; .governance-allowlist is the working Layer-2.5 backstop.
  • claude/websocket-binding branch — left alone (parallel-session WIP).
  • tests/codegen-deno/*.deno.js regenerated outputs — left at their pre-existing committed state; the test runner regenerates them on every run.

Test plan

  • dune build bin/main.exe clean
  • dune runtest 357/357 green
  • tools/run_codegen_deno_tests.sh all 30 harnesses pass
  • check-ts-allowlist clean against local checkout
  • Post-merge: next push run is green on CI / Governance / Scorecards
  • Pages stays guard-skipped until owner enables Pages (then green)

🤖 Generated with Claude Code

…ges precheck + Scorecard caller perms

Foundational CI/CD audit of main (HEAD 319bc84). Four red lanes, four
root-cause fixes.

1. CI / build — compiler resolution failure (real bug)

tests/codegen-deno/deno_scripting_part2.affine and random_smoke.affine
imported `use Deno::{ bytesLength, bytesByteAt, bytesAsciiSlice, ... }`
and `use Deno::{ math_random, random_u32, random_in_range,
performance_now }` respectively, but the matching `pub extern fn` decls
never landed in stdlib/Deno.affine. PR #504 (STEP 3) and #509 (STEP 4-B)
each added the codegen lowering in lib/codegen_deno.ml `deno_builtins`
and the fixture, but skipped the stdlib decl — so the resolver bails
with `undefined value: bytesLength` on every push.

Added 7 missing `pub extern fn` decls in stdlib/Deno.affine:
bytesLength / bytesByteAt / bytesAsciiSlice (STEP 3 Bytes I/O read-only
accessors) and math_random / random_u32 / random_in_range /
performance_now (STEP 4-B Randomness + high-res clock).

To prevent the same shape recurring: added
test/test_deno_builtins_consistency.ml — reads lib/codegen_deno.ml
`deno_builtins` and every stdlib/*.affine, asserts codegen subset
⊆ stdlib externs (modulo a small `codegen_only_names` allowlist for
true compiler intrinsics like len / panic / http_request / the
JSON-FFI bridge surface). Catches the missing-decl shape locally
before merge. 357/357 tests green.

2. Governance / TypeScript anti-pattern — .governance-allowlist

Hypatia's check-ts-allowlist parsed all 3 rows of .claude/CLAUDE.md's
TypeScript Exemptions table but matched zero files: its `globToRegex`
emits an unanchored `^affinescript-deno-test/.*\.ts$` while
`__as_walkRecursive(".")` yields paths *with* a leading `./` prefix,
so the anchor fails. The 3 builtin-allowed paths (mod.ts, the two
.d.ts) hid the breakage on the small-.ts-count cases until lib/runner.ts
was added.

Added a .governance-allowlist (Layer 2.5, called out in the scanner's
own remediation message) with a `*affinescript-deno-test/*.ts` pattern
that compiles to `^.*affinescript-deno-test/.*\.ts$`, which DOES match
`./affinescript-deno-test/...`. Filed-as-comment in the file: the
underlying scanner bug belongs upstream at standards.

3. GitHub Pages — precheck guard

actions/configure-pages@v6 `enablement: true` returns 403 "Resource not
accessible by integration" on a repo where Pages was never enabled
(verified: gh api repos/.../pages → 404). GITHUB_TOKEN cannot escalate
to repo-admin, so first-run self-provisioning is structurally
impossible. Added a `precheck` job that probes the Pages API and
short-circuits build + deploy when disabled, with a one-line notice
and unblock instruction.

When Pages is enabled (Settings → Pages, source: GitHub Actions),
the precheck flips to enabled=true and the build/deploy chain runs.

4. Scorecards supply-chain security — match canonical caller

Every Scorecard run since the workflow was adopted has been
startup_failure — no successful run in the 10-deep history (PR #457's
"add job-level perms" landed but did not unblock). Caller had
`permissions: read-all` at workflow level, diverging from the canonical
caller block in standards/.github/workflows/scorecard-reusable.yml and
from the known-working sibling julia-professional-registry/scorecard.yml
(`permissions: contents: read`).

Aligned to the canonical form + bumped the reusable's pinned SHA to the
latest on standards/main (e0caf11 → e03686486). Verified SHA exists on
standards.

What was NOT changed

- Other repos in the estate — owner directive: don't go estate-wide.
- .hypatia-ignore — unchanged; governs a different rule.
- .claude/CLAUDE.md TS exemption table — left as Layer-2 reference;
  the new .governance-allowlist is the working Layer-2.5 backstop.
- claude/websocket-binding branch — left alone (parallel-session WIP).
- tests/codegen-deno/*.deno.js regenerated outputs — left at their
  pre-existing committed state; the test runner regenerates them on
  every run so the committed copies are aide-mémoire only.

Verification

- dune build bin/main.exe: clean.
- dune runtest: 357/357 tests green (including the new consistency
  test).
- tools/run_codegen_deno_tests.sh: all 30 harnesses pass.
- check-ts-allowlist locally: "No TypeScript files outside allowlist
  (4 per-repo exemption(s) parsed across CLAUDE.md + .governance-
  allowlist)."

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath
hyperpolymath enabled auto-merge (squash) June 1, 2026 11:49
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 88 issues detected

Severity Count
🔴 Critical 2
🟠 High 16
🟡 Medium 70

⚠️ 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": "missing_timeout_minutes",
    "file": "affine-vscode-publish.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

#496

Same shape as the bytesLength STEP-3 / STEP-4-B + governance-allowlist
fixes in 105baf1: a recent code-shipping PR landed new files but did
not update the cross-file metadata that gates them.

PRs #481/#494/#495/#496 added `partial1.res`, `phase3.res`, `phase3b.res`,
`phase3c.res` under `tools/res-to-affine/test/fixtures/` as migration-
assistant input corpus. The `.hypatia-ignore` Layer-2 exemption file
only carried the two original fixtures (`sample.res`, `phase2c.res`),
so the banned-language scanner flagged the four new ones — but the
failure was MASKED on main by the TypeScript-allowlist red, which
exits the same `language-policy` job before reaching the .res gate.
The 105baf1 allowlist fix unmasked it.

Added the four paths under the same Layer-2 exemption header, with a
short comment explaining the masking and the surfacing.

This is a per-repo backstop; the underlying "code adds files but skips
the gate file" pattern keeps repeating (codegen ⊆ stdlib decls, TS
allowlist glob, this hypatia-ignore lag) and belongs upstream as a
pre-commit check. Filed-as-comment for the upstream follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 88 issues detected

Severity Count
🔴 Critical 2
🟠 High 16
🟡 Medium 70

⚠️ 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": "missing_timeout_minutes",
    "file": "affine-vscode-publish.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in casket-pages.yml",
    "type": "missing_timeout_minutes",
    "file": "casket-pages.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in ci.yml",
    "type": "missing_timeout_minutes",
    "file": "ci.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
hyperpolymath merged commit 03d7d67 into main Jun 1, 2026
28 checks passed
@hyperpolymath
hyperpolymath deleted the fix/cicd-foundational-2026-06-01 branch June 1, 2026 12:29
hyperpolymath added a commit that referenced this pull request Jun 1, 2026
First slice of the proof-obligation catalogue's "ready-this-week" list
— small, no-stdlib-dep semigroup/monoid laws over built-in operators,
asserted through the tree-walking interpreter (the source-level oracle,
backend-independent).

## Cases

| # | Law | Operators |
|---|---|---|
| 1 | `(a ++ b) ++ c = a ++ (b ++ c)` | string `++` associativity |
| 2 | `"" ++ s = s` | string `++` left unit |
| 3 | `s ++ "" = s` | string `++` right unit |
| 4 | `5 / 2 = 2` | int `/` truncates toward zero (positive) |
| 5 | `-5 / 2 = -2` | int `/` truncates toward zero (negative); #478 source-level |
| 6 | `(a + b) + c = a + (b + c)` | int `+` associativity |

Each case compiles a small inline `.affine` source string, runs the
full frontend (parse → resolve → typecheck) + the interpreter, looks
up the law-checker function in the resulting env, applies it with no
args, and asserts the returned `Int` is `0` (convention: zero means
the law held; nonzero is the failing branch's tag).

The interpreter is the right oracle: it executes the AS semantics
directly, so we're testing what every backend must preserve, not just
one backend's lowering. The #478 fix's *codegen* invariant is already
covered by the int_div codegen-deno harness; this case asserts the
*source-level* invariant the codegen must respect.

## Tests run

363 / 363 (was 357 — six new). No flakes. Total runtime delta ≈ 50ms.

## Next batches (catalogue refs)

- Option / Result functor laws (`map(id) = id`, `map(f ∘ g) = map(f) ∘ map(g)`)
- Dict key uniqueness under insert/delete
- Bytes endianness round-trip (`bytes_get_u32_le ∘ bytes_set_u32_le = id`)
- JSON round-trip (`parse ∘ stringify = id` on canonical Json values)
- Hash stability for derived-`Hash` AST types

Stacked on #511 (fix/cicd-foundational-2026-06-01). After #511 merges,
this branch rebases cleanly via patch-equivalent skip on the shared
commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Jun 1, 2026
…cked on #511 (#512)

## Summary

First slice of the proof-obligation catalogue's "ready-this-week" list.
Six semigroup/monoid laws over built-in operators (no stdlib dep),
asserted through the **tree-walking interpreter** — the source-level
oracle, backend-independent.

| # | Law |
|---|---|
| 1 | string `++` associativity: `(a ++ b) ++ c = a ++ (b ++ c)` |
| 2 | string `++` left unit: `"" ++ s = s` |
| 3 | string `++` right unit: `s ++ "" = s` |
| 4 | int `/` truncates positive: `5 / 2 = 2` |
| 5 | int `/` truncates negative (#478 source-level): `-5 / 2 = -2` |
| 6 | int `+` associativity: `(a + b) + c = a + (b + c)` |

## Why the interpreter is the right oracle

We assert what every backend **must preserve**, not what one backend
lowers to. The #478 fix's codegen invariant is already covered by the
`int_div` codegen-deno harness; this PR's case 5 asserts the
*source-level* invariant the codegen must respect — if the AS semantics
were ever changed (e.g. to floored division like Python), this would
catch it before any backend was even compiled.

## Test count

**363 / 363** (was 357). Runtime delta ≈ 50 ms. No flakes.

## Next batches

The catalogue's top-20 ready-this-week items continue with:
- Option / Result functor laws (`map(id) = id`, `map(f ∘ g) = map(f) ∘
map(g)`)
- Dict key uniqueness under insert/delete
- Bytes endianness round-trip
- JSON round-trip
- Hash stability on derived-`Hash` AST types
- Borrow-graph return-escape & NLL-last-use regression assertions
- Formatter idempotence

Each batch ships as one focused PR so reverts are surgical and the
proof-obligation tracking issue list (to be filed) maps 1:1.

## Stacked on #511

Base is `main`; the diff currently includes #511's changes because the
branch forks from `fix/cicd-foundational-2026-06-01`. Once #511 merges,
`git rebase main` patch-equivalent-skips the shared commits and this PR
shrinks to the 2-file delta.

## Test plan

- [x] `dune runtest` 363 / 363 green
- [x] All 6 new cases pass on local interpreter
- [ ] Same after #511 merges + rebase

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

Co-authored-by: Claude Opus 4.7 (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