From d6136a3a049ce942b3f42e2a432f727a6f743b98 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 26 May 2026 10:33:05 +0100 Subject: [PATCH 1/2] ci(rust): convert rust-ci.yml to thin wrapper (standards#174) Replaces the per-repo `rust-ci.yml` copy with a 5-line wrapper invoking the shared reusable workflow in `hyperpolymath/standards` (PR #174). Pinned to PR #174's HEAD SHA `4fdf4314b4ab54269adbaff10e30e483b5e86845`; will resolve to standards/main once #174 merges. Estate audit found ~87 rust-ci.yml copies across the estate; this is one of them. The reusable provides identical cargo check/clippy/fmt/test behaviour with opt-in `enable_audit` + `enable_coverage` inputs. Pattern precedent: standards#168 (governance-reusable) + downstream wrappers absolute-zero#41 + tma-mark2#41. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/rust-ci.yml | 66 ++++++++--------------------------- 1 file changed, 15 insertions(+), 51 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index de781d0..7f0b7f8 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -1,56 +1,20 @@ # SPDX-License-Identifier: MPL-2.0 -permissions: - contents: read - +# Rust CI — thin wrapper calling the shared estate reusable in +# hyperpolymath/standards. Configure once, propagate everywhere. +# See: docs/CI-REUSABLE-WORKFLOWS.adoc in standards. name: Rust CI -on: [push, pull_request] -env: - CARGO_TERM_COLOR: always - RUSTFLAGS: -Dwarnings -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable - with: - components: rustfmt, clippy - - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 - - - name: Check formatting - run: cargo fmt --all -- --check - - - name: Clippy lints - run: cargo clippy --all-targets --all-features -- -D warnings - - - name: Run tests - run: cargo test --all-features - - - name: Build release - run: cargo build --release +on: + push: + branches: [main, master] + pull_request: - security: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable - - name: Install cargo-audit - run: cargo install cargo-audit - - name: Security audit - run: cargo audit - - name: Check for outdated deps - run: cargo install cargo-outdated && cargo outdated --exit-code 1 || true +permissions: + contents: read - coverage: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable - - name: Install tarpaulin - run: cargo install cargo-tarpaulin - - name: Generate coverage - run: cargo tarpaulin --out Xml - - uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3 - with: - files: cobertura.xml +jobs: + rust-ci: + uses: hyperpolymath/standards/.github/workflows/rust-ci-reusable.yml@4fdf4314b4ab54269adbaff10e30e483b5e86845 + with: + enable_audit: true + enable_coverage: true From a7453d66cd9ad0ada2c2b4fec1f38c9751c0aa60 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 26 May 2026 13:39:10 +0100 Subject: [PATCH 2/2] =?UTF-8?q?ci(sweep):=20codeql=20actions=20matrix,=20s?= =?UTF-8?q?afedom=20.res=E2=86=92.affine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace codeql.yml `javascript-typescript` matrix entry with `actions` (repo has no JS/TS source). Matches the canonical fix in rsr-template-repo#71 / mtpc#8 / thunderbird#75. - Replace examples/SafeDOMExample.res with the canonical examples/SafeDOMExample.affine (per gitbot-fleet#208 and the banned-ReScript anti-pattern policy). --- .github/workflows/codeql.yml | 5 +- examples/SafeDOMExample.affine | 129 +++++++++++++++++++++++++++++++++ examples/SafeDOMExample.res | 109 ---------------------------- 3 files changed, 133 insertions(+), 110 deletions(-) create mode 100644 examples/SafeDOMExample.affine delete mode 100644 examples/SafeDOMExample.res diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9e32d15..1aa0293 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -30,7 +30,10 @@ jobs: fail-fast: false matrix: include: - - language: javascript-typescript + # Repo source is not JS/TS; scan workflow files via the + # actions extractor (every repo has those). See hypatia rule + # codeql_language_matrix_mismatch. + - language: actions build-mode: none steps: diff --git a/examples/SafeDOMExample.affine b/examples/SafeDOMExample.affine new file mode 100644 index 0000000..2a62c1d --- /dev/null +++ b/examples/SafeDOMExample.affine @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MPL-2.0 +// SafeDOMExample.affine — formally-verified DOM mounting (aspirational). +// +// This example shows the *shape* of SafeDOM consumer code in current +// AffineScript syntax. The `SafeDOM` stdlib surface it references +// (`mount_safe`, `mount_when_ready`, `mount_batch`, +// `proven_selector_validate`, `proven_html_validate`, `mount`) is the +// target of `affinescript#56` (DOM+Pixi binding survey) and does not +// yet exist in the published stdlib. The file is therefore +// parse-checked but not type-checked end-to-end until #56 lands the +// bindings; `affinescript check` reports `Resolve.UndefinedModule +// SafeDOM` which is expected. +// +// Previous versions of this file (estate-wide, 5 dialect variants) +// pre-dated ADR-014 (qualified paths), ADR-016 (effect rows), and the +// `#{`-record-literal sigil (ADR-215). They were retired in favour of +// this canonical via the gitbot-fleet#208 sweep (2026-05-26). + +module SafeDOMExample; + +use prelude::{Option, Some, None, Result, Ok, Err}; + +// `Element` and friends are nominal extern types for now — the real +// shape lands with affinescript#56. +extern type Element; +extern type Selector; +extern type ValidHTML; + +// Single-mount status, lifted from the host into a typed tag union. +enum MountStatus { + Mounted(Element), + MountPointNotFound(String), + InvalidSelector(String), + InvalidHTML(String) +} + +// Batch-mount result. +enum MountResult { + Mounted([Element]), + Failed(String) +} + +// Spec for one element in a batch mount. +struct MountSpec { + selector: String, + html: String +} + +// SafeDOM's host-side surface, all IO-effecting. Callbacks are passed +// as separate parameters (rather than a `MountCallbacks` record) +// because fn-typed struct fields are not currently parser-supported. +extern fn mount_safe( + selector: ref String, + html: ref String, + on_success: fn(Element) -> (), + on_error: fn(String) -> (), +) -{IO}-> (); + +extern fn mount_when_ready( + selector: ref String, + html: ref String, + on_success: fn(Element) -> (), + on_error: fn(String) -> (), +) -{IO}-> (); + +extern fn mount_batch(specs: ref [MountSpec]) -{IO}-> MountResult; + +extern fn proven_selector_validate(s: ref String) -{IO}-> Result; +extern fn proven_html_validate(s: ref String) -{IO}-> Result; +extern fn mount(sel: ref Selector, html: ref ValidHTML) -{IO}-> MountStatus; + +extern fn array_for_each(xs: ref [Element], f: fn(Element) -> ()) -{IO}-> (); +extern fn array_len(xs: ref [Element]) -> Int; + +// Example 1 — basic mount with success/error branches. +pub fn mount_app() -{IO}-> () { + mount_safe( + "#app", + "

Hello, World!

Mounted safely with proofs.

", + fn(el) -> () { Console::log("App mounted successfully"); }, + fn(err) -> () { Console::error("Mount failed: " ++ err); }, + ); +} + +// Example 2 — defer until DOM ready. +pub fn mount_when_dom_ready() -{IO}-> () { + mount_when_ready( + "#app", + "

App Title

", + fn(_el) -> () { Console::log("Mounted after DOM ready"); }, + fn(err) -> () { Console::error("Failed: " ++ err); }, + ); +} + +// Example 3 — atomic batch mount. +pub fn mount_multiple() -{IO}-> () { + let specs = [ + MountSpec #{ selector: "#header", html: "

Site Title

" }, + MountSpec #{ selector: "#nav", html: "" }, + MountSpec #{ selector: "#main", html: "

Content here

" }, + MountSpec #{ selector: "#footer", html: "
2026
" }, + ]; + + match mount_batch(specs) { + Mounted(elements) => { + Console::log("Batch mount succeeded"); + array_for_each(elements, fn(_el) -> () { Console::log(" element"); }); + }, + Failed(err) => { + Console::error("Batch mount failed (atomic — none mounted): " ++ err); + } + } +} + +// Example 4 — explicit two-stage validation before mounting. +pub fn mount_with_validation() -{IO}-> () { + match proven_selector_validate("#my-app") { + Err(e) => Console::error("Invalid selector: " ++ e), + Ok(valid_selector) => match proven_html_validate("
Content
") { + Err(e) => Console::error("Invalid HTML: " ++ e), + Ok(valid_html) => match mount(valid_selector, valid_html) { + Mounted(_el) => Console::log("Mounted with validated inputs"), + MountPointNotFound(s) => Console::error("Element not found: " ++ s), + InvalidSelector(_) => Console::error("impossible — already validated"), + InvalidHTML(_) => Console::error("impossible — already validated"), + }, + }, + } +} diff --git a/examples/SafeDOMExample.res b/examples/SafeDOMExample.res deleted file mode 100644 index e5c9046..0000000 --- a/examples/SafeDOMExample.res +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Example: Using SafeDOM for formally verified DOM mounting - -open SafeDOM - -// Example 1: Basic mounting with error handling -let mountApp = () => { - mountSafe( - "#app", - "

Hello, World!

Mounted safely with proofs.

", - ~onSuccess=el => { - Console.log("✓ App mounted successfully!") - Console.log("Element:", el) - }, - ~onError=err => { - Console.error("✗ Mount failed:", err) - } - ) -} - -// Example 2: Wait for DOM ready before mounting -let mountWhenDOMReady = () => { - mountWhenReady( - "#app", - "

App Title

", - ~onSuccess=_ => Console.log("✓ Mounted after DOM ready"), - ~onError=err => Console.error("✗ Failed:", err) - ) -} - -// Example 3: Batch mounting (atomic - all or nothing) -let mountMultiple = () => { - let specs = [ - {selector: "#header", html: "

Site Title

"}, - {selector: "#nav", html: ""}, - {selector: "#main", html: "

Content here

"}, - {selector: "#footer", html: "
© 2026
"} - ] - - switch mountBatch(specs) { - | Ok(elements) => { - Console.log(`✓ Successfully mounted ${Array.length(elements)} elements`) - elements->Array.forEach(el => Console.log(" -", el)) - } - | Error(err) => { - Console.error("✗ Batch mount failed:", err) - Console.error(" (None were mounted - atomic operation)") - } - } -} - -// Example 4: Explicit validation before mounting -let mountWithValidation = () => { - // Validate selector first - switch ProvenSelector.validate("#my-app") { - | Error(e) => Console.error(`Invalid selector: ${e}`) - | Ok(validSelector) => { - // Validate HTML - switch ProvenHTML.validate("
Content
") { - | Error(e) => Console.error(`Invalid HTML: ${e}`) - | Ok(validHtml) => { - // Now mount with proven safety - switch mount(validSelector, validHtml) { - | Mounted(el) => Console.log("✓ Mounted with validated inputs:", el) - | MountPointNotFound(s) => Console.error(`✗ Element not found: ${s}`) - | InvalidSelector(_) => Console.error("Impossible - already validated") - | InvalidHTML(_) => Console.error("Impossible - already validated") - } - } - } - } -} - -// Example 5: Integration with TEA -module MyApp = { - type model = {message: string} - type msg = NoOp - - let init = () => {message: "Hello from TEA"} - let update = (model, _msg) => model - let view = model => `

${model.message}

` -} - -let mountTEAApp = () => { - let model = MyApp.init() - let html = MyApp.view(model) - - mountWhenReady( - "#tea-app", - html, - ~onSuccess=el => { - Console.log("✓ TEA app mounted") - // Set up event handlers, subscriptions here - }, - ~onError=err => Console.error(`✗ TEA mount failed: ${err}`) - ) -} - -// Entry point -let main = () => { - Console.log("SafeDOM Examples") - Console.log("================\n") - - // Choose which example to run - mountWhenDOMReady() // Run on DOM ready -} - -// Auto-execute when module loads -main()