Skip to content

fix(governance): TS allowlist regex + introduce .governance-allowlist (Layer 2.5)#185

Open
hyperpolymath wants to merge 1 commit into
mainfrom
claude/governance-allowlist-foundation
Open

fix(governance): TS allowlist regex + introduce .governance-allowlist (Layer 2.5)#185
hyperpolymath wants to merge 1 commit into
mainfrom
claude/governance-allowlist-foundation

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Root-causes and fixes the long-standing governance / Language / package anti-pattern policy false-fail on every PR in affinescript.

The job parses per-repo TypeScript exemptions from a markdown table in .claude/CLAUDE.md, but the original heading-match regex was the literal pattern TypeScript [Ee]xemptions — accepting only the exact two-word heading. affinescript's heading is ### TypeScript / JavaScript Exemptions (Approved) — the slash and JavaScript between the two keywords mean the regex never matched, the 3 legitimate exemptions (packages/affine-js/types.d.ts, packages/affinescript-cli/mod.d.ts, affinescript-deno-test/*.ts) were silently ignored, and the check has been red on every PR in that repo for as long as the heading text has carried the extra words.

Two-layer fix

  1. Regex relaxation + multi-table support. The exemption-heading regex is now (?:TypeScript|JavaScript|TS|JS|\.tsx?)\b[^#\n]*[Ee]xemption so it matches the slash-form heading, the singular form, and .ts-mentioning variants. The loop is also no longer "break on first heading" — it scans every heading and re-evaluates whether the next section is another exemption table.

  2. .governance-allowlist mechanism (new Layer 2.5). Optional plain-text allowlist file at the repo root. One glob per line, #-comments supported. Decoupled from .claude/CLAUDE.md heading text. Both sources merge on every check; either alone is sufficient.

Test plan

  • Python smoke: regex matches 11/11 representative heading variants
  • End-to-end parse of affinescript/.claude/CLAUDE.md against patched logic yields 3 exemptions (was 0)
  • docs/EXEMPTION-MECHANISMS.adoc updated with Layer 2.5 section

Cross-refs

🤖 Generated with Claude Code

…t` (Layer 2.5)

The `language-policy` job in `governance-reusable.yml` parses
per-repo TypeScript exemptions from a markdown table in
`.claude/CLAUDE.md`, but the original heading-match regex was the
literal pattern `TypeScript [Ee]xemptions` — accepting only the exact
two-word heading. Any repo with a different heading text silently
failed to parse and the workflow then false-failed on every PR.

Concrete fallout (root cause traced 2026-05-26):

* `affinescript`'s heading is `### TypeScript / JavaScript Exemptions
  (Approved)` — the slash and `JavaScript` are between the two
  keywords, so the literal regex never matched. Three legitimate
  exemptions (`packages/affine-js/types.d.ts`,
  `packages/affinescript-cli/mod.d.ts`, `affinescript-deno-test/*.ts`)
  were silently ignored, and the check has been red on every PR in
  that repo for as long as the heading text has carried the extra
  words. Documented as known baseline noise in affinescript's
  CLAUDE.md "Known-failing baseline checks" section; this fix
  resolves the actual root cause.

Two-layer fix:

1. **Regex relaxation + multi-table support.** The exemption-heading
   regex is now `(?:TypeScript|JavaScript|TS|JS|\.tsx?)\b[^#\n]*[Ee]xemption`
   so it matches the slash-form heading, the singular form, and
   `.ts`-mentioning variants. The loop is also no longer "break on
   first heading" — it now scans every heading and re-evaluates
   whether the next section is another exemption table, so a CLAUDE.md
   with multiple exemption tables (e.g. TypeScript + Runtime, or split
   per-platform) gets all of them parsed instead of just the first.

2. **`.governance-allowlist` mechanism (Layer 2.5).** New optional
   plain-text allowlist file at the repo root. One glob per line,
   `#`-comments supported. Decoupled from `.claude/CLAUDE.md` heading
   text — so a repo can carry a stable allowlist that survives prose
   rewrites and isn't sensitive to which exact words appear in the
   markdown. Both sources merge on every check; either alone is
   sufficient. Failure message now mentions the new file in the
   resolution menu, and the parsed-count line credits both sources.

Test plan covered:

* Regex matches 11/11 representative heading variants in a Python
  smoke (affinescript's exact `### TypeScript / JavaScript Exemptions
  (Approved)` → matches; `### Runtime Exemptions` → correctly does
  NOT match; `### Closed exemptions:` prose → correctly does NOT
  match).
* End-to-end parse of affinescript's `.claude/CLAUDE.md` against the
  patched logic: yields 3 exemptions (was 0 with the old regex). All
  three paths line up with the documented table rows.

Docs: `docs/EXEMPTION-MECHANISMS.adoc` gains a "Layer 2.5" section
explaining when to reach for the new file vs the markdown table
(Layer 1.5 number kept clear of the existing 3-layer numbering to
avoid renumber-churn; the file already documents bot-denylist as
Layer 1 and Hypatia baseline as Layer 2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath enabled auto-merge (squash) May 26, 2026 09:23
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 118 issues detected

Severity Count
🔴 Critical 64
🟠 High 43
🟡 Medium 11

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/deno-ci-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "deno-ci-reusable.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance-reusable.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Python file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/standards/standards/a2ml-templates/state-scm-to-v2.py",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/standards/standards/a2ml/bindings/deno/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/standards/standards/lol/test/vitest.config.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/standards/standards/k9-svc/bindings/deno/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "Agda postulate assumes without proof -- potential soundness hole (4 occurrences, CWE-704)",
    "type": "agda_postulate",
    "file": "/home/runner/work/standards/standards/lol/proofs/theories/information_theory.agda",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "believe_me undermines formal verification (1 occurrences, CWE-704)",
    "type": "believe_me",
    "file": "/home/runner/work/standards/standards/lol/src/abi/Locale.idr",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "Wildcard CORS -- restrict to specific origins or use env var (1 occurrences, CWE-942)",
    "type": "js_wildcard_cors",
    "file": "/home/runner/work/standards/standards/consent-aware-http/examples/reference-implementations/deno/aibdp_middleware.js",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

hyperpolymath added a commit to hyperpolymath/affinescript that referenced this pull request May 26, 2026
…unpublished (#381)

## Summary

Root-causes and fixes the long-standing `vscode-smoke` false-fail on
every PR. The check has been red since the WASM-host-bindings refactor
because `@hyperpolymath/affine-vscode` (host adapter) is unpublished on
npm (issue #104, owner-action-gated). Documented as known baseline noise
in CLAUDE.md; this PR resolves the actual root cause at source.

## Failure chain

1. The `.cjs` extension's WASM module imports the `Vscode` and
`VscodeLanguageClient` host modules.
2. `_makeVscodeBindings` is loaded from `@hyperpolymath/affine-vscode`
via defensive try-catch in `out/extension.cjs` — when the package is
absent, the variable stays `null`.
3. `extraImports()` returns `{}`; the WASM import set lacks the `Vscode`
/ `VscodeLanguageClient` modules.
4. `WebAssembly.instantiate` rejects with "module is not an object or
function".
5. `extension.activate()` throws; AC1 fails; whole suite fails.

## Foundational fix

- **`editors/vscode/test/suite/extension.test.js`**: `suiteSetup` now
detects whether `@hyperpolymath/affine-vscode` resolves via
`require.resolve()`. If not, it calls `this.skip()` (mocha's whole-suite
skip) with a `console.warn` explaining the gate. CI then reports the
suite as SKIPPED with all tests skipped — not failed. When the npm
publish lands and the adapter resolves, the detector flips
automatically.
- **`.github/workflows/ci.yml`**: adds a "Report adapter availability"
step before the smoke run that prints a GitHub `::notice` annotation
with current state ("present — smoke will run" vs "NOT installed
(optional, awaits #104 npm publish) — smoke will skip").

## Why not "make the dep optional"

It already IS an `optionalDependency`. The failure is downstream at
WASM-instantiate time, because the WASM module's import set is fixed at
compile time and cannot be satisfied without the adapter. Skipping the
smoke is therefore the correct foundational fix until the npm package
lands.

## Expected behaviour after merge

Every PR run:
- "Report adapter availability" prints the NOT-installed annotation
- "Run in-editor smoke (xvfb)" passes with all tests SKIPPED
- The `vscode-smoke` check goes from RED to GREEN on every PR

Once #104 closes, the detector flips, smoke runs as written.

## Test plan

- [x] `node --check` on the modified test file — clean
- [x] `python3 -c "import yaml; yaml.safe_load(open(...))"` on the
workflow — clean
- [x] `node -e "require.resolve('@hyperpolymath/affine-vscode')"` in
this env throws (expected); detector flag flips to false correctly

## Cross-refs

- Closes part of #139
- Refs #104 (the upstream owner-action-gated publish)
- Refs hyperpolymath/standards#185 (parallel governance-check fix)

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

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
auto-merge was automatically disabled May 26, 2026 14:41

Base branch was modified

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