feat(rules): AffineScript hand-port pitfalls — HANDLE-as-fn-name + OCaml float ops#332
Merged
Merged
Conversation
…aml float ops Adds 5 patterns under language `"affine"`/`"affinescript"` to catch the two classes of parse-blocking hand-port leaks that surfaced during the sustainabot ReScript→AffineScript migration (gitbot-fleet#148). Both classes were resolved manually in that session via parser PRs (affinescript#370–#376) and hand-port rewrites (gitbot-fleet#206), but the underlying *signal* — "this code won't parse" — was discovered only after `affinescript check` was run. Capturing them as Hypatia rules surfaces the signal at PR-review time, before the iteration loop. The two pitfalls: 1. **`handle` as a fn name.** AffineScript's `handle` is a reserved keyword (HANDLE token), used by the effect-handler expression form `handle body { handler_arms }`. So `pub fn handle(...)` is a parse error. Detection: regex matching `fn handle(` or `fn handle<` at start of line (incl. visibility/totality modifiers). Severity: high. Recommended rename: `dispatch`, `handle_request`, `handle_event`. 2. **OCaml-style float operators.** AffineScript uses unified `+ - * /` for both Int and Float (per `examples/lessons/01_hello.affine`). The OCaml form `+. -. *. /.` is never accepted. Detection: 4 separate regex patterns (one per operator), each matching `<operand-char> <op>. <space>` so we don't fire on `.0`-suffixed numeric literals (e.g. `1.0 +` parses as `1.0+`, not `1 +.`). Severity: high (parse blocker). Integration: * New `@affine_hand_port_patterns` module attribute in `lib/rules/code_safety.ex` (immediately after `@rescript_patterns`). * Dispatch registered for both `"affine"` and `"affinescript"` language keys (mirrors the `javascript`/`typescript` alias precedent at lines 384-385). * File-extension fallback in `lib/rules/rules.ex` after the existing JS/TS web-security fallback: any `.affine` file gets the hand-port scan even when the caller passes `language = nil` or an unrelated upstream default. Cross-refs: - Refs hyperpolymath/gitbot-fleet#148 - Refs hyperpolymath/affinescript#370 - Refs hyperpolymath/affinescript#371 - Refs hyperpolymath/affinescript#372 - Refs hyperpolymath/affinescript#373 - Refs hyperpolymath/affinescript#376 - Refs hyperpolymath/gitbot-fleet#206 Test plan: - Regex spot-check via `elixir -e 'Regex.scan(...)'`: `pub fn handle(` matches; `let x = a /. 1.0` matches; `let x = a / 1.0` does NOT match (true negative). Confirmed all three. - `elixirc` of `code_safety.ex` and `rules.ex` succeeds (only pre-existing module-resolution warnings unrelated to this patch). - `mix compile` blocked by a Phoenix-dep / Elixir-1.15 mismatch in the sandbox env; not in scope for this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🔍 Hypatia Security ScanFindings: 20 issues detected
View findings[
{
"reason": "expect() in hot path (2 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/batch.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
"type": "unwrap_dangerous_default",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/scan.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (1 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/scan.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (2 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/fleet.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (2 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/cli/src/output.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (2 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/cli/build.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (2 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/fixer/src/main.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (1 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/integration/src/lib.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "1 untracked file(s) -- review and add or .gitignore",
"type": "GS001",
"file": ".",
"action": "review",
"rule_module": "git_state",
"severity": "low"
},
{
"reason": "Repository has 4 non-main remote branch(es). Policy: single main branch only.",
"type": "GS007",
"file": ".",
"action": "delete_remote_branches",
"rule_module": "git_state",
"severity": "medium"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
hyperpolymath
added a commit
to hyperpolymath/gitbot-fleet
that referenced
this pull request
May 27, 2026
…ep + Hypatia) (#211) ## Summary Adds the second human + machine readable session record for 2026-05-26. Turn 2 follows the gitbot-fleet#148 sustainabot validation (turn 1, captured in `SESSION-2026-05-26-sustainabot-148-validation.md`). The user's four sequenced asks for turn 2: 1. Set automerge on the seven #148 PRs (7/7 enabled; 4 cleared in-session; 2 still queued at session-write time) 2. Resolve CI/CD baseline noise **foundationally at root/source/upstream** 3. Pass new lessons to the Hypatia ruleset if not already captured 4. Document everything for humans and machines 5. Search the estate for SafeDOMExample and resolve estate-wide ## Outcome 10 PRs merged this turn + 4 outstanding (2 BLOCKED awaiting review, 2 still queued in baseline-CI). 5 dialect-distinct SafeDOMExample variants resolved across 50 stale copies in 4 repos (1,267 `.res` siblings deferred to affinescript#57 Phase 2). ## Files - `docs/archive/SESSION-2026-05-26-cicd-foundational-fixes.md` — Human-readable; full per-phase narrative with root-cause traces. - `docs/archive/README.md` — Table updated with the new entry. - `.machine_readable/SESSION-2026-05-26-cicd.a2ml` — Machine-readable companion; per-phase PR rosters, agent-memory audit, captured lessons. ## Cross-refs - Refs gitbot-fleet#148, #208, #210 - Refs hyperpolymath/affinescript#370, #371, #372, #373, #376, #381 - Refs hyperpolymath/standards#185, #188 - Refs hyperpolymath/hypatia#332 - Refs hyperpolymath/burble#92 - Refs hyperpolymath/claude-gecko-browser-extension#30 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 5 patterns under language
"affine"/"affinescript"to catch parse-blocking hand-port leaks that surface when.res/.ml/.recode is migrated to AffineScript. Both classes were discovered manually during the sustainabot ReScript→AffineScript migration (gitbot-fleet#148) and resolved via 5 parser PRs + a hand-port-rewrite PR — but the underlying signal ("this code won't parse") only emerged afteraffinescript check. Capturing them as Hypatia rules surfaces the signal at PR-review time, before the iteration loop.The two pitfalls
handleas a fn name. AffineScript'shandleis a reserved keyword (HANDLE token), used by the effect-handler expression formhandle body { handler_arms }. Sopub fn handle(...)is a parse error. Severity::high. Recommended rename:dispatch,handle_request,handle_event.OCaml-style float operators. AffineScript uses unified
+ - * /for both Int and Float. The OCaml form+. -. *. /.is never accepted. Four separate patterns (one per operator) — each requires an operand-character preceding the operator-and-dot, so.0-suffixed numeric literals (e.g.1.0 + x) don't false-positive. Severity::high(parse blocker).Integration
@affine_hand_port_patternsmodule attribute inlib/rules/code_safety.ex(immediately after@rescript_patterns)."affine"and"affinescript"language keys (mirrors thejavascript/typescriptalias precedent at lines 384-385).lib/rules/rules.exafter the existing JS/TS web-security fallback: any.affinefile gets the hand-port scan even when the caller passeslanguage = nilor an unrelated upstream default.Test plan
elixir -e 'Regex.scan(...)':pub fn handle(matches;let x = a /. 1.0matches;let x = a / 1.0does NOT match (true negative). All three confirmed.elixircof both modified files succeeds (only pre-existing module-resolution warnings unrelated to this patch).mix compileblocked by a Phoenix-dep / Elixir-1.15 mismatch in the sandbox env; not in scope for this PR.Cross-refs
🤖 Generated with Claude Code