From ebe109ad1004d35646ec41bc8a015d287dd26b50 Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sat, 31 Oct 2026 13:23:41 +0000 Subject: [PATCH 1/2] Adopt the Rust build standard and the dev-build footprint Set incremental = false, debug = 0 and embed-metadata = false for the footprint rule, and -Zthreads=8 in every rustflags source with mold in the Linux source for the build standard. Restate both flags where RUSTFLAGS is assigned (the gate recipes, the build job's setup-rust, the Dependabot test step and the mutation workflow), install mold in CI, and record why Cranelift is not the development backend. A contract test holds the configuration, the recipes and the CI provisioning. --- .cargo/config.toml | 34 +++- .github/workflows/ci.yml | 8 +- .github/workflows/mutation-testing.yml | 6 + Makefile | 22 ++- docs/developers-guide.md | 74 +++++++ .../workflow_contracts/build_standard_test.py | 185 ++++++++++++++++++ .../mutation_testing_test.py | 6 + 7 files changed, 327 insertions(+), 8 deletions(-) create mode 100644 tests/workflow_contracts/build_standard_test.py diff --git a/.cargo/config.toml b/.cargo/config.toml index a2c102976..ce105750e 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,8 +1,36 @@ +# Repository-wide Cargo defaults: the dev-build footprint (Concordat +# `rust-dev-build-footprint`) and the Rust build standard (Concordat +# `rust-build-defaults`). Cargo auto-discovers this file, so a bare +# `cargo build` gets them. See "Development build footprint" and "Rust build +# standard" in docs/developers-guide.md. +# +# The parallel `rustc` frontend and, on Linux, the `mold` linker are the +# defaults for every development, test, lint and typecheck build. Cargo +# selects a single `rustflags` source rather than merging them: a matching +# `[target.*]` table replaces `[build] rustflags` outright, and an assigned +# `RUSTFLAGS` replaces both. Every flag is therefore repeated in each source, +# and the Makefile and CI compose them into the `RUSTFLAGS` they assign. +# Coverage assigns `RUSTFLAGS` without them on purpose, so a measurement stays +# on the platform linker and the single-threaded frontend. Cranelift is not the +# development backend because the suite does not pass under it; the exception +# is recorded in the developers' guide. +[unstable] +embed-metadata = false + [profile.dev] opt-level = 1 # default is 0; 1 shrinks a bit without killing compile times -debug = 1 # 2 is “full debuginfo”; 1 is usually enough +debug = 0 # no debug info keeps the target tree small; see the developers' guide debug-assertions = true overflow-checks = true -incremental = true -panic = "unwind" # keep nice backtraces in normal dev builds +incremental = false # incremental state is the largest part of a dev target tree +panic = "unwind" # keep unwinding in normal dev builds strip = "none" # don't strip by default for dev + +[build] +rustflags = ["-Zthreads=8"] + +# `mold` ships for Linux only, so the linker flag lives in a Linux-only table. +# macOS and Windows fall through to `[build] rustflags` above and keep their +# platform linker. +[target.'cfg(target_os = "linux")'] +rustflags = ["-Zthreads=8", "-Clink-arg=-fuse-ld=mold"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 650a8d5b5..8d764bab6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -277,6 +277,12 @@ jobs: # server with a 60 s startup timeout, and falls back to an uncached # build, with a `sccache-fallback` warning, if the server will not start. expect-cache: any + # The build standard (.cargo/config.toml): the parallel frontend and + # the `mold` linker. The exported RUSTFLAGS displaces the Cargo + # configuration's sources, so it restates both after `-D warnings`. + # The Whitaker lint assigns its own value and is unaffected. + install-mold: 'true' + rustflags: -D warnings -Zthreads=8 -Clink-arg=-fuse-ld=mold - name: Install uv id: setup-uv @@ -542,7 +548,7 @@ jobs: run: | # Clean stale pg-embed directories that may conflict with new runs. find target/ -maxdepth 1 -type d -name 'pg-embed-*' -exec rm -rf {} + 2>/dev/null || true - RUSTFLAGS="-D warnings" cargo nextest run --locked \ + RUSTFLAGS="-D warnings -Zthreads=8 -Clink-arg=-fuse-ld=mold" cargo nextest run --locked \ --manifest-path backend/Cargo.toml \ --all-targets --all-features \ -E "$NEXTEST_EXCLUSION" diff --git a/.github/workflows/mutation-testing.yml b/.github/workflows/mutation-testing.yml index 82eb4fbe1..dc632c80e 100644 --- a/.github/workflows/mutation-testing.yml +++ b/.github/workflows/mutation-testing.yml @@ -50,7 +50,13 @@ jobs: # it without setting it (issue #464), so a run that omits it lets # postgresql_embedded generate a different password per process and the # shared cluster is torn down and re-authenticated repeatedly. + # `.cargo/config.toml` links with `mold` on Linux (the build standard). + # `setup-rust` exports `RUSTFLAGS=-D warnings`, which displaces the + # configuration's `rustflags`, so the first command restores the complete + # value for cargo-mutants. + install-mold: 'true' setup-commands: | + echo "RUSTFLAGS=-D warnings -Zthreads=8 -Clink-arg=-fuse-ld=mold" >> "$GITHUB_ENV" echo "POSTGRESQL_VERSION==16.10.0" >> "$GITHUB_ENV" echo "POSTGRESQL_RELEASES_URL=https://github.com/theseus-rs/postgresql-binaries" >> "$GITHUB_ENV" echo "PG_PASSWORD=wildside_embedded_test" >> "$GITHUB_ENV" diff --git a/Makefile b/Makefile index ddd915960..cd7b4a9e8 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,21 @@ endef RUSTFLAGS_STRICT := -D warnings RUST_FLAGS ?= $(RUSTFLAGS_STRICT) +# The build standard (Concordat `rust-build-defaults`): the parallel `rustc` +# frontend and, on Linux, the `mold` linker. An assigned `RUSTFLAGS` replaces +# every `rustflags` source in `.cargo/config.toml`, so each gate recipe that +# assigns it restates both through `GATE_RUST_FLAGS_ENV`, keeping any inherited +# value (CI's setup-rust exports one). `RUST_FLAGS_ENV` stays free of them for +# Whitaker, which builds its lint driver on a different toolchain that must not +# inherit development flags. +BUILD_HOST_OS := $(shell uname -s) +STANDARD_RUSTFLAGS := -Zthreads=8$(if $(filter Linux,$(BUILD_HOST_OS)), -Clink-arg=-fuse-ld=mold) +# The linker flag is chosen from the host, so a gate that compiles for another +# target would get the wrong one. Refuse that rather than guess; run a +# cross-compiling build with `cargo` directly. +STANDARD_TARGET_GUARD = $(if $(strip $(CARGO_BUILD_TARGET)),$(error the build standard picks `mold` from the host; unset CARGO_BUILD_TARGET, or run cargo directly)) RUST_FLAGS_ENV := RUSTFLAGS="$(RUST_FLAGS)" +GATE_RUST_FLAGS_ENV = $(STANDARD_TARGET_GUARD)RUSTFLAGS="$${RUSTFLAGS:+$$RUSTFLAGS }$(RUST_FLAGS) $(STANDARD_RUSTFLAGS)" RUSTDOC_FLAGS ?= --cfg docsrs -D warnings ASYNCAPI_CLI_VERSION ?= 3.4.2 @@ -279,7 +293,7 @@ lint-python: $(PYLINT) $(PYLINT_TARGETS) lint-architecture: - $(RUST_FLAGS_ENV) cargo run -p architecture-lint --quiet + $(GATE_RUST_FLAGS_ENV) cargo run -p architecture-lint --quiet # Lint AsyncAPI spec if present. Split to keep `lint` target concise per checkmake rules. lint-asyncapi: @@ -356,10 +370,10 @@ docs-check: deps test: test-rust test-frontend test-workflow-contracts test-scripts test-lint-actions test-rust: workspace-sync prepare-pg-worker - PG_EMBEDDED_WORKER=$(PG_WORKER_PATH) NEXTEST_TEST_THREADS=$(NEXTEST_TEST_THREADS) $(RUST_FLAGS_ENV) cargo nextest run --workspace --all-targets --all-features --no-fail-fast \ + PG_EMBEDDED_WORKER=$(PG_WORKER_PATH) NEXTEST_TEST_THREADS=$(NEXTEST_TEST_THREADS) $(GATE_RUST_FLAGS_ENV) cargo nextest run --workspace --all-targets --all-features --no-fail-fast \ -E 'not (binary(declare_test_support_compile_fail) | binary(compile_fail_tests) | binary(sha2_digest_formatting_compile_fail))' - $(RUST_FLAGS_ENV) cargo test -p backend --test declare_test_support_compile_fail --all-features - $(RUST_FLAGS_ENV) cargo test -p backend --test sha2_digest_formatting_compile_fail --all-features + $(GATE_RUST_FLAGS_ENV) cargo test -p backend --test declare_test_support_compile_fail --all-features + $(GATE_RUST_FLAGS_ENV) cargo test -p backend --test sha2_digest_formatting_compile_fail --all-features test-frontend: deps typecheck pnpm run test diff --git a/docs/developers-guide.md b/docs/developers-guide.md index f4d271a34..f38c488e1 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -504,6 +504,80 @@ Four consequences follow from the table. where its `coverage` job is skipped because `coverage-main.yml` already covers that branch. +## Rust build standard and development build footprint + +Two Concordat rules set the estate's Rust build defaults, and +`.cargo/config.toml` carries both, so a bare `cargo build` gets them. + +### Development build footprint + +The `rust-dev-build-footprint` rule keeps default development builds small. +`[profile.dev]` sets `incremental = false` and `debug = 0`, so builds keep no +incremental compilation state and no debug information. The trade-off is that a +debugger sees no source-level debug information or line tables in a default +development build and every rebuild recompiles whole crates. To debug, run with +`CARGO_PROFILE_DEV_DEBUG=true`; to rebuild incrementally, set +`CARGO_INCREMENTAL=1`. `[unstable] embed-metadata = false` is the native Cargo +key for dated nightlies from 2026-07-31, so Cargo coordinates metadata output +itself. No raw `rustc` metadata flags are used. + +### Rust build standard + +The `rust-build-defaults` rule sets the compiler and linker defaults: + +- Every `rustflags` source enables the parallel `rustc` frontend with + `-Zthreads=8`. +- The `cfg(target_os = "linux")` source also links with `mold`. macOS and + Windows keep their platform linker. CI installs `mold` through `setup-rust`'s + `install-mold` input, and through the mutation workflow's input of the same + name. + +Cargo applies a single `rustflags` source rather than merging them, and an +assigned `RUSTFLAGS` replaces every source. So each source repeats the frontend +flag, and the places that assign `RUSTFLAGS` restate both flags: + +- The Makefile's `lint-architecture` and `test-rust` recipes go through + `GATE_RUST_FLAGS_ENV`, which composes `-D warnings`, any inherited + `RUSTFLAGS` and the standard flags. It refuses `CARGO_BUILD_TARGET`, because + the linker flag is chosen from the host; run `cargo` directly to + cross-compile. +- The `build` job's `setup-rust` step exports the same value through its + `rustflags` input, and the Dependabot `Rust tests` step restates it. The + mutation workflow's `setup-commands` writes it to `GITHUB_ENV`, because + `setup-rust` exports `-D warnings` alone there. +- The Whitaker recipes keep `RUST_FLAGS_ENV` (`-D warnings` only), because + Whitaker builds its lint driver on a different toolchain that must not + inherit these flags. The coverage jobs also assign `-D warnings` alone, so a + measurement stays on the platform linker. + +The first run after this change misses the compiler cache for every crate, +because the flags are part of the cache key, so it takes as long as a cold +build (about 87 minutes on `ubicloud-standard-4`, within the 120-minute +ceiling). `tests/workflow_contracts/build_standard_test.py` holds the +configuration, the footprint settings, the Makefile composition, the target +refusal and the CI provisioning. + +### Cranelift is not the development backend + +The Cranelift codegen backend is not part of this repository's standard, +because the repository's own suite does not pass under it. Measured on +2026-10-31 on the pinned `nightly-2026-10-28`, running `make test-rust` with +`CARGO_UNSTABLE_CODEGEN_BACKEND=true`, +`CARGO_PROFILE_DEV_CODEGEN_BACKEND=cranelift` and +`CARGO_PROFILE_TEST_CODEGEN_BACKEND=cranelift` and nothing else changed, a +Cranelift-compiled panic does not find its unwind handler. The run was stopped +at 1,218 of 1,849 tests: 52 tests aborted with `SIGABRT` (for example +`domain::ports::cache_key::tests::coordinate_bound_is_inclusive`, +`domain::offline_bundle_service::tests::upsert_persists_bundle_without_idempotency_key` +and the `inbound::http::offline` and `inbound::http::routes` rejection tests) +and `domain::user::tests::new_panics_when_invalid_id` failed. The same suite +passes on the LLVM backend. The pin is now `nightly-2026-10-31`; the +measurement above was taken on `nightly-2026-10-28` and has not been repeated +on the newer pin, and Dependabot moves the pin daily. Re-measure on the next +deliberate toolchain bump or by 2026-11-30, whichever is first, and adopt the +backend, through `[unstable] codegen-backend` and +`[profile.dev] codegen-backend = "cranelift"`, when the suite passes under it. + ## Local Kubernetes preview Use the repository-local Kubernetes preview when validating the backend image diff --git a/tests/workflow_contracts/build_standard_test.py b/tests/workflow_contracts/build_standard_test.py new file mode 100644 index 000000000..a2a0f99ee --- /dev/null +++ b/tests/workflow_contracts/build_standard_test.py @@ -0,0 +1,185 @@ +"""Contract tests for the Rust build standard and the dev-build footprint. + +The Concordat ``rust-build-defaults`` rule needs the parallel ``rustc`` frontend +in every ``rustflags`` source, ``mold`` in the Linux source only, the gate +recipes that assign ``RUSTFLAGS`` to restate both, and a runner that has the +linker. The ``rust-dev-build-footprint`` rule needs the development profile to +disable incremental compilation and debug information, and the pinned nightly +to take Cargo's native ``embed-metadata = false``. Cranelift is not adopted +(see "Cranelift is not the development backend" in +``docs/developers-guide.md``), so the configuration must stay free of it. + +Run via ``make test-workflow-contracts``. +""" + +from __future__ import annotations + +import os +import re +import subprocess # ruff: ignore[suspicious-subprocess-import] - the contract reads what `make -n` prints. +import tomllib +import typing as typ +from pathlib import Path +from shutil import which + +import pytest +import workflow_inventory as inv + +ROOT = Path(__file__).resolve().parents[2] +THREADS = "-Zthreads=8" +LINKER_FLAG = "-Clink-arg=-fuse-ld=mold" +LINUX_TABLE = 'cfg(target_os = "linux")' +GATE_TARGETS = ("lint-architecture", "test-rust") +STANDARD_FLAGS = f"-D warnings {THREADS} {LINKER_FLAG}" + + +def _config() -> dict[str, typ.Any]: + return tomllib.loads((ROOT / ".cargo" / "config.toml").read_text("utf-8")) + + +def test_every_rustflags_source_carries_the_parallel_frontend() -> None: + """``[build]`` and the Linux target table both enable ``-Zthreads=8``.""" + config = _config() + assert THREADS in config["build"]["rustflags"] + assert THREADS in config["target"][LINUX_TABLE]["rustflags"] + + +def test_the_linker_is_configured_for_linux_only() -> None: + """The linker flag is in the Linux table and in no other source.""" + config = _config() + assert LINKER_FLAG in config["target"][LINUX_TABLE]["rustflags"] + assert LINKER_FLAG not in config["build"]["rustflags"] + others = [name for name in config["target"] if name != LINUX_TABLE] + assert not others, f"unexpected target tables {others}" + + +def test_cranelift_stays_out_of_the_configuration() -> None: + """The recorded exception holds while the configuration omits the backend.""" + config = _config() + assert "codegen-backend" not in config.get("unstable", {}) + assert "codegen-backend" not in config.get("profile", {}).get("dev", {}) + + +def test_the_development_profile_keeps_the_footprint_settings() -> None: + """The dev profile disables incremental state and debug information.""" + dev = _config()["profile"]["dev"] + assert dev.get("incremental") is False, "profile.dev must set incremental = false" + debug = dev.get("debug") + # Python equality would also accept ``0.0``, which Cargo rejects, so test the + # type: the exact integer ``0`` or the boolean ``false``. + is_zero_integer = ( + isinstance(debug, int) and not isinstance(debug, bool) and debug == 0 + ) + assert debug is False or is_zero_integer, "profile.dev must set debug = 0" + + +def test_the_configuration_disables_metadata_embedding_through_cargo() -> None: + """The native Cargo key is set, the old key and raw rustc flags are absent.""" + config = _config() + unstable = config.get("unstable", {}) + assert unstable.get("embed-metadata") is False + assert "no-embed-metadata" not in unstable + flags = [*config["build"]["rustflags"], *config["target"][LINUX_TABLE]["rustflags"]] + assert not any("embed-metadata" in flag for flag in flags) + + +def _make_dry_run( + target: str, host_os: str, env: dict[str, str] | None = None +) -> subprocess.CompletedProcess[str]: + """Run ``make -n`` for ``target`` as if on ``host_os``.""" + environment = {k: v for k, v in os.environ.items() if k != "CARGO_BUILD_TARGET"} + environment.update(env or {}) + make = which("make") + assert make is not None, "make must be installed to read the recipes" + return subprocess.run( # ruff: ignore[subprocess-without-shell-equals-true] - a resolved `make -n` over this repository. + [make, "-n", "-s", f"BUILD_HOST_OS={host_os}", target], + cwd=ROOT, + capture_output=True, + text=True, + check=False, + env=environment, + ) + + +def _recipe_commands(target: str, host_os: str) -> str: + """Return the commands ``make -n`` prints for ``target`` on ``host_os``.""" + completed = _make_dry_run(target, host_os) + assert completed.returncode == 0, completed.stderr + return completed.stdout + + +@pytest.mark.parametrize("target", GATE_TARGETS) +def test_gate_recipes_restate_the_standard_flags_on_linux(target: str) -> None: + """A recipe that assigns ``RUSTFLAGS`` restates the frontend and the linker.""" + commands = _recipe_commands(target, "Linux") + assert "RUSTFLAGS=" in commands + assert STANDARD_FLAGS in commands + + +@pytest.mark.parametrize("target", GATE_TARGETS) +def test_gate_recipes_leave_the_linker_off_other_platforms(target: str) -> None: + """Off Linux the recipe keeps the frontend flag and drops the linker.""" + commands = _recipe_commands(target, "Darwin") + assert f"-D warnings {THREADS}" in commands + assert LINKER_FLAG not in commands + + +def test_gate_recipes_keep_an_inherited_rustflags() -> None: + """The recipe appends to an inherited value instead of replacing it.""" + commands = _recipe_commands("test-rust", "Linux") + assert re.search(r'RUSTFLAGS="\$\{RUSTFLAGS:\+\$RUSTFLAGS \}', commands) + + +@pytest.mark.parametrize("target", GATE_TARGETS) +def test_gate_recipes_refuse_a_cross_target_invocation(target: str) -> None: + """The linker flag comes from the host, so a target override is refused.""" + completed = _make_dry_run( + target, "Linux", env={"CARGO_BUILD_TARGET": "x86_64-pc-windows-gnu"} + ) + assert completed.returncode != 0 + assert "picks `mold` from the host" in completed.stderr + + +def test_whitaker_keeps_its_own_flag_boundary() -> None: + """The Whitaker driver does not inherit the development flags.""" + commands = _recipe_commands("lint-whitaker", "Linux") + whitaker = [line for line in commands.splitlines() if "whitaker" in line] + assert whitaker, "lint-whitaker must run Whitaker" + assert all(THREADS not in line and LINKER_FLAG not in line for line in whitaker) + + +def _build_job() -> dict[str, typ.Any]: + return inv.load_workflow("ci.yml")["jobs"]["build"] + + +def _setup_rust_step(job: dict[str, typ.Any]) -> dict[str, typ.Any]: + steps = [ + step + for step in job["steps"] + if "/actions/setup-rust@" in str(step.get("uses", "")) + ] + assert len(steps) == 1, "the build job must run setup-rust once" + return steps[0] + + +def test_the_build_job_installs_the_linker_and_exports_the_standard_flags() -> None: + """setup-rust exports the complete flags, so cargo steps keep the standard.""" + inputs = _setup_rust_step(_build_job())["with"] + assert str(inputs.get("install-mold")) == "true" + assert inputs.get("rustflags") == STANDARD_FLAGS + + +def test_the_dependabot_test_step_restates_the_standard_flags() -> None: + """The step that assigns ``RUSTFLAGS`` itself carries the same flags.""" + steps = [step for step in _build_job()["steps"] if step.get("name") == "Rust tests"] + assert len(steps) == 1 + assert f'RUSTFLAGS="{STANDARD_FLAGS}" cargo nextest run' in steps[0]["run"] + + +def test_the_mutation_workflow_installs_the_linker_and_restores_the_flags() -> None: + """``setup-rust`` exports ``-D warnings`` alone, so the flags are restored.""" + job = inv.load_workflow("mutation-testing.yml")["jobs"]["mutation"] + assert str(job["with"].get("install-mold")) == "true" + assert ( + f'RUSTFLAGS={STANDARD_FLAGS}" >> "$GITHUB_ENV"' in job["with"]["setup-commands"] + ) diff --git a/tests/workflow_contracts/mutation_testing_test.py b/tests/workflow_contracts/mutation_testing_test.py index 2f259205d..349636ebf 100644 --- a/tests/workflow_contracts/mutation_testing_test.py +++ b/tests/workflow_contracts/mutation_testing_test.py @@ -61,7 +61,13 @@ "backend/src/outbound/queue/test_helpers.rs" ), "extra-args": "--all-features --test-workspace=true", + # The build standard links with `mold` on Linux, so the runner needs it. + "install-mold": "true", "setup-commands": ( + # `setup-rust` exports `-D warnings` alone, which displaces the Cargo + # configuration, so the standard flags are restored for cargo-mutants. + 'echo "RUSTFLAGS=-D warnings -Zthreads=8 -Clink-arg=-fuse-ld=mold" ' + '>> "$GITHUB_ENV"\n' 'echo "POSTGRESQL_VERSION==16.10.0" >> "$GITHUB_ENV"\n' 'echo "POSTGRESQL_RELEASES_URL=' 'https://github.com/theseus-rs/postgresql-binaries" >> "$GITHUB_ENV"\n' From d81c4dcda97f635d0fa051a680cc7a4353bbb004 Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sat, 31 Oct 2026 14:47:34 +0000 Subject: [PATCH 2/2] Ignore the mold linker's identifiers in the spelling gate mold names the linker, not the English word the en-GB dictionary prefers. Ignore only the flag and the install-mold input so prose stays checked. --- typos.local.toml | 4 ++++ typos.toml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/typos.local.toml b/typos.local.toml index b1649663d..f006f94c8 100644 --- a/typos.local.toml +++ b/typos.local.toml @@ -10,6 +10,10 @@ accepted = [] [patterns] ignore = [ + # `mold` names the linker (https://github.com/rui314/mold), not the English + # word; match only the identifiers that carry it, so prose stays checked. + "-fuse-ld=mold", + "\\binstall-mold\\b", "backspace\\-pulverize\\-grime", "types:\\ \\[opened,\\ reopened,\\ synchronize,\\ labeled,\\ ready_for_review\\]", ",\\ SVG_FILENAME,\\ SnapshotArtifacts,\\ SnapshotGenerationErro", diff --git a/typos.toml b/typos.toml index 1d1f98448..7269310bd 100644 --- a/typos.toml +++ b/typos.toml @@ -41,6 +41,7 @@ extend-ignore-re = [ "(?x)\n\\#\\[\\s*tokio::main\\s*\\(\\s*\nflavor\\s*=\\s*(?:\\\\\"local\\\\\"|\"local\")\n(?:\\s*,\\s*|\\s*)\\)\\s*\\]", ",\\ SVG_FILENAME,\\ SnapshotArtifacts,\\ SnapshotGenerationErro", "--[A-Za-z0-9_-]*color(?:ed|s|ing|ize[ds]?|izing)?(?:\\b|_)", + "-fuse-ld=mold", "3\\ \\|\\ struct\\ NotDeserialisable\\ \\{", ":\\ 4813\\.62px;\\ background\\-color:\\ white;\"\\ class=\"erDiagr", ":\\ Option,", @@ -146,6 +147,7 @@ extend-ignore-re = [ "\\bAppFactory