From ec240d7703d5bf12b053b24a951e471c241c1aa1 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 18:36:00 +0000 Subject: [PATCH 1/4] ci(test): give tests/migrate its own race shard Under -race the SysML v1 migrator's suite grew past 24 minutes on the pull-request runners and past the 30-minute package timeout on CircleCI's develop builds, failing every merge since the rest shard carried it. scripts/race-shard.sh migrate names it; both CI configurations run six race shards and five coverage shards. Co-Authored-By: jason.han --- .circleci/config.yml | 12 ++++++------ .github/workflows/pr.yml | 7 ++++--- AGENTS.md | 2 +- Makefile | 8 ++++---- changes/unreleased/race-shard-migrate.changed.md | 1 + scripts/race-shard.sh | 16 +++++++++++----- 6 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 changes/unreleased/race-shard-migrate.changed.md diff --git a/.circleci/config.yml b/.circleci/config.yml index 1c2d1b6b7e..4f1f1c0aee 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -420,11 +420,11 @@ jobs: client/java/opensysml-client/src/main/java/org/openmbee/opensysml/proto # The race-enabled suite. It is the longest single step in the pipeline, so - # it gets the larger class and four executors, one named package shard each. + # it gets the larger class and five executors, one named package shard each. go-race-test: executor: go-executor resource_class: large - parallelism: 4 + parallelism: 5 steps: - checkout - go/load-cache @@ -444,7 +444,7 @@ jobs: name: Run Go race tests command: | set -o pipefail - shards=(runtime model export rest) + shards=(runtime model export rest migrate) if [ "$CIRCLE_NODE_TOTAL" != "${#shards[@]}" ]; then echo "error: parallelism is $CIRCLE_NODE_TOTAL, but scripts/race-shard.sh has ${#shards[@]} shards" >&2 exit 1 @@ -483,11 +483,11 @@ jobs: # A second, race-free pass, because the profile the scan reads is written # with -coverpkg, which is too slow to instrument under -race. Serially the # instrumented suite overran the 60-minute job limit even on large, so it runs - # as the race test's four package shards, and go-coverage-merge joins them. + # as the race test's five package shards, and go-coverage-merge joins them. go-coverage: executor: go-executor resource_class: large - parallelism: 4 + parallelism: 5 steps: - checkout - go/load-cache @@ -501,7 +501,7 @@ jobs: - run: name: Write this shard's coverage profile command: | - shards=(runtime model export rest) + shards=(runtime model export rest migrate) if [ "$CIRCLE_NODE_TOTAL" != "${#shards[@]}" ]; then echo "error: parallelism is $CIRCLE_NODE_TOTAL, but scripts/race-shard.sh has ${#shards[@]} shards" >&2 exit 1 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 5672191a35..ce674d6061 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -78,11 +78,12 @@ jobs: needs: changes if: needs.changes.outputs.go == 'true' # The shards split the suite's slowest packages across runners so each gets its own job cap (see scripts/race-shard.sh). - # internal/exec/runtime alone outruns a job under -race, so it is two shards split by test name (see make test-shard). + # internal/exec/runtime alone outruns a job under -race, so it is two shards split by test name (see make test-shard); + # tests/migrate is the next-longest package, so it is a shard of its own rather than the long pole of rest. strategy: fail-fast: false matrix: - shard: [runtime, runtime-corpus, model, export, rest] + shard: [runtime, runtime-corpus, model, export, rest, migrate] runs-on: ubuntu-latest permissions: contents: read @@ -291,7 +292,7 @@ jobs: set -euo pipefail profiles=(shards/coverage-*/coverage.txt) # One per race-tests shard. - if [ "${#profiles[@]}" -ne 5 ]; then echo "error: expected 5 shard profiles, found ${#profiles[@]}" >&2; exit 1; fi + if [ "${#profiles[@]}" -ne 6 ]; then echo "error: expected 6 shard profiles, found ${#profiles[@]}" >&2; exit 1; fi for f in "${profiles[@]}"; do [ "$(head -n 1 "$f")" = "mode: atomic" ] || { echo "error: $f is not an atomic-mode profile" >&2; exit 1; } done diff --git a/AGENTS.md b/AGENTS.md index ba4f8c48be..6d6d88c14c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,7 @@ make build # build bin/sysml and bin/sysml-lsp (with version ldflags) make build-sysml # REPL binary only make build-lsp # LSP binary only make test # full suite: go test -race -coverprofile ... ./... -make test-shard SHARD=runtime # one CI shard of the race suite (runtime|model|export|rest) +make test-shard SHARD=runtime # one CI shard of the race suite (runtime|runtime-corpus|model|export|rest|migrate) make test-short # faster, no race detector make clean # remove build artifacts ``` diff --git a/Makefile b/Makefile index 59ca917ab3..a57b752d8b 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ TOOLS_DIR := tools COVERAGE_PACKAGES ?= ./... COVERAGE_TOOLS ?= ./... COVERAGE_PROFILE ?= coverage.txt -COVERAGE_SHARDS := runtime model export rest +COVERAGE_SHARDS := runtime model export rest migrate # The commands whose manual pages are generated and shipped, in section 1. COMMANDS := sysml sysml-lsp sysml-grpc sysml-jupyter-kernel @@ -288,7 +288,7 @@ RACE_SHARD_SKIP := ^(TestTrainingExamplesSemanticErrors|TestCorpusGatesCacheStat RACE_RUNTIME_CORPUS := ^Test(ExploreWithIsExploreOverTheConformanceCorpus|CheckAgreesWithExploreOverTheConformanceCorpus|CheckWitnessesReplayOverTheConformanceCorpus|ExecutionConformance|ExecutionConformanceUnderPolicies)$$ RACE_SHARD_TOOLS_SKIP := ^(TestSuiteRead|TestSuiteClassification|TestEmitSuite|TestSuiteClassificationReasons|TestSuiteLibraryCallsAreClassified|TestSuiteReadsControlAndObjectFlow|TestSuiteReadsClassifiers|TestSuiteReadsExceptionHandlers)$$ -test-shard: ## Run one CI shard of the race suite (SHARD=runtime|runtime-corpus|model|export|rest) +test-shard: ## Run one CI shard of the race suite (SHARD=runtime|runtime-corpus|model|export|rest|migrate) @echo "Running Go race tests, shard $(SHARD)..." @# 55m per package; its job's ceiling is 60. Whole, internal/exec/runtime took 31-57 minutes @# under -race on the CI runners, so it runs as two shards of about half that each. @@ -324,14 +324,14 @@ coverage: ## Write the coverage profile the SonarCloud scan reads python3 scripts/dedupe-coverage.py $(COVERAGE_PROFILE) @go tool cover -func=$(COVERAGE_PROFILE) | tail -n 1 -coverage-shard: ## Write one CI shard's coverage profile, coverage-$(SHARD).txt (SHARD=runtime|model|export|rest) +coverage-shard: ## Write one CI shard's coverage profile, coverage-$(SHARD).txt (SHARD=runtime|model|export|rest|migrate) @# The race shards' packages; tests/wasm and the tools, which no race shard runs, go with runtime. pkgs=$$(scripts/race-shard.sh $(SHARD)) && \ $(MAKE) --no-print-directory coverage COVERAGE_PROFILE=coverage-$(SHARD).txt \ COVERAGE_PACKAGES="$$(echo $$pkgs) $(if $(filter runtime,$(SHARD)),./tests/wasm)" \ COVERAGE_TOOLS="$(if $(filter runtime,$(SHARD)),./...)" -coverage-merge: ## Merge the shard profiles coverage-runtime/model/export/rest.txt into coverage.txt +coverage-merge: ## Merge the shard profiles coverage-runtime/model/export/rest/migrate.txt into coverage.txt { echo "mode: atomic"; for shard in $(COVERAGE_SHARDS); do tail -n +2 coverage-$$shard.txt || exit 1; done; } > coverage.txt python3 scripts/dedupe-coverage.py coverage.txt @go tool cover -func=coverage.txt | tail -n 1 diff --git a/changes/unreleased/race-shard-migrate.changed.md b/changes/unreleased/race-shard-migrate.changed.md new file mode 100644 index 0000000000..69b8f8bc85 --- /dev/null +++ b/changes/unreleased/race-shard-migrate.changed.md @@ -0,0 +1 @@ +- **`tests/migrate` is a race shard of its own.** The SysML v1 migrator's suite ran as part of the `rest` shard, where under `-race` it grew past 24 minutes on the pull-request runners and past the 30-minute package timeout on CircleCI's `develop` builds, failing every merge. `scripts/race-shard.sh migrate` names it, `make test-shard SHARD=migrate` and `make coverage-shard SHARD=migrate` run it, and both CI configurations run six race shards and five coverage shards. diff --git a/scripts/race-shard.sh b/scripts/race-shard.sh index 390424faf8..61c2d3e2a5 100755 --- a/scripts/race-shard.sh +++ b/scripts/race-shard.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash # Prints the root-module package paths assigned to one race-test shard. -# Usage: scripts/race-shard.sh runtime|runtime-corpus|model|export|rest +# Usage: scripts/race-shard.sh runtime|runtime-corpus|model|export|rest|migrate # runtime and runtime-corpus are one package, split by test name in make test-shard. set -euo pipefail cd "$(dirname "$0")/.." if [[ $# -ne 1 ]]; then - echo "usage: scripts/race-shard.sh runtime|runtime-corpus|model|export|rest" >&2 + echo "usage: scripts/race-shard.sh runtime|runtime-corpus|model|export|rest|migrate" >&2 exit 2 fi @@ -20,14 +20,17 @@ smt="$module/internal/exec/smt" export="$module/tests/export" model="$module/tests/model" passes="$module/internal/check/passes" +# tests/migrate runs the SysML v1 migrator over whole models and is the next-longest +# package under -race after the runtime, so it is a shard of its own. +migrate="$module/tests/migrate" # tests/wasm runs in the WebAssembly gate job, not a race shard. wasm="$module/tests/wasm" -named_packages=("$runtime" "$workspace_model" "$corpus" "$smt" "$export" "$model" "$passes" "$wasm") +named_packages=("$runtime" "$workspace_model" "$corpus" "$smt" "$export" "$model" "$passes" "$wasm" "$migrate") case "$shard" in - runtime|runtime-corpus|model|export|rest) ;; + runtime|runtime-corpus|model|export|rest|migrate) ;; *) - echo "usage: scripts/race-shard.sh runtime|runtime-corpus|model|export|rest" >&2 + echo "usage: scripts/race-shard.sh runtime|runtime-corpus|model|export|rest|migrate" >&2 exit 2 ;; esac @@ -50,6 +53,9 @@ case "$shard" in export) printf '%s\n' "$export" "$model" "$passes" ;; + migrate) + printf '%s\n' "$migrate" + ;; rest) while IFS= read -r package; do case " ${named_packages[*]} " in From b16be826d8689e40a8b8eca8b536f3ea171da5a2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:40:17 +0000 Subject: [PATCH 2/4] chore(release): give tests/migrate its own race shard in 0.10.0 Under -race the SysML v1 migrator suite outgrew the 30-minute package timeout on the CircleCI rest node, so the release workflow could not pass; it now runs as a shard of its own. Co-Authored-By: jason.han --- CHANGELOG.md | 1 + changes/unreleased/race-shard-migrate.changed.md | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changes/unreleased/race-shard-migrate.changed.md diff --git a/CHANGELOG.md b/CHANGELOG.md index fcf9e37a6e..319b02354b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -444,6 +444,7 @@ release is described in [docs/project/releasing.md](docs/project/releasing.md). - The non-normative `OpenSysML Libraries/` extension libraries are now maintained upstream at [Open-MBEE/OpenSysML-Extensions-Library](https://github.com/Open-MBEE/OpenSysML-Extensions-Library); the bundled copy is pinned by `scripts/extension-libraries-pin.sh`, refreshed by `scripts/sync-extension-libraries.sh` (`make extension-libraries-sync`), checked for drift on every pull request (`make extension-libraries-check`), and carries `engine-contract.json` recording the qualified names the engine binds. - **Declare the bundled OpenSysML extension libraries as non-standard.** `DiagramLayout`, `IdentityMetadata`, `MigrationMetadata` and `SysMLValidation` were written `standard library package`, which both asserted membership of the normative Kernel/SysML libraries and drew a pilot warning (`User library packages should not be marked as standard`); they are project extensions and now read `library package`. RDF export no longer marks them `sysml:isStandard true`. +- **`tests/migrate` is a race shard of its own.** The SysML v1 migrator's suite ran as part of the `rest` shard, where under `-race` it grew past 24 minutes on the pull-request runners and past the 30-minute package timeout on CircleCI's `develop` builds, failing every merge. `scripts/race-shard.sh migrate` names it, `make test-shard SHARD=migrate` and `make coverage-shard SHARD=migrate` run it, and both CI configurations run six race shards and five coverage shards. ### Deprecated diff --git a/changes/unreleased/race-shard-migrate.changed.md b/changes/unreleased/race-shard-migrate.changed.md deleted file mode 100644 index 69b8f8bc85..0000000000 --- a/changes/unreleased/race-shard-migrate.changed.md +++ /dev/null @@ -1 +0,0 @@ -- **`tests/migrate` is a race shard of its own.** The SysML v1 migrator's suite ran as part of the `rest` shard, where under `-race` it grew past 24 minutes on the pull-request runners and past the 30-minute package timeout on CircleCI's `develop` builds, failing every merge. `scripts/race-shard.sh migrate` names it, `make test-shard SHARD=migrate` and `make coverage-shard SHARD=migrate` run it, and both CI configurations run six race shards and five coverage shards. From f76b3f037a79648b564b907909b4956d5388d1c9 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:41:30 +0000 Subject: [PATCH 3/4] ci(test): keep the race-shard comment to two lines Co-Authored-By: jason.han --- .github/workflows/pr.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 7246115bee..4fa12dfeb1 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -83,9 +83,8 @@ jobs: name: Go race tests (${{ matrix.shard }}) needs: changes if: needs.changes.outputs.go == 'true' - # The shards split the suite's slowest packages across runners so each gets its own job cap (see scripts/race-shard.sh). - # internal/exec/runtime alone outruns a job under -race, so it is two shards split by test name (see make test-shard); - # tests/migrate is the next-longest package, so it is a shard of its own rather than the long pole of rest. + # The shards split the suite's slowest packages across runners so each gets its own job cap (see scripts/race-shard.sh); + # internal/exec/runtime is two shards split by test name (see make test-shard) and tests/migrate, the next-longest, is one of its own. strategy: fail-fast: false matrix: From 8490203875b3e2c64ebe16f14549a53fd263a033 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:41:51 +0000 Subject: [PATCH 4/4] ci(circleci): give the migrate race shard a 55-minute package timeout The package already ran 1370-1800+ seconds under -race on the rest node, so the 30-minute default leaves no margin even on a node of its own. Co-Authored-By: jason.han --- .circleci/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1a0037aa2f..f97654c2d0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -459,7 +459,10 @@ jobs: go test -v -race -pgo=off -timeout 30m ./tests/wasm go test -C tools -v -race -pgo=off -timeout 30m ./... else - go test -v -race -pgo=off -timeout 30m $pkgs + # tests/migrate alone runs 23-32 minutes under -race on this class, so its + # shard gets the Makefile's per-package timeout (see make test-shard). + timeout=30m; [ "$shard" = migrate ] && timeout=55m + go test -v -race -pgo=off -timeout "$timeout" $pkgs fi no_output_timeout: 30m environment: