From ef55945932f5d417b8d205530f0868cff6d73ffa Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Wed, 8 Jul 2026 05:17:19 +0800 Subject: [PATCH 1/2] claude note --- CLAUDE.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 82df03d..5663734 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -413,8 +413,8 @@ If you believe a skip is genuinely warranted, stop and ask the user; do not add ### Known intermittent CI failure: pyo3-runner torch registration timeout -`et-ws-pyo3-runner`'s `module_behaves::case_5_torch` intermittently fails on test.yaml's Windows lane with the -literal failure line +`et-ws-pyo3-runner`'s `module_behaves::case_5_torch` intermittently fails on test.yaml's Windows and macOS lanes +with the literal failure line Error: "runner never registered" @@ -426,7 +426,11 @@ torch's import when the test's registration timeout expired. The ~117 MB `pipx:t cold runner is the slow step; a rerun passes because the import caches warm. Observed on commit `6479913bdc288dd680fbe0520f63054e8c71fe6c` at `https://github.com/edge-toolkit/core/actions/runs/28686533955/job/85080173283` (PR #70; the rerun passed and the PR -merged). If this signature recurs, stop rerunning and fix the root cause: raise (or make torch-case-specific) the +merged), and on the `default (macos-latest, 45)` job -- same signature, unix-form +`torch/lib/python3.13/site-packages/torch/_subclasses/functional_tensor.py:362` warning path -- on commit +`f2a85307a2415f4a50625627795e02c84f1c8e5d` at +`https://github.com/edge-toolkit/core/actions/runs/28865327221/job/85613919558` (PR #73), 20.68s into the test +run. If this signature recurs, stop rerunning and fix the root cause: raise (or make torch-case-specific) the runner-registration timeout in the pyo3-runner module tests, or warm the torch import before the registration clock starts. From 244711dd666febf1de6775970053ccfda26a741b Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Wed, 8 Jul 2026 06:43:00 +0800 Subject: [PATCH 2/2] Enable local C# roslynator --- .deepsource.toml | 7 +++++ .mise/config.dotnet.toml | 39 ++++++++++++++++++++--- .mise/config.toml | 15 +++++---- .mise/config.windows.toml | 45 +++++++++++++++------------ config/conftest/policy/mise/mise.rego | 30 +++++++++++++++--- 5 files changed, 101 insertions(+), 35 deletions(-) diff --git a/.deepsource.toml b/.deepsource.toml index e97af7a..0784103 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -10,6 +10,13 @@ exclude_patterns = ["**/pkg/**", "generated/**", "services/ws-web-runner/mingw-s # Repo convention: tests live in a `tests/` directory or in source files prefixed `test_`. test_patterns = ["**/test_*.py", "**/tests/**"] +# Dart is explicitly deactivated; its coverage comes from the mise pipeline's check:dart instead. +# DeepSource's Dart support is an AI-only analyzer (no classic static analysis), so with AI review off it +# auto-surfaces as a perpetually-skipped "DeepSource: Dart" check on every PR. +[[analyzers]] +enabled = false +name = "dart" + [[analyzers]] enabled = true name = "python" diff --git a/.mise/config.dotnet.toml b/.mise/config.dotnet.toml index d9f445d..bff0c21 100644 --- a/.mise/config.dotnet.toml +++ b/.mise/config.dotnet.toml @@ -3,9 +3,14 @@ [tools] # Pinned: SDK churn re-keys the wasm-tools workload manifest, forcing a ~1.5GB pack re-download on every bump. dotnet = "10.0.300" -dotnet-core = "10.0.300" "dotnet:roslynator.dotnet.cli" = "latest" +[vars] +# MSBuild dir inside the mise dotnet SDK, for the roslynator `-m` flags below. +# `$DOTNET_ROOT` stays literal here; the task shell expands it. The version segment is the [tools] dotnet +# pin above -- bump together. +dotnet_msbuild = "$DOTNET_ROOT/sdk/10.0.300" + [tasks.dotnet-fmt] description = "Format dotnet C# code" run = "dotnet format" @@ -14,15 +19,41 @@ run = "dotnet format" description = "Check dotnet C# formatting" run = "dotnet format --verify-no-changes" -# Namespaced aggregators picked up by the default config's globbed `check`/`fmt`. +[tasks.roslynator-check] +description = "Analyze C# sources (Roslynator)" +# Roslynator analyzes with its embedded analyzer assemblies, against the mise dotnet SDK's own MSBuild. +# The dotnet:roslynator.dotnet.cli tool ships the analyzers, so the csproj needs no analyzer +# PackageReference. Diagnostics at the default info-and-up severity fail the task; loading the project +# performs an implicit restore, so no `dotnet restore` dependency is needed. +# +# `-m` pins MSBuild to the mise dotnet SDK instead of MSBuildLocator's automatic discovery. On Windows the +# inline-task shell is busybox ash (windows_default_inline_shell_args), and busybox-w32 rewrites `\` to `/` +# in every env var it passes to children -- DOTNET_ROOT arrives as `C:/Users/...`, a form hostfxr accepts +# (dotnet-check works) but MSBuildLocator's SDK discovery does not, failing with: +# Cannot choose MSBuild location automatically. Use option '-m, --msbuild-path' to specify MSBuild location +# Observed on commit ef55945932f5d417b8d205530f0868cff6d73ffa (locally; reproduced under `mise exec` by +# setting a forward-slashed DOTNET_ROOT). The env var itself is fine as the `-m` argument -- mise's core +# dotnet plugin exports DOTNET_ROOT on every OS, and the forward-slashed form ash delivers on Windows is +# accepted there; only MSBuildLocator's own probing of it breaks. +run = 'roslynator analyze -m "{{ vars.dotnet_msbuild }}" services/ws-modules/dotnet-data1/dotnet-data1.csproj' + +[tasks.roslynator-fix] +description = "Apply Roslynator diagnostics' machine-applicable C# fixes in place" +run = 'roslynator fix -m "{{ vars.dotnet_msbuild }}" services/ws-modules/dotnet-data1/dotnet-data1.csproj' + +# Namespaced aggregators picked up by the default config's globbed `check`/`fmt`/`fix`. [tasks."check:dotnet"] -depends = ["dotnet-check"] -description = "Run .NET checks (format verification)" +depends = ["dotnet-check", "roslynator-check"] +description = "Run .NET checks (format verification + Roslynator analysis)" [tasks."fmt:dotnet"] depends = ["dotnet-fmt"] description = "Format .NET sources" +[tasks."fix:dotnet"] +depends = ["roslynator-fix"] +description = "Apply .NET lint-fix passes (roslynator fix)" + [tasks.build-ws-dotnet-data1-module] description = "Build the dotnet-data1 C# WASM workflow module" dir = "services/ws-modules/dotnet-data1" diff --git a/.mise/config.toml b/.mise/config.toml index 998e7f8..564319e 100644 --- a/.mise/config.toml +++ b/.mise/config.toml @@ -358,6 +358,10 @@ a_rp_wasm_short = "08a1d6f" # ("C:/Users/runneradmin/AppData/Local"), and a mixed-slash path trips uutils-coreutils' Windows path # normalizer (`cp: The system cannot find the path specified. (os error 3)`). gh_http = "--http-url https://github.com --progress --ignore-existing" +# OpenObserve docker image for the openobserve/o2 dev-observability task. +o2_image = "openobserve/openobserve:v0.70.3" +# pywasm1's own module version, templated into the pkg/package.json its build task emits. +pywasm1_pkg_version = "0.1.0" rp_wasm_dir = '''{% if os() == "windows" -%} {%- set base = (env?.LOCALAPPDATA or "") | replace(from="\\", to="/") -%} {{- base -}}/mise/installs/http-rp-wasm/{{- vars.a_rp_wasm_short -}} @@ -634,16 +638,15 @@ description = "Cross-check Dockerfiles against mise [tools] pins + heredoc body- # 2. `ignore` parser, which preserves every line of the file as a separate entry -- conftest's dockerfile parser # flattens heredoc bodies out of the AST, so the body-first-line `set -euo pipefail` check lives in its # own namespace + parser. -# Each git-tracked Dockerfile* gets both passes. +# Each git-tracked Dockerfile* gets both passes, except that the `ignore` pass only visits Dockerfiles that +# actually contain heredocs: conftest 0.68.2's ignore parser panics with `runtime error: slice bounds out of +# range` on plain Dockerfiles (no heredocs), and the body-first-line rule has nothing to check there anyway. run = """ files=$(git ls-files '*Dockerfile' '*Dockerfile.*') # $files is a newline-separated path list; word-splitting is intentional (no spaces in paths). # shellcheck disable=SC2086 conftest test --combine --namespace dockerfile -p config/conftest/policy $files .mise # Filter to Dockerfiles that actually use heredocs before the `ignore` parser. -# conftest 0.68.2's ignore parser panics with -# `runtime error: slice bounds out of range` on plain Dockerfiles (no -# heredocs). The body-first-line rule has nothing to check there anyway. for f in $files; do if rg -q '^RUN[^\n]*<<' "$f"; then conftest test --parser ignore --namespace dockerfile_heredoc -p config/conftest/policy "$f" @@ -1110,7 +1113,7 @@ description = "Run both the ws-server and ws-wasm-agent using Chrome" [tasks.openobserve] alias = "o2" -run = "docker run --rm -it --name openobserve -p 5080:5080 --env-file config/o2.env openobserve/openobserve:v0.70.3" +run = "docker run --rm -it --name openobserve -p 5080:5080 --env-file config/o2.env {{ vars.o2_image }}" [tasks.demo] depends = ["openobserve", "ws-server"] @@ -1237,7 +1240,7 @@ coreutils cat > pkg/package.json <<'PKG_JSON' { "name": "et-ws-pywasm1", "type": "module", - "version": "0.1.0", + "version": "{{ vars.pywasm1_pkg_version }}", "main": "index.js", "files": [ "index.js", diff --git a/.mise/config.windows.toml b/.mise/config.windows.toml index a8384e3..7283398 100644 --- a/.mise/config.windows.toml +++ b/.mise/config.windows.toml @@ -212,18 +212,31 @@ _.path = ["{{ vars.m2_gnupg_bin }}", "{{ vars.maven_bin }}", "{{ vars.win_libcla # flips rustup's default host to gnullvm so both host and target link via llvm-mingw rather than the absent # MSVC link.exe (no Visual Studio Build Tools needed). The prereqs mise CAN'T supply (a recent mise + the # VC++ runtime) are in the README. +# +# The body opens with a sha256sum fail-fast diagnostic for the Nano CI lane. mise's conda backend (mise +# 2026.6.5, src/hash.rs:file_hash_sha256, files > 50 MB only) shells out to whichever `sha256sum` PATH finds +# to verify each conda tarball download. On the Nano docker build that lookup historically hit a +# sha256sum.exe that fails to load with `STATUS_ENTRYPOINT_NOT_FOUND` (0xC0000139 == decimal -1073741511, +# the msys2-runtime autoload-trap signature), surfacing as: +# mise ERROR Failed to install conda:m2-git@latest: command ["sha256sum", +# ""] exited with code -1073741511 +# Dockerfile.nanoserver stages a busybox-w32-backed `sha256sum.exe` ahead of the broken shim on PATH so the +# check should now pass; failing fast tells us which binary PATH picked when the next CI break hits. +# +# The pipx bootstrap's rustpython arm carries pinned-version baggage: RustPython's bundled ensurepip is pip +# 26.1.1, which crashes on import via `sys.addaudithook` (rustpython 0.5.0 doesn't implement -- +# RustPython#7956), so that arm fetches pip 26.0.1 (the last release before that audit hook landed, commit +# fb01b1830 / 2026-04-14), unzips the wheel, and puts it on RUSTPYTHONPATH so `rustpython -m pip` resolves +# to that copy. config.maint.toml's `rustpython-investigation` task documents the workaround at length, and +# also explains the PIPX_SHARED_LIBS pre-creation: pipx's first install creates a shared-libs venv via +# `python -m venv --clear` (no --without-pip), which triggers RustPython's bundled ensurepip audit-hook +# crash again. Pre-creating that venv with --without-pip and dropping the unpacked pip site-packages in lets +# pipx's `is_valid()` short-circuit past `create()`. The Nano Dockerfile then exports PATH + PIPX_* env vars +# so `mise install pipx:` can find this pipx and reuse the pre-created shared venv. depends = ["_setup_all"] description = "Preinstall: toolchain + pipx + flip rustup to the gnullvm host" run = """ -# sha256sum diagnostic for the Nano CI lane. -# mise's conda backend in 2026.6.5 shells out to a `sha256sum` it finds on PATH (via src/hash.rs:file_hash_sha256, -# only for files > 50 MB) to verify each conda tarball download. On the Nano docker build that lookup -# historically hit a sha256sum.exe that fails to load with `STATUS_ENTRYPOINT_NOT_FOUND` (0xC0000139 == -# decimal -1073741511, the msys2-runtime autoload-trap signature), surfacing as: -# mise ERROR Failed to install conda:m2-git@latest: command ["sha256sum", -# ""] exited with code -1073741511 -# Dockerfile.nanoserver stages a busybox-w32-backed `sha256sum.exe` ahead of the broken shim on PATH so this -# should now pass; the diagnostic is fail-fast so the next CI break here tells us which binary PATH picked. +# sha256sum fail-fast diagnostic for the Nano CI lane. sha256sum_path="$(command -v sha256sum 2>/dev/null || true)" echo "[sha256sum-check] command -v sha256sum: ${sha256sum_path:-}" if [ -z "$sha256sum_path" ]; then @@ -256,17 +269,9 @@ mise exec -- rustup set default-host x86_64-pc-windows-gnullvm # pipx backs the pipx:* tools but isn't a mise tool on Windows. # On a workstation, the system python's Scripts dir already has pipx on PATH; use that python. On the Nano # container there's no CPython (Dockerfile.nanoserver drops the `mise install python` step now that we can -# pipx-bootstrap via the `http:et-rp` rustpython mise installs as part of the global tool set). RustPython's -# bundled ensurepip is pip 26.1.1, which crashes on import via `sys.addaudithook` (rustpython 0.5.0 doesn't -# implement -- RustPython#7956), so the rustpython arm fetches pip 26.0.1 (the last release before that audit -# hook landed, see commit fb01b1830 / 2026-04-14), unzips the wheel, and puts it on RUSTPYTHONPATH so -# `rustpython -m pip` resolves to that copy. The matching workaround is documented at length in config.maint.toml's -# `rustpython-investigation` task. The same task also explains the PIPX_SHARED_LIBS pre-creation below: -# pipx's first install creates a shared-libs venv via `python -m venv --clear` (no --without-pip), which -# triggers RustPython's bundled ensurepip + pip 26.1.1 audit-hook crash again. Pre-creating that venv with -# --without-pip and dropping the unpacked pip-26.0.1 site-packages in lets pipx's `is_valid()` short-circuit -# past `create()`. The Nano Dockerfile then exports PATH + PIPX_* env vars so `mise install pipx:` can -# find this pipx and reuse the pre-created shared venv. +# pipx-bootstrap via the `http:et-rp` rustpython mise installs as part of the global tool set), so the +# rustpython arm fetches a pinned pre-audit-hook pip wheel, unzips it, and puts it on RUSTPYTHONPATH so +# `rustpython -m pip` resolves to that copy. if command -v python >/dev/null 2>&1; then python -m pip install pipx elif [ "${ENABLE_RUSTPYTHON_PIPX_BOOTSTRAP:-}" = "1" ]; then diff --git a/config/conftest/policy/mise/mise.rego b/config/conftest/policy/mise/mise.rego index 45bd919..eeecc8d 100644 --- a/config/conftest/policy/mise/mise.rego +++ b/config/conftest/policy/mise/mise.rego @@ -50,24 +50,44 @@ deny contains msg if { # `compgen` is a bash builtin that busybox-w32 ash (Nano Server's shell) does not provide. # A task using it fails on Nano with the literal error `: not found` (verified in the build-rp-native task). -# Skip lines whose first non-whitespace char is `#` so explanatory comments (like this one's siblings) don't -# false-positive. POSIX-portable alternatives include `[ -e "$prefix/bin/foo" ] || [ -e "$prefix/bin/foo.exe" ]` for an +# The whole run body is checked, comments included -- compgen-narrating prose belongs in a TOML comment on +# the task, not inside the body. config.maint.toml is exempt: maintainer-only, never runs on Nano. +# POSIX-portable alternatives include `[ -e "$prefix/bin/foo" ] || [ -e "$prefix/bin/foo.exe" ]` for an # extension-agnostic existence check, and `for f in "$prefix/bin/foo"*` to walk a literal glob (no-match leaves the # literal as the loop var). deny contains msg if { some file in input is_mise(file) + replace(file.path, "\\", "/") != ".mise/config.maint.toml" some name, task in file.contents.tasks is_string(task.run) - some line in split(task.run, "\n") - not startswith(trim_space(line), "#") - regex.match(`\bcompgen\b`, line) + regex.match(`\bcompgen\b`, task.run) msg := sprintf( "%s: task %q uses `compgen`, which is bash-only -- busybox ash (Nano) errors `compgen: not found`", [file.path, name], ) } +# A task `run` must not embed version-like text (an `X.Y.Z` literal), comments included. +# A version baked into a run body silently goes stale when the pin it mirrors is bumped -- version_drift +# only cross-checks [vars]/[env] values, never run strings. Keep the versioned fragment in a [vars] entry +# beside the [tools] pin it tracks and template it into the run with `{{ vars. }}`; version-narrating +# prose belongs in a TOML comment on the task, not inside the body. +# config.maint.toml is exempt: its maintainer-only investigation/publish tasks narrate and probe specific +# upstream versions by design. +deny contains msg if { + some file in input + is_mise(file) + replace(file.path, "\\", "/") != ".mise/config.maint.toml" + some name, task in file.contents.tasks + is_string(task.run) + some m in regex.find_all_string_submatch_n(`[0-9]+\.[0-9]+\.[0-9]+`, task.run, -1) + msg := sprintf( + "%s: task %q run embeds version-like text %q -- move it into a [vars] entry beside the pin it tracks", + [file.path, name, m[0]], + ) +} + # `cargo:` tools may build from source; prefer a prebuilt backend. # Allowed only when either (a) allowlisted by name below -- cargo-binstall fetches a prebuilt there (e.g. a # cargo-quickinstall release, routed via an `install_env` CARGO_BUILD_TARGET override), or the tool has no prebuilt