From 1f673dba92921e236bb62e3acf7da6a92779333e Mon Sep 17 00:00:00 2001 From: Joe Rivera Date: Tue, 29 Sep 2026 21:09:32 -0500 Subject: [PATCH 1/3] fix(ariadne): guard channel_policy_ member on CVC_STATE_EXEC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Runtime::Impl declared `cvc::state_exec::channel_policy channel_policy_` unconditionally, but (which defines it) is only included under #ifdef CVC_STATE_EXEC. Builds with -DCVC_STATE_EXEC=OFF -- notably the non-pthread wasm gh-pages demos -- failed to compile ariadne.cpp since the §12 channel-scoping work. Every use of the member (set_channel_policy, the ictx.channels wiring in submit_action / submit_resident) is already inside CVC_STATE_EXEC, so guarding the declaration is sufficient. No behaviour change when state_exec is enabled; set_channel_policy stays a no-op without it. --- src/cvc/ariadne/ariadne.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cvc/ariadne/ariadne.cpp b/src/cvc/ariadne/ariadne.cpp index 7948f3d5..903f9b7d 100644 --- a/src/cvc/ariadne/ariadne.cpp +++ b/src/cvc/ariadne/ariadne.cpp @@ -626,8 +626,11 @@ struct Runtime::Impl { // §12 channel enforcement: the document's channel policy, installed on every action/resident // ictx. Default (enforce=false, empty) = permissive; set_channel_policy fills it from a // LoadResult. Pointed-to by ictx.channels (non-owning), so it must outlive the contexts — it - // lives here in the Impl, which owns the live_actions_ / residents that reference it. + // lives here in the Impl, which owns the live_actions_ / residents that reference it. Declared + // before them so it destructs after them. Only exists with state_exec (no ictx to point at it). +#ifdef CVC_STATE_EXEC cvc::state_exec::channel_policy channel_policy_; +#endif Widget root; Widget pending; From 71d8176e99f9c6c4edbae008ebdf4409a94d45f5 Mon Sep 17 00:00:00 2001 From: Joe Rivera Date: Tue, 29 Sep 2026 21:38:29 -0500 Subject: [PATCH 2/3] wasm: build with CVC_STATE_EXEC=ON everywhere state_exec is the substrate Ariadne apps run on (the demos are moving to .ari apps), so forcing it OFF in the wasm builds only strips the program lanes and leaves an OFF configuration nothing else exercises -- which is how #467's unguarded channel_policy_ broke the gh-pages build. #438 already turned it ON for the wasm-mt libcvc/cvcgl publish; do the same for the rest: - src/cvcGL/examples/wasm/build-wasm-demo.sh (deploy-pages gh-pages gallery, both non-pthread and --pthread) - cvcpkg/recipes/cvcgl-examples/build-wasm.sh (wasm-mt gallery bundle); cvc_revision 2 -> 3 so the change republishes over 3.4.0+cvc.2 - cvcpkg/recipes/pycvc-gl-cp312/build-wasm.{sh,ps1} (CPython-wasm host; no wasm variant published yet, so no revision bump needed) The paths the demos use (stackless evaluator + async_scheduler) spawn no threads, so this is safe on the non-pthread build; the compute pool sizes itself to 0 workers there. --- cvcpkg/recipes/cvcgl-examples/build-wasm.sh | 2 +- cvcpkg/recipes/cvcgl-examples/recipe.yaml | 5 ++++- cvcpkg/recipes/pycvc-gl-cp312/build-wasm.ps1 | 2 +- cvcpkg/recipes/pycvc-gl-cp312/build-wasm.sh | 2 +- src/cvcGL/examples/wasm/build-wasm-demo.sh | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cvcpkg/recipes/cvcgl-examples/build-wasm.sh b/cvcpkg/recipes/cvcgl-examples/build-wasm.sh index 2e8ba80a..d3b47a66 100755 --- a/cvcpkg/recipes/cvcgl-examples/build-wasm.sh +++ b/cvcpkg/recipes/cvcgl-examples/build-wasm.sh @@ -54,7 +54,7 @@ emcmake cmake -G Ninja \ -DCVC_ENABLE_ASSIMP=ON \ -DCVC_ENABLE_MESHER=OFF \ -DCVC_ENABLE_SDF=ON \ - -DCVC_STATE_EXEC=OFF \ + -DCVC_STATE_EXEC=ON \ -DCVC_BUILD_CVCGL=ON \ -DCVC_BUILD_EXAMPLES=ON \ -DCVC_WASM_PTHREADS=ON \ diff --git a/cvcpkg/recipes/cvcgl-examples/recipe.yaml b/cvcpkg/recipes/cvcgl-examples/recipe.yaml index 488ec3b9..bf9a2c2a 100644 --- a/cvcpkg/recipes/cvcgl-examples/recipe.yaml +++ b/cvcpkg/recipes/cvcgl-examples/recipe.yaml @@ -14,7 +14,10 @@ recipe: # build-pages.py into share/cvcgl-examples/web/, so cvcgl-examples-web serves # the same gallery that deploys to gh-pages. lsystem_forest dropped from the # wasm gallery (irrelevant to navigation). - cvc_revision: 2 + # Bumped 2 -> 3: build-wasm.sh now builds with CVC_STATE_EXEC=ON (was forced + # OFF), so the wasm gallery carries the state_exec program lanes that Ariadne + # apps run on — matching the wasm-mt libcvc/cvcgl bundles (#438). + cvc_revision: 3 maintainer: "cvcpkg group" maintainer_email: "info@cvcpkg.org" maintainer_url: "https://cvcpkg.org" diff --git a/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.ps1 b/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.ps1 index fc5d064d..1f7ae433 100644 --- a/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.ps1 +++ b/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.ps1 @@ -85,7 +85,7 @@ Invoke-CvcWasmCMakeBuild -ExtraArgs @( '-DCVC_ENABLE_ASSIMP=ON', '-DCVC_ENABLE_MESHER=OFF', '-DCVC_ENABLE_SDF=ON', - '-DCVC_STATE_EXEC=OFF', + '-DCVC_STATE_EXEC=ON', '-DCVC_BUILD_CVCGL=ON', '-DCVC_BUILD_EXAMPLES=OFF', "-DCVC_WASM_PTHREADS=$pthreads", diff --git a/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.sh b/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.sh index 13aec89b..7975ee87 100644 --- a/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.sh +++ b/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.sh @@ -92,7 +92,7 @@ emcmake cmake -G Ninja \ -DCVC_ENABLE_ASSIMP=ON \ -DCVC_ENABLE_MESHER=OFF \ -DCVC_ENABLE_SDF=ON \ - -DCVC_STATE_EXEC=OFF \ + -DCVC_STATE_EXEC=ON \ -DCVC_BUILD_CVCGL=ON \ -DCVC_BUILD_EXAMPLES=OFF \ -DCVC_WASM_PTHREADS="${_PTHREADS}" \ diff --git a/src/cvcGL/examples/wasm/build-wasm-demo.sh b/src/cvcGL/examples/wasm/build-wasm-demo.sh index bd67b3c2..2246cdd0 100755 --- a/src/cvcGL/examples/wasm/build-wasm-demo.sh +++ b/src/cvcGL/examples/wasm/build-wasm-demo.sh @@ -92,7 +92,7 @@ emcmake cmake -G Ninja -S "${REPO_ROOT}" -B "${BUILD_DIR}" \ ${CVC_WASM_NAV_WEIGHTS:+-DCVC_WASM_NAV_WEIGHTS="${CVC_WASM_NAV_WEIGHTS}"} \ -DCVC_ENABLE_MESHER=OFF \ -DCVC_ENABLE_SDF=ON \ - -DCVC_STATE_EXEC=OFF \ + -DCVC_STATE_EXEC=ON \ -DCVC_BUILD_CVCGL=ON \ -DCVC_BUILD_EXAMPLES=ON \ -DCVC_WASM_PTHREADS=${PTHREAD} From 2c391c8f0e2bcc713a8ed7dc4391fa3c68c44bfb Mon Sep 17 00:00:00 2001 From: Joe Rivera Date: Tue, 29 Sep 2026 22:16:46 -0500 Subject: [PATCH 3/3] state_exec: remove the CVC_STATE_EXEC option -- always built state_exec is core: Ariadne's program + read lanes run on it and the demos are moving to .ari apps. An OFF configuration only stripped those lanes and left a build mode nothing tested -- the gh-pages wasm build was its sole user, and #467's unguarded channel_policy_ broke it after merge. - CMake: drop option(CVC_STATE_EXEC), the core/state_exec/ source filter, the PUBLIC compile definition, and the if(CVC_STATE_EXEC) test gating. - Strip every #ifdef/#ifndef CVC_STATE_EXEC, keeping the ON code: ariadne (runtime, loader, app_runtime, net_intrinsics), core async_task, pycvc Exec. The OFF fallbacks (warn-and-show reactive slots, run_init error, throwing Exec stubs, no-op residents/verbs) are gone. - Remove have_state_exec() (cvc::ariadne, AppRuntime, cvc::gl AriRuntime) and its callers: nav_compute's runtime check, net_intrinsics' early return, and the GTEST_SKIP / Python skip guards in the tests. - Drop the now-meaningless -DCVC_STATE_EXEC=ON from the wasm build scripts and publish-cvcgl-wasm.yml; update docs. This also fixes the native OFF build's executable links (app.cpp's exec_scheduler() and nav_compute referenced state_exec unconditionally), since there is no OFF build any more. --- .github/workflows/publish-cvcgl-wasm.yml | 2 +- bindings/pycvc/pycvc_exec.cpp | 26 +- bindings/pycvc/pycvc_exec.h | 3 - bindings/pycvc/test_pycvc_ari.py | 1 - bindings/pycvc/test_pycvc_exec.py | 39 +-- bindings/pycvc/test_pycvc_integration.py | 20 +- cvcpkg/recipes/cvcgl-examples/build-wasm.sh | 1 - cvcpkg/recipes/cvcgl-examples/recipe.yaml | 6 +- cvcpkg/recipes/libcvc/recipe.yaml | 4 +- cvcpkg/recipes/pycvc-gl-cp312/build-wasm.ps1 | 1 - cvcpkg/recipes/pycvc-gl-cp312/build-wasm.sh | 1 - docs/ARIADNE.md | 9 +- docs/roadmap/ARIADNE_APPRUNTIME_REFACTOR.md | 1 - docs/roadmap/CVCGL-UI-DSL-ROADMAP.md | 24 +- docs/roadmap/STATE_EXEC_PORTING_PLAN.md | 3 +- docs/roadmap/STATE_EXEC_ROADMAP.md | 2 +- inc/cvc/ariadne/app_runtime.h | 9 +- inc/cvc/ariadne/ariadne.h | 26 +- inc/cvc/ariadne/net_intrinsics.h | 2 +- inc/cvc/core/async_task.h | 4 - inc/cvc/gl/ariadne/AriRuntime.h | 5 +- src/cvc/CMakeLists.txt | 11 +- src/cvc/ariadne/app_runtime.cpp | 14 +- src/cvc/ariadne/ariadne.cpp | 131 +------- src/cvc/ariadne/loader.cpp | 12 +- src/cvc/ariadne/net_intrinsics.cpp | 29 +- src/cvc/core/async_task.cpp | 7 +- src/cvc/tests/CMakeLists.txt | 328 +++++++++---------- src/cvc/tests/ariadne_loader_test.cpp | 16 - src/cvc/tests/ariadne_runtime_test.cpp | 146 +-------- src/cvcGL/ariadne/AriRuntime.cpp | 1 - src/cvcGL/examples/nav_compute.cpp | 45 ++- src/cvcGL/examples/wasm/build-wasm-demo.sh | 1 - 33 files changed, 263 insertions(+), 667 deletions(-) diff --git a/.github/workflows/publish-cvcgl-wasm.yml b/.github/workflows/publish-cvcgl-wasm.yml index 9f2ecb0a..052d643d 100644 --- a/.github/workflows/publish-cvcgl-wasm.yml +++ b/.github/workflows/publish-cvcgl-wasm.yml @@ -102,7 +102,7 @@ jobs: -DCVC_ENABLE_OPENMP=OFF -DDISABLE_CGAL=ON -DCVC_USING_HDF5=OFF \ -DCVC_USING_IMOD_MRC=OFF -DCVC_ENABLE_IMAGEMAGICK=ON -DCVC_ENABLE_FFTW=OFF \ -DCVC_FFT_PROVIDER=none -DCVC_ENABLE_ASSIMP=ON \ - -DCVC_ENABLE_MESHER=OFF -DCVC_ENABLE_SDF=ON -DCVC_STATE_EXEC=ON \ + -DCVC_ENABLE_MESHER=OFF -DCVC_ENABLE_SDF=ON \ -DCVC_BUILD_CVCGL=ON -DCVC_BUILD_EXAMPLES=OFF -DCVC_WASM_PTHREADS=ON cmake --build build-wasm --target cvc cvcGL -j"$(nproc)" cmake --install build-wasm diff --git a/bindings/pycvc/pycvc_exec.cpp b/bindings/pycvc/pycvc_exec.cpp index 428b8987..f2725aee 100644 --- a/bindings/pycvc/pycvc_exec.cpp +++ b/bindings/pycvc/pycvc_exec.cpp @@ -6,10 +6,6 @@ #include "pycvc_exec.h" -#include - -#ifdef CVC_STATE_EXEC - #include #include #include @@ -23,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -634,24 +631,3 @@ std::string Exec::run(const std::string &src) { } } // namespace pycvc - -#else // !CVC_STATE_EXEC - -namespace pycvc { - -struct Exec::ExecImpl {}; - -Exec::Exec(const std::shared_ptr &) { - throw std::runtime_error("pycvc.Exec: this libcvc build was compiled without state_exec"); -} -Exec::~Exec() = default; -void Exec::register_fn(const std::string &, PyObject *) { - throw std::runtime_error("pycvc.Exec: this libcvc build was compiled without state_exec"); -} -std::string Exec::run(const std::string &) { - throw std::runtime_error("pycvc.Exec: this libcvc build was compiled without state_exec"); -} - -} // namespace pycvc - -#endif // CVC_STATE_EXEC diff --git a/bindings/pycvc/pycvc_exec.h b/bindings/pycvc/pycvc_exec.h index c3e4dd04..192de81d 100644 --- a/bindings/pycvc/pycvc_exec.h +++ b/bindings/pycvc/pycvc_exec.h @@ -5,9 +5,6 @@ // environment + scheduler over that app's state root, so registered Python // functions and run() programs all see the same shared tree. A Python function // registered here becomes callable from DSL source like any builtin. -// -// Guarded by CVC_STATE_EXEC (a PUBLIC compile def on the cvc target); when the -// build lacks state_exec, the ctor throws. #pragma once #include diff --git a/bindings/pycvc/test_pycvc_ari.py b/bindings/pycvc/test_pycvc_ari.py index 0be3d20f..6bdf24ba 100644 --- a/bindings/pycvc/test_pycvc_ari.py +++ b/bindings/pycvc/test_pycvc_ari.py @@ -35,7 +35,6 @@ def _child(): ari = pycvc_gl.AriRuntime(view, cam, ui) try: - assert pycvc_gl.AriRuntime.have_state_exec(), "expected state_exec in this build" pinged = {"n": 0} ari.on("ping", lambda: pinged.__setitem__("n", pinged["n"] + 1)) warnings = ari.load(os.path.join(HERE, "demo.ari")) diff --git a/bindings/pycvc/test_pycvc_exec.py b/bindings/pycvc/test_pycvc_exec.py index 53a035e4..e22b7c9e 100644 --- a/bindings/pycvc/test_pycvc_exec.py +++ b/bindings/pycvc/test_pycvc_exec.py @@ -11,25 +11,12 @@ import pycvc -def _exec(app): - """Exec(app), or None if this build lacks state_exec.""" - try: - return pycvc.Exec(app) - except Exception as e: - if "without state_exec" in str(e): - return None - raise - - # ── core DSL evaluation ───────────────────────────────────────────────── def test_arithmetic_and_forms(): app = pycvc.make_app() - ex = _exec(app) - if ex is None: - print(" skip: build has no state_exec") - return + ex = pycvc.Exec(app) assert ex.run("(+ 2 3)") == "5" assert ex.run("(* 6 7)") == "42" assert ex.run("(begin (set x 5) x)") == "5" @@ -40,9 +27,7 @@ def test_arithmetic_and_forms(): def test_parse_error_raises(): app = pycvc.make_app() - ex = _exec(app) - if ex is None: - return + ex = pycvc.Exec(app) try: ex.run("(begin (set x") # unbalanced except Exception: @@ -56,9 +41,7 @@ def test_parse_error_raises(): def test_dsl_reads_and_writes_shared_state(): app = pycvc.make_app() - ex = _exec(app) - if ex is None: - return + ex = pycvc.Exec(app) # Python writes state; DSL reads it. pycvc.state_set(app, "shared.x", "7") assert ex.run('(state-get "shared.x")') == "7" @@ -72,9 +55,7 @@ def test_dsl_reads_and_writes_shared_state(): def test_python_function_called_from_dsl(): app = pycvc.make_app() - ex = _exec(app) - if ex is None: - return + ex = pycvc.Exec(app) ex.register_fn("py-double", lambda x: x * 2) ex.register_fn("py-add", lambda a, b: a + b) @@ -89,9 +70,7 @@ def test_python_function_called_from_dsl(): def test_python_dsl_function_touches_state(): app = pycvc.make_app() - ex = _exec(app) - if ex is None: - return + ex = pycvc.Exec(app) # A Python DSL function that writes state on the shared app. def stash(key, value): @@ -109,9 +88,7 @@ def stash(key, value): def test_python_exception_is_contained(): app = pycvc.make_app() - ex = _exec(app) - if ex is None: - return + ex = pycvc.Exec(app) def boom(*_): raise ValueError("kaboom") @@ -130,9 +107,7 @@ def boom(*_): def test_value_marshaling_types(): app = pycvc.make_app() - ex = _exec(app) - if ex is None: - return + ex = pycvc.Exec(app) seen = {} def capture(x): diff --git a/bindings/pycvc/test_pycvc_integration.py b/bindings/pycvc/test_pycvc_integration.py index 826d50c2..def9818f 100644 --- a/bindings/pycvc/test_pycvc_integration.py +++ b/bindings/pycvc/test_pycvc_integration.py @@ -52,19 +52,13 @@ def on_changed(self, path): # Phase 4: a DSL program calls a Python function that reads the state we just # wrote — Python <-> state <-> DSL, all on the same app. - ex = None - try: - ex = pycvc.Exec(app) - except Exception as e: - if "without state_exec" not in str(e): - raise - if ex is not None: - ex.register_fn("verts", lambda: int(pycvc.state_get(app, "mesh.verts"))) - # (+ 0 (verts)) forces numeric context; equals the vertex count. - assert ex.run("(+ 0 (verts))") == str(surf.num_vertices()) - # A DSL write is visible back in Python. - ex.run('(state-set "dsl.done" "yes")') - assert pycvc.state_get(app, "dsl.done") == "yes" + ex = pycvc.Exec(app) + ex.register_fn("verts", lambda: int(pycvc.state_get(app, "mesh.verts"))) + # (+ 0 (verts)) forces numeric context; equals the vertex count. + assert ex.run("(+ 0 (verts))") == str(surf.num_vertices()) + # A DSL write is visible back in Python. + ex.run('(state-set "dsl.done" "yes")') + assert pycvc.state_get(app, "dsl.done") == "yes" # Phase 5a: drop the meshes into a scene bound to the SAME app (no cvcGL # singleton). num_graphics reflects what we added. diff --git a/cvcpkg/recipes/cvcgl-examples/build-wasm.sh b/cvcpkg/recipes/cvcgl-examples/build-wasm.sh index d3b47a66..dfce801d 100755 --- a/cvcpkg/recipes/cvcgl-examples/build-wasm.sh +++ b/cvcpkg/recipes/cvcgl-examples/build-wasm.sh @@ -54,7 +54,6 @@ emcmake cmake -G Ninja \ -DCVC_ENABLE_ASSIMP=ON \ -DCVC_ENABLE_MESHER=OFF \ -DCVC_ENABLE_SDF=ON \ - -DCVC_STATE_EXEC=ON \ -DCVC_BUILD_CVCGL=ON \ -DCVC_BUILD_EXAMPLES=ON \ -DCVC_WASM_PTHREADS=ON \ diff --git a/cvcpkg/recipes/cvcgl-examples/recipe.yaml b/cvcpkg/recipes/cvcgl-examples/recipe.yaml index bf9a2c2a..b5b04e9e 100644 --- a/cvcpkg/recipes/cvcgl-examples/recipe.yaml +++ b/cvcpkg/recipes/cvcgl-examples/recipe.yaml @@ -14,9 +14,9 @@ recipe: # build-pages.py into share/cvcgl-examples/web/, so cvcgl-examples-web serves # the same gallery that deploys to gh-pages. lsystem_forest dropped from the # wasm gallery (irrelevant to navigation). - # Bumped 2 -> 3: build-wasm.sh now builds with CVC_STATE_EXEC=ON (was forced - # OFF), so the wasm gallery carries the state_exec program lanes that Ariadne - # apps run on — matching the wasm-mt libcvc/cvcgl bundles (#438). + # Bumped 2 -> 3: build-wasm.sh no longer forces state_exec OFF (the + # CVC_STATE_EXEC option is gone; state_exec is always built), so the wasm + # gallery carries the program lanes that Ariadne apps run on. cvc_revision: 3 maintainer: "cvcpkg group" maintainer_email: "info@cvcpkg.org" diff --git a/cvcpkg/recipes/libcvc/recipe.yaml b/cvcpkg/recipes/libcvc/recipe.yaml index 75dcd88b..9a7c91ce 100644 --- a/cvcpkg/recipes/libcvc/recipe.yaml +++ b/cvcpkg/recipes/libcvc/recipe.yaml @@ -286,9 +286,9 @@ depends: # The gRPC/xmlrpc network state-transport stack (CVC_USING_XMLRPC + CVC_ENABLE_GRPC). # Scoped to the platforms this recipe actually BUILDS the transport on (the build matrix # below: linux/macos/windows). The wasm-mt bundle (built separately by publish-cvcgl-wasm.yml, - # xmlrpc/grpc OFF, CVC_STATE_EXEC ON) links none of these, so declaring them for wasm made + # xmlrpc/grpc OFF) links none of these, so declaring them for wasm made # `cvcpkg install cvc/libcvc --platform wasm-mt` fail to resolve (abseil/protobuf/grpc are not - # published for wasm). The state_exec evaluator is independent of this stack and stays on. + # published for wasm). The state_exec evaluator is independent of this stack (always built). - name: openssl platforms: [linux, macos, windows] - name: c-ares diff --git a/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.ps1 b/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.ps1 index 1f7ae433..43fdf829 100644 --- a/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.ps1 +++ b/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.ps1 @@ -85,7 +85,6 @@ Invoke-CvcWasmCMakeBuild -ExtraArgs @( '-DCVC_ENABLE_ASSIMP=ON', '-DCVC_ENABLE_MESHER=OFF', '-DCVC_ENABLE_SDF=ON', - '-DCVC_STATE_EXEC=ON', '-DCVC_BUILD_CVCGL=ON', '-DCVC_BUILD_EXAMPLES=OFF', "-DCVC_WASM_PTHREADS=$pthreads", diff --git a/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.sh b/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.sh index 7975ee87..cc4fec90 100644 --- a/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.sh +++ b/cvcpkg/recipes/pycvc-gl-cp312/build-wasm.sh @@ -92,7 +92,6 @@ emcmake cmake -G Ninja \ -DCVC_ENABLE_ASSIMP=ON \ -DCVC_ENABLE_MESHER=OFF \ -DCVC_ENABLE_SDF=ON \ - -DCVC_STATE_EXEC=ON \ -DCVC_BUILD_CVCGL=ON \ -DCVC_BUILD_EXAMPLES=OFF \ -DCVC_WASM_PTHREADS="${_PTHREADS}" \ diff --git a/docs/ARIADNE.md b/docs/ARIADNE.md index 025dfdc1..8efffe97 100644 --- a/docs/ARIADNE.md +++ b/docs/ARIADNE.md @@ -78,8 +78,8 @@ safety property. You cannot make a "read" slot perform an action, or vice versa. Reactive slots — `visible_when`, `enabled_when` / `disabled_when`, computed `bind` / `text`, `tooltip`, `options`, `repeat.count` — run in a long-lived stackless evaluator -(`class ReactiveEngine`, [`ariadne.cpp:385`](../src/cvc/ariadne/ariadne.cpp), compiled -under `CVC_STATE_EXEC`) over a **default-deny** environment: +(`class ReactiveEngine`, [`ariadne.cpp:385`](../src/cvc/ariadne/ariadne.cpp)) over a +**default-deny** environment: - The value allowlist admits arithmetic, coercion, logic, and side-effect-free state *readers* only (`kAllowed`, [`ariadne.cpp:417`](../src/cvc/ariadne/ariadne.cpp)). @@ -91,9 +91,8 @@ Nothing that writes state, schedules, watches, sends messages, or does I/O is reachable. An off-allowlist symbol does not error loudly — it **fails safe**: the slot degrades to hidden / disabled / empty / last-good value and the frame records a one-shot warning (`take_reactive_warnings`, -[`ariadne.cpp:1682`](../src/cvc/ariadne/ariadne.cpp)). When the build lacks -`CVC_STATE_EXEC` the whole engine is `#ifdef`'d out and reactive slots default to their -fail-safe *shown* state. +[`ariadne.cpp:1682`](../src/cvc/ariadne/ariadne.cpp)). state_exec is always built (there is +no build option to drop it), so the engine is always present. **The read lane never parks.** It is bounded three ways ([`ariadne.cpp:603`](../src/cvc/ariadne/ariadne.cpp)): diff --git a/docs/roadmap/ARIADNE_APPRUNTIME_REFACTOR.md b/docs/roadmap/ARIADNE_APPRUNTIME_REFACTOR.md index 27157b0a..d33218df 100644 --- a/docs/roadmap/ARIADNE_APPRUNTIME_REFACTOR.md +++ b/docs/roadmap/ARIADNE_APPRUNTIME_REFACTOR.md @@ -85,7 +85,6 @@ public: std::vector take_warnings(); bool reload_if_changed(); - static bool have_state_exec(); static bool have_yaml(); }; } diff --git a/docs/roadmap/CVCGL-UI-DSL-ROADMAP.md b/docs/roadmap/CVCGL-UI-DSL-ROADMAP.md index 5c59847d..30cd1a90 100644 --- a/docs/roadmap/CVCGL-UI-DSL-ROADMAP.md +++ b/docs/roadmap/CVCGL-UI-DSL-ROADMAP.md @@ -902,8 +902,8 @@ This is the heart of v0.2. Every `*_when`, `fmt`, dynamic `options`, `repeat.cou computed `bind`, and every `on:`/`on_change:` action is a `state_exec` program over the **same state tree the widgets bind to**. -> **Status — the read-only lane's first field, `visible_when`, is LANDED** (`cvc::ariadne`, -> `CVC_STATE_EXEC`-gated). `Widget::visible_when` is a predicate the walk re-evaluates each +> **Status — the read-only lane's first field, `visible_when`, is LANDED** (`cvc::ariadne`). +> `Widget::visible_when` is a predicate the walk re-evaluates each > frame; falsy → the widget and its subtree are skipped (in a grid, a hidden child consumes > **no** cell). Implementation notes that refine §4.1 below as-built: > - **Default-deny, on mechanisms not just an allowlist.** The between-steps caps cannot see @@ -928,18 +928,16 @@ the **same state tree the widgets bind to**. > aggregate wall-time budget (so N reactive widgets can't together stall a frame). A step > cap returns `done==false`; a time cap throws `evaluation_timeout`; both → fail-safe hide > + one warning. Diagnostics are de-duplicated and drained by `Runtime::take_reactive_warnings()`. -> - **Fail-safe polarity.** A broken/over-budget predicate HIDES the widget (§4.1). The one -> deliberate carve-out: on a build **without** `CVC_STATE_EXEC` a `visible_when` widget is -> SHOWN (+ a one-time warning), not hidden — hiding every reactive widget would gut a -> minimal build. That is a build-config axis, distinct from a per-eval failure on a build -> that *has* the evaluator (which hides). +> - **Fail-safe polarity.** A broken/over-budget predicate HIDES the widget (§4.1). (An earlier +> carve-out SHOWED `visible_when` widgets on builds without state_exec; the `CVC_STATE_EXEC` +> option was removed in #493, so every build has the evaluator.) > > **Also LANDED — `enabled_when` / `disabled_when`** (reactive grey-out, the sibling of > `visible_when`): a widget stays drawn but non-interactive when `enabled_when` is falsy or > `disabled_when` is truthy. Realized through a new backend `begin_disabled()`/`end_disabled()` > scope (non-pure no-op defaults; ImGuiBackend → `ImGui::BeginDisabled/EndDisabled`) wrapping the -> widget and its subtree. Fail-safe DISABLES on a broken predicate; a build without state_exec -> leaves it enabled (+ warns once). Same hardened engine as `visible_when`. +> widget and its subtree. Fail-safe DISABLES on a broken predicate. Same hardened engine as +> `visible_when`. > > **Also LANDED — computed values + `options` + `tooltip`** (on `ReactiveEngine::eval_string` / > `eval_string_list`): a Text whose `bind` is an s-expression (starts with `(`) is a computed @@ -1160,7 +1158,7 @@ other `on:tick`/`on:key` cadence questions. `state-set` (coalesced publisher). No scheduler touched on the draw thread. - **Action fire (during the walk):** never inline. Fast path enqueues an intent; general path calls `execute(ast, opts)→pid`. - > **Program `on:` LANDED** (`ariadne.cpp`, `CVC_STATE_EXEC`): an `on:` value that starts with + > **Program `on:` LANDED** (`ariadne.cpp`): an `on:` value that starts with > `(` — an s-expression, exactly like a computed `bind:`/`tooltip:` — is a **state_exec program** > run at `Runtime::drain()`, not a bare event name. So a flag toggle / reset is **pure `.ari`**, > no C++ handler (`on: (state-set "paused" (if …))`). It shares one bounded runner with the @@ -3848,13 +3846,13 @@ points are before anything renders. **The `init:` block — a state_exec script run on load.** An optional top-level `init:` block carries a `cvc::state_exec` script that runs ONCE at load, for dynamic initialization (seed/compute state before the first frame). The loader only CAPTURES the text into `LoadResult::init_script` (it has no `cvc::app` and -never runs the DSL); `cvc::ariadne::run_init(app, prefix, script, errors)` runs it — a core seam gated by -`CVC_STATE_EXEC` (state_exec is core libcvc, so no VTK), which the host calls after load and BEFORE the +never runs the DSL); `cvc::ariadne::run_init(app, prefix, script, errors)` runs it — a core seam +(state_exec is core libcvc, so no VTK), which the host calls after load and BEFORE the first `render()`. It runs the script under a state_exec **chroot at the document prefix**, so `(state-set "demo.agents" "256")` writes `.demo.agents` — the same key a widget `bind: demo.agents` resolves to (the shared "." separator), and init values win because widget/scene `read_or_seed` only fills keys init left unset. A parse/runtime error is reported (never thrown), not fatal — init is optional -dynamic seeding, not a hard gate; `have_state_exec()` reports whether the build can run it. The run is +dynamic seeding, not a hard gate. The run is **bounded** (a finite step + wall-clock budget on both the process and the run loop, and success requires a `terminated` status) so a looping or blocking init script is reported rather than hanging the app at load. This is a new concept beyond the two §4.1 state_exec lanes (per-frame read + effectful action): a run-once init lane. diff --git a/docs/roadmap/STATE_EXEC_PORTING_PLAN.md b/docs/roadmap/STATE_EXEC_PORTING_PLAN.md index ebe94a86..a8fbc372 100644 --- a/docs/roadmap/STATE_EXEC_PORTING_PLAN.md +++ b/docs/roadmap/STATE_EXEC_PORTING_PLAN.md @@ -2315,7 +2315,8 @@ Map per category: #### Phase 7: CMake Integration + Polish 34. ✅ **CMakeLists.txt** updates — `CVC_STATE_EXEC` and - `CVC_STATE_MEMORY_MANAGER` feature-flag options (default ON). + `CVC_STATE_MEMORY_MANAGER` feature-flag options (default ON). (`CVC_STATE_EXEC` + was later removed in #493 — state_exec is always built.) Conditional source filtering and test target guards (`add_executable`, `target_link_libraries`, `target_compile_features`, `gtest_discover_tests`) wrap all state_exec and memory_manager targets. diff --git a/docs/roadmap/STATE_EXEC_ROADMAP.md b/docs/roadmap/STATE_EXEC_ROADMAP.md index 9c41bf57..248bed07 100644 --- a/docs/roadmap/STATE_EXEC_ROADMAP.md +++ b/docs/roadmap/STATE_EXEC_ROADMAP.md @@ -100,7 +100,7 @@ Full design: [STATE_EXEC_PORTING_PLAN.md](STATE_EXEC_PORTING_PLAN.md) | Component | Status | Tests | |-----------|--------|-------| -| `CVC_STATE_EXEC` feature flag | ✅ Done | — | +| `CVC_STATE_EXEC` feature flag | ✅ Done, later removed (#493) — always built | — | | `CVC_STATE_MEMORY_MANAGER` feature flag | ✅ Done | — | | Doxygen `@file`/`@brief` on all 17 headers | ✅ Done | — | | Benchmark tests | ✅ Done | 7 | diff --git a/inc/cvc/ariadne/app_runtime.h b/inc/cvc/ariadne/app_runtime.h index 3475cb14..e03fc0df 100644 --- a/inc/cvc/ariadne/app_runtime.h +++ b/inc/cvc/ariadne/app_runtime.h @@ -12,10 +12,8 @@ // + a cvcGL SceneAdapter + an AppRuntime. A terminal host would compose an FtxuiBackend // + no adapter over the same AppRuntime. // -// libpython-free (std::function, never PyObject*). The program lanes (init:/on_tick/ -// on_key/on_pointer, register_verb*, the scheduler pump) are no-ops without -// CVC_STATE_EXEC; loading a YAML .ari needs the yaml build. have_state_exec()/have_yaml() -// report which are live. +// libpython-free (std::function, never PyObject*). Loading a YAML .ari needs the yaml +// build; have_yaml() reports whether it is live. #pragma once #include @@ -64,7 +62,7 @@ class AppRuntime { // inline on the drain thread, no args/return). register_async_verb OFFLOADS `work` to the // app compute pool and posts its string result to `done_channel`; the program parks on // (msg-recv "done_channel") and resumes under drain()'s slice budget (the nav_compute - // pattern). Both are no-ops without state_exec. + // pattern). void register_verb(const std::string &name, std::function fn); void register_async_verb(const std::string &name, const std::string &done_channel, std::function work); @@ -96,7 +94,6 @@ class AppRuntime { // --- diagnostics / capabilities --- std::vector take_warnings(); // reactive read-lane diagnostics from the last render bool reload_if_changed(); // §12.5 hot reload if a watched source changed - static bool have_state_exec(); // program lanes + verbs live? static bool have_yaml(); // can load a YAML .ari? private: diff --git a/inc/cvc/ariadne/ariadne.h b/inc/cvc/ariadne/ariadne.h index e07bd039..2e88e89a 100644 --- a/inc/cvc/ariadne/ariadne.h +++ b/inc/cvc/ariadne/ariadne.h @@ -68,8 +68,8 @@ class Runtime { // Unlike a fire-once action `on:`, this submits ONE long-lived, owner-tagged process on the // app-wide scheduler that PARKS between frames (it never re-submits per frame); each drain() // posts it a tick to run the program once. Pass a LoadResult::on_tick_script here after loading. - // "" clears it (kills the resident). A no-op in a build without state_exec. The resident is - // reaped with the Runtime (kill_owner on teardown). + // "" clears it (kills the resident). The resident is reaped with the Runtime (kill_owner on + // teardown). void set_tick_program(std::string script); // §4.6 document-level input handlers — register a state_exec PROGRAM run per input event. Like @@ -79,7 +79,7 @@ class Runtime { // set_key_program handles keyboard events (key_down/key_up); set_pointer_program handles mouse // events (mouse_move/mouse_button_*/mouse_wheel). Pass LoadResult::on_key_script / // on_pointer_script. - // "" clears it. No-ops without state_exec. (Widget-level on_click/on_hover are a separate path.) + // "" clears it. (Widget-level on_click/on_hover are a separate path.) void set_key_program(std::string script); void set_pointer_program(std::string script); @@ -91,16 +91,14 @@ class Runtime { // (the load lint already surfaced those), which clears enforcement. The host feeds these from a // LoadResult (channels: + lint:); pass plain vectors so this header stays independent of the // loader. Calling with `strict=false` and empty lists (the default) disables runtime enforcement. - // A no-op without state_exec. Set once after load, before render/drain; it applies to every - // action + resident. + // Set once after load, before render/drain; it applies to every action + resident. void set_channel_policy(std::vector declared, std::vector global, bool strict, bool quiet); // Feed one input event to the runtime — the host calls this each frame for every event from its // input source (e.g. cvc::gl::SdlInput), BEFORE drain(), so the event is delivered to the // on_key/on_pointer residents this same frame (drain() drains the ingress + pumps). Thread-safe - // (routes through the scheduler's MPSC ingress). A no-op without state_exec, or if no matching - // handler is registered. + // (routes through the scheduler's MPSC ingress). A no-op if no matching handler is registered. void post_input(const InputEvent &ev); // Render one frame: apply any pending tree swap, then walk the tree driving the @@ -115,7 +113,7 @@ class Runtime { // Drain the §4 read-lane diagnostics accumulated during render() — one message per // distinct failing predicate (a parse error, a runtime error, or a per-frame budget - // overrun; or, on a build without state_exec, one note that visible_when was ignored). + // overrun). // De-duplicated for the Runtime's lifetime, so a broken predicate warns ONCE however // many frames it renders. Empty when everything evaluated cleanly. The host may log // these (e.g. after the first frame); ignoring them is safe — the walk already @@ -168,16 +166,12 @@ bool has_widget_type(const std::string &type); // `bind: demo.n` resolves to (a state_exec chroot on the shared "." separator). Run it // at load, BEFORE the first render(), so init values win and widget/scene read_or_seed // defaults only fill keys init left unset. Returns true on success (or an empty -// script); false with a message appended to `errors` on a parse/runtime error, or when -// this build lacks state_exec. Never throws. (The loader stays app-free and only -// captures the script; this is the host/Runtime-side seam that has the app.) +// script); false with a message appended to `errors` on a parse/runtime error. Never +// throws. (The loader stays app-free and only captures the script; this is the +// host/Runtime-side seam that has the app.) bool run_init(cvc::app &app, const std::string &prefix, const std::string &script, std::vector *errors = nullptr); -// Whether this build has state_exec (CVC_STATE_EXEC). When false, run_init cannot -// execute a non-empty init: script (it reports an error instead). -bool have_state_exec(); - // --------------------------------------------------------------------------- // Host-contributed state_exec intrinsics for the PROGRAM lanes (init: and a program `on:` action). // A host binds native functions — e.g. nav verbs `(nav-step)` / `(nav-arrived)` — that a .ari @@ -192,7 +186,7 @@ bool have_state_exec(); // per-frame reactive READ lane (visible_when/computed binds — kept default-deny + // side-effect-free). A program on: runs synchronously on the host thread inside Runtime::drain(), // so an intrinsic that mutates async host state (a worker-thread sim) should ENQUEUE the mutation, -// not do it inline. A no-op in a build without state_exec (the program lanes do not run). +// not do it inline. // --------------------------------------------------------------------------- } // namespace ariadne namespace state_exec { diff --git a/inc/cvc/ariadne/net_intrinsics.h b/inc/cvc/ariadne/net_intrinsics.h index 77a02a34..21ef6877 100644 --- a/inc/cvc/ariadne/net_intrinsics.h +++ b/inc/cvc/ariadne/net_intrinsics.h @@ -25,7 +25,7 @@ // `init:` (no per-frame pump, so a park never resumes) and NOT in the reactive read lane // (visible_when/computed, default-deny). NATIVE ONLY for now: the background-worker model assumes // native threads (wasm needs the §13.8 async-fetch path). A no-op without a compiled cvc::net -// backend or without state_exec. +// backend. namespace cvc { class app; diff --git a/inc/cvc/core/async_task.h b/inc/cvc/core/async_task.h index 7d78c01c..be31599e 100644 --- a/inc/cvc/core/async_task.h +++ b/inc/cvc/core/async_task.h @@ -37,8 +37,6 @@ #ifndef CVC_CORE_ASYNC_TASK_H #define CVC_CORE_ASYNC_TASK_H -#ifdef CVC_STATE_EXEC - #include #include #include @@ -81,6 +79,4 @@ state_exec::value_t future_pool_task(app &a, state_exec::async_scheduler &sched, } // namespace cvc -#endif // CVC_STATE_EXEC - #endif // CVC_CORE_ASYNC_TASK_H diff --git a/inc/cvc/gl/ariadne/AriRuntime.h b/inc/cvc/gl/ariadne/AriRuntime.h index 2cab8dea..f9d1a04e 100644 --- a/inc/cvc/gl/ariadne/AriRuntime.h +++ b/inc/cvc/gl/ariadne/AriRuntime.h @@ -10,8 +10,8 @@ // render test consume it exactly as before. libpython-free (std::function, never // PyObject*). Input is VTK's (widget on_click/on_hover + camera nav arrive through the VTK // interactor -> ImGui); document-level on_key/on_pointer residents need a producer fed via -// post_key/post_pointer (a follow-up seam), never SDL. The program lanes are no-ops without -// CVC_STATE_EXEC; loading a YAML .ari needs the yaml build. +// post_key/post_pointer (a follow-up seam), never SDL. Loading a YAML .ari needs the yaml +// build. #pragma once #include // cvc::ariadne::AppRuntime, SceneAdapter @@ -72,7 +72,6 @@ class AriRuntime { // --- diagnostics / capabilities --- std::vector take_warnings(); bool reload_if_changed(); - static bool have_state_exec(); static bool have_yaml(); private: diff --git a/src/cvc/CMakeLists.txt b/src/cvc/CMakeLists.txt index ccba3a49..9263b210 100644 --- a/src/cvc/CMakeLists.txt +++ b/src/cvc/CMakeLists.txt @@ -348,15 +348,9 @@ if(WIN32) list(REMOVE_ITEM SOURCE_FILES core/state_transport_ipc.cpp) endif() -# state_exec / state_memory_manager feature flags -option(CVC_STATE_EXEC "Enable state_exec DSL evaluator and process scheduler" ON) +# state_memory_manager feature flag option(CVC_STATE_MEMORY_MANAGER "Enable state memory manager with LRU/LFU eviction" ON) -if(NOT CVC_STATE_EXEC) - list(FILTER SOURCE_FILES EXCLUDE REGEX "core/state_exec/") -endif() - -# state_memory_manager feature flag if(NOT CVC_STATE_MEMORY_MANAGER) list(REMOVE_ITEM SOURCE_FILES core/state_memory_manager.cpp core/state_eviction_store.cpp) endif() @@ -1319,9 +1313,6 @@ if(CVC_ENABLE_GRPC) target_include_directories(cvc PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(cvc PRIVATE gRPC::grpc++ protobuf::libprotobuf) endif() -if(CVC_STATE_EXEC) - target_compile_definitions(cvc PUBLIC CVC_STATE_EXEC) -endif() if(CVC_STATE_MEMORY_MANAGER) target_compile_definitions(cvc PUBLIC CVC_STATE_MEMORY_MANAGER) endif() diff --git a/src/cvc/ariadne/app_runtime.cpp b/src/cvc/ariadne/app_runtime.cpp index e23f49e4..f70f8b4a 100644 --- a/src/cvc/ariadne/app_runtime.cpp +++ b/src/cvc/ariadne/app_runtime.cpp @@ -7,17 +7,14 @@ #include // load_file / LoadResult / ChannelDecl / sources_changed #include // register_cvc_uri_handler #include -#include -#include - -#ifdef CVC_STATE_EXEC #include // exec_scheduler().post_message #include // register_fn #include // intrinsics_context, resolve_channel_key #include // value_t #include #include -#endif +#include +#include namespace cvc { namespace ariadne { @@ -36,7 +33,6 @@ void AppRuntime::on(const std::string &event, std::function handler) { rt_.on(event, std::move(handler)); } -bool AppRuntime::have_state_exec() { return cvc::ariadne::have_state_exec(); } bool AppRuntime::have_yaml() { return cvc::ariadne::have_yaml(); } // ── host verbs on the app-wide scheduler ──────────────────────────────────────────────────────── @@ -52,9 +48,8 @@ void AppRuntime::register_async_verb(const std::string &name, const std::string ensure_intrinsics(); } -#ifdef CVC_STATE_EXEC void AppRuntime::ensure_intrinsics() { - if (intrinsicsRegistered_ || !have_state_exec()) + if (intrinsicsRegistered_) return; intrinsicsRegistered_ = true; cvc::app *app = &app_; @@ -97,9 +92,6 @@ void AppRuntime::ensure_intrinsics() { } }); } -#else -void AppRuntime::ensure_intrinsics() {} -#endif // ── document ──────────────────────────────────────────────────────────────────────────────────── diff --git a/src/cvc/ariadne/ariadne.cpp b/src/cvc/ariadne/ariadne.cpp index 903f9b7d..9f41494d 100644 --- a/src/cvc/ariadne/ariadne.cpp +++ b/src/cvc/ariadne/ariadne.cpp @@ -7,14 +7,16 @@ // backend reports the edit committed — so a slider drag costs one state write on // release, not one per frame (writes fan out to observers / replicated peers). +#include +#include +#include +#include #include #include #include #include // §4.6 InputEvent — Runtime::post_input feeds on_key/on_pointer #include #include - -#ifdef CVC_STATE_EXEC #include // §4.7 app-wide action scheduler (exec_scheduler) #include #include // evaluation_timeout / evaluation_interrupted @@ -23,12 +25,6 @@ #include #include #include // §4 read-lane predicate evaluator -#endif - -#include -#include -#include -#include #include #include #include // §11.4 geometry (de)serialize: pin std::locale::classic() so digit grouping @@ -233,20 +229,10 @@ bool has_widget_type(const std::string &type) { return widget_registry().find(type) != widget_registry().end(); } -// --- the init: block runner (state_exec, gated by CVC_STATE_EXEC) ------------ - -bool have_state_exec() { -#ifdef CVC_STATE_EXEC - return true; -#else - return false; -#endif -} +// --- the init: block runner (state_exec) -------------------------------------- -// Host-contributed program-lane intrinsics registry (§ nav verbs et al.). Kept OUTSIDE the -// CVC_STATE_EXEC guard so register/clear are always callable (the providers are simply never -// applied in a build with no program lanes). ActionIntrinsicProvider is state_exec-typed via -// forward-decls. +// Host-contributed program-lane intrinsics registry (§ nav verbs et al.). ActionIntrinsicProvider +// is state_exec-typed via forward-decls. namespace { std::mutex &action_intrinsics_mutex() { static std::mutex m; @@ -273,7 +259,6 @@ void clear_action_intrinsics() { action_intrinsics_registry().clear(); } -#ifdef CVC_STATE_EXEC namespace { // Run `script` as a FULL-ENV state_exec program chrooted to `prefix`, bounded by (max_steps, // max_seconds, max_bytes). Returns "" on a clean, normal finish, else a human diagnostic (no @@ -332,7 +317,6 @@ std::string run_scoped_program(cvc::app &app, const std::string &prefix, const s } } } // namespace -#endif // CVC_STATE_EXEC bool run_init(cvc::app &app, const std::string &prefix, const std::string &script, std::vector *errors) { @@ -342,13 +326,6 @@ bool run_init(cvc::app &app, const std::string &prefix, const std::string &scrip }; if (script.empty()) return true; // nothing to run -#ifndef CVC_STATE_EXEC - (void)app; - (void)prefix; - err("ari: init: script present but this libcvc was built without state_exec " - "(CVC_STATE_EXEC=OFF) — the script did not run"); - return false; -#else // A load-time seed/compute is generous — well above the per-tick action budget (§7.4). static constexpr uint64_t kInitMaxSteps = 10'000'000; static constexpr double kInitMaxSeconds = 5.0; @@ -358,11 +335,9 @@ bool run_init(cvc::app &app, const std::string &prefix, const std::string &scrip return false; } return true; -#endif } // --- §4 read-lane: the per-frame reactive predicate evaluator ---------------- -#ifdef CVC_STATE_EXEC namespace { namespace se = cvc::state_exec; @@ -616,7 +591,6 @@ class ReactiveEngine { std::unordered_map> compiled_; }; } // namespace -#endif // CVC_STATE_EXEC struct Runtime::Impl { cvc::app &app; @@ -627,10 +601,8 @@ struct Runtime::Impl { // ictx. Default (enforce=false, empty) = permissive; set_channel_policy fills it from a // LoadResult. Pointed-to by ictx.channels (non-owning), so it must outlive the contexts — it // lives here in the Impl, which owns the live_actions_ / residents that reference it. Declared - // before them so it destructs after them. Only exists with state_exec (no ictx to point at it). -#ifdef CVC_STATE_EXEC + // before them so it destructs after them. cvc::state_exec::channel_policy channel_policy_; -#endif Widget root; Widget pending; @@ -654,11 +626,8 @@ struct Runtime::Impl { // §4 read-lane: the reactive predicate evaluator (lazily built on first use so a UI // with no reactive fields pays nothing), plus de-duplicated diagnostics surfaced by - // take_reactive_warnings(). The warnings live regardless of state_exec (the OFF path - // also warns once). reactive_warned keeps the dedup set across drains. -#ifdef CVC_STATE_EXEC + // take_reactive_warnings(). reactive_warned keeps the dedup set across drains. std::unique_ptr reactive; -#endif std::vector reactive_warnings; std::set reactive_warned; int frame_instances = 0; // §3: repeat instances emitted this frame (bounds many repeats) @@ -672,7 +641,6 @@ struct Runtime::Impl { Impl(cvc::app &a, std::string p) : app(a), prefix(std::move(p)) {} ~Impl(); -#ifdef CVC_STATE_EXEC // A submitted `on:` program's persistent context. The intrinsics capture &ictx by // pointer, so — unlike the load-time init: lane's one-scope run — an action that // suspends (await/sleep/msg-recv) needs its context kept alive across frames until the @@ -719,7 +687,6 @@ struct Runtime::Impl { // Deliver one input event: serialize it to a dict and post_message it (NO coalesce — a burst in // one frame must all arrive) onto the key channel (keyboard) or pointer channel (mouse). void deliver_input(const InputEvent &ev); -#endif // §12: the active mount scope during emit. Empty stack = the document prefix; each entry is // a composed sub-prefix pushed when the walk enters a mounted subtree (Widget::scope) and @@ -759,7 +726,6 @@ struct Runtime::Impl { return resolve_bind(current_prefix(), bind); } -#ifdef CVC_STATE_EXEC // Lazily build the read-lane engine (a UI with no reactive fields pays nothing), then point // it at the active mount scope so a mounted fragment's predicates read its own sub-prefix, // matching where its binds resolve. Every reactive helper goes through here. @@ -769,7 +735,6 @@ struct Runtime::Impl { reactive->set_root_path(app, current_prefix()); return *reactive; } -#endif void enqueue(const std::string &event) { if (!event.empty()) @@ -793,12 +758,11 @@ struct Runtime::Impl { void render(); // §4 read-lane: is `w` shown this frame? True when it has no visible_when; otherwise - // the predicate's result (fail-safe HIDDEN on a state_exec build, fail-safe SHOWN on a - // build without state_exec — hiding every reactive widget would gut a minimal build). + // the predicate's result (fail-safe HIDDEN on a broken predicate). bool visible(const Widget &w); // §4 read-lane: is `w` disabled (greyed, non-interactive) this frame? True when // enabled_when is falsy OR disabled_when is truthy. Fail-safe DISABLED on a broken - // predicate; a build without state_exec leaves it enabled (+ warns once). + // predicate. bool disabled(const Widget &w); // §4 read-lane: evaluate a computed-value expression `expr` to a display string. // Fail-safe EMPTY string on a broken predicate (§4.1 fmt→""); warns once. @@ -821,22 +785,15 @@ struct Runtime::Impl { bool Runtime::Impl::visible(const Widget &w) { if (w.visible_when.empty()) return true; -#ifdef CVC_STATE_EXEC const ReactiveEngine::Outcome o = ensure_reactive().eval_bool(w.visible_when, /*dflt=*/false); if (!o.error.empty()) warn_once(o.error); return o.value; -#else - warn_once("ari: visible_when on '" + (w.label.empty() ? w.id : w.label) + - "' ignored — this libcvc was built without state_exec (CVC_STATE_EXEC=OFF)"); - return true; -#endif } bool Runtime::Impl::disabled(const Widget &w) { if (w.enabled_when.empty() && w.disabled_when.empty()) return false; -#ifdef CVC_STATE_EXEC ReactiveEngine &re = ensure_reactive(); // enabled_when falsy -> disabled (fail-safe dflt=false: a broken predicate disables). if (!w.enabled_when.empty()) { @@ -855,55 +812,33 @@ bool Runtime::Impl::disabled(const Widget &w) { return true; } return false; -#else - warn_once("ari: enabled_when/disabled_when on '" + (w.label.empty() ? w.id : w.label) + - "' ignored — this libcvc was built without state_exec (CVC_STATE_EXEC=OFF)"); - return false; // can't evaluate -> leave it functional -#endif } std::string Runtime::Impl::eval_text(const std::string &expr) { -#ifdef CVC_STATE_EXEC const ReactiveEngine::StringOutcome o = ensure_reactive().eval_string(expr, /*dflt=*/std::string()); if (!o.error.empty()) warn_once(o.error); return o.value; -#else - warn_once("ari: computed text expr ignored — this libcvc was built without state_exec " - "(CVC_STATE_EXEC=OFF)"); - return std::string(); -#endif } std::string Runtime::Impl::eval_text_or_literal(const std::string &expr) { -#ifdef CVC_STATE_EXEC const ReactiveEngine::StringOutcome o = ensure_reactive().eval_string(expr, /*dflt=*/std::string()); // Success -> the computed value; failure -> the literal verbatim (no warning: a tooltip that // starts with '(' is far more likely plain text than a broken expression). return o.error.empty() ? o.value : expr; -#else - return expr; // no state_exec -> show the literal -#endif } std::vector Runtime::Impl::eval_options(const std::string &expr) { -#ifdef CVC_STATE_EXEC const ReactiveEngine::StringListOutcome o = ensure_reactive().eval_string_list(expr); if (!o.error.empty()) warn_once(o.error); return o.value; -#else - warn_once("ari: computed options ignored — this libcvc was built without state_exec " - "(CVC_STATE_EXEC=OFF)"); - return {}; -#endif } int Runtime::Impl::eval_count(const std::string &expr) { constexpr int64_t kMaxRepeat = 4096; // a UI with thousands of repeated widgets is pathological -#ifdef CVC_STATE_EXEC const ReactiveEngine::IntOutcome o = ensure_reactive().eval_int(expr, /*dflt=*/0); if (!o.error.empty()) warn_once(o.error); @@ -916,11 +851,6 @@ int Runtime::Impl::eval_count(const std::string &expr) { n = kMaxRepeat; } return static_cast(n); -#else - (void)kMaxRepeat; - warn_once("ari: repeat ignored — this libcvc was built without state_exec (CVC_STATE_EXEC=OFF)"); - return 0; -#endif } void Runtime::Impl::emit_children(const Widget &w) { @@ -1348,10 +1278,8 @@ void Runtime::Impl::render() { planted_holes.clear(); wired_mounts.clear(); } -#ifdef CVC_STATE_EXEC if (reactive) reactive->begin_frame(); // §4: reset the per-frame reactive eval budget -#endif frame_instances = 0; // §3: reset the per-frame repeat-expansion budget scope_stack.clear(); // §12: start every frame at the document scope (RAII keeps it balanced; // this is belt-and-suspenders so one bad frame can't leak into the next) @@ -1365,16 +1293,13 @@ void Runtime::Impl::render() { std::atomic Runtime::Impl::owner_seq{0}; Runtime::Impl::~Impl() { -#ifdef CVC_STATE_EXEC // Reap this document's whole process group so a parked (await/sleep/msg-recv) action never // lingers on the shared app scheduler after its Runtime is gone. Only touch the scheduler if // we ever used it (exec_scheduler() lazily builds it — don't force it at teardown otherwise). if (used_scheduler_) app.exec_scheduler().kill_owner(owner_); -#endif } -#ifdef CVC_STATE_EXEC void Runtime::Impl::submit_action(const QueuedAction &action) { namespace se = cvc::state_exec; const std::string &action_prefix = action.prefix; @@ -1551,7 +1476,6 @@ void Runtime::Impl::deliver_input(const InputEvent &ev) { app.exec_scheduler().post_message(keyboard ? key_channel() : pointer_channel(), se::make_dict(std::move(f))); } -#endif // CVC_STATE_EXEC Runtime::Runtime(cvc::app &app, std::string prefix) : m_(new Impl(app, std::move(prefix))) {} @@ -1569,7 +1493,6 @@ void Runtime::on(std::string event, std::function handler) { } void Runtime::set_tick_program(std::string script) { -#ifdef CVC_STATE_EXEC // Replacing the program kills any running resident so the new body starts fresh next drain. if (m_->tick_resident_pid_ >= 0) { m_->app.exec_scheduler().kill(m_->tick_resident_pid_); @@ -1577,40 +1500,28 @@ void Runtime::set_tick_program(std::string script) { m_->tick_resident_pid_ = -1; } m_->tick_script_ = std::move(script); -#else - (void)script; // residents need state_exec — no-op otherwise -#endif } void Runtime::set_key_program(std::string script) { -#ifdef CVC_STATE_EXEC if (m_->key_resident_pid_ >= 0) { m_->app.exec_scheduler().kill(m_->key_resident_pid_); m_->key_resident_.reset(); m_->key_resident_pid_ = -1; } m_->key_script_ = std::move(script); -#else - (void)script; -#endif } void Runtime::set_pointer_program(std::string script) { -#ifdef CVC_STATE_EXEC if (m_->pointer_resident_pid_ >= 0) { m_->app.exec_scheduler().kill(m_->pointer_resident_pid_); m_->pointer_resident_.reset(); m_->pointer_resident_pid_ = -1; } m_->pointer_script_ = std::move(script); -#else - (void)script; -#endif } void Runtime::set_channel_policy(std::vector declared, std::vector global, bool strict, bool quiet) { -#ifdef CVC_STATE_EXEC // Installed on every action/resident ictx via the &channel_policy_ pointer set in submit_action / // submit_resident, so this applies from the next activation on. `strict` is the enforce switch; // warn/off pass strict=false (the load-time lint already surfaced those), leaving runtime @@ -1620,24 +1531,14 @@ void Runtime::set_channel_policy(std::vector declared, std::vector< m_->channel_policy_.global = std::move(global); m_->channel_policy_.enforce = strict; m_->channel_policy_.quiet = quiet; -#else - (void)declared; - (void)global; - (void)strict; - (void)quiet; -#endif } void Runtime::post_input(const InputEvent &ev) { -#ifdef CVC_STATE_EXEC // Submit the input residents on first use so the delivered event is consumed, not dropped, then // deliver it. The pump in drain() steps the resident; feed input BEFORE drain() for same-frame - // delivery (see the header contract). A no-op without state_exec. + // delivery (see the header contract). m_->ensure_input_residents(); m_->deliver_input(ev); -#else - (void)ev; -#endif } void Runtime::render() { m_->render(); } @@ -1651,24 +1552,17 @@ void Runtime::drain() { // A program action (`on:` starting with '(', like a computed `bind:`/`tooltip:`) runs through // state_exec — the north-star lane: a flag toggle or reset is pure .ari, no C++ handler. if (is_expr(a.event)) { // a program on: (whitespace-tolerant, exactly like a computed bind:) -#ifdef CVC_STATE_EXEC // SUBMIT it to the app-wide scheduler (owner-tagged, chrooted to the action's mount prefix) // and return — no run-to-completion inside drain(). The per-frame pump below advances it; a // quick action still finishes this drain, while an (await …)/(msg-recv …)/(sleep …) parks // and resumes on a later frame instead of blocking the UI thread (§4.7). m_->submit_action(a); -#else - m_->warn_once("ari: on: a program action needs state_exec (CVC_STATE_EXEC=OFF); it did not " - "run [" + - a.event + "]"); -#endif continue; } auto it = m_->handlers.find(a.event); // a bare event name -> the host C++ handler seam if (it != m_->handlers.end() && it->second) it->second(); } -#ifdef CVC_STATE_EXEC // §7.1 resident on:tick: submit the resident once (idempotent), then post it this frame's tick. m_->ensure_tick_resident(); m_->post_tick(); @@ -1679,7 +1573,6 @@ void Runtime::drain() { // bounded slice, then sweep the finished ones. Runs once used_scheduler_ latches. if (m_->used_scheduler_) m_->pump_and_sweep_actions(); -#endif } std::vector Runtime::take_reactive_warnings() { diff --git a/src/cvc/ariadne/loader.cpp b/src/cvc/ariadne/loader.cpp index 7a744e0b..021cccbf 100644 --- a/src/cvc/ariadne/loader.cpp +++ b/src/cvc/ariadne/loader.cpp @@ -16,12 +16,10 @@ #include #include // §12/§13 import: resolve library URIs (file/state/http) #include // CVC_VERSION_STRING (generated from project(VERSION)) -#ifdef CVC_STATE_EXEC // §12 channel lint: parse a script (parser.h) and walk its value_t / symbol / list_ptr AST // (types.h) for the static msg-* channel references. #include #include -#endif #include // §12 import: dirname of a resolved library for nested bases #include #include @@ -1633,9 +1631,7 @@ std::vector parse_customs(Ctx &ctx, const YAML::Node &c) { // §12 channel lint (A) — the fail-fast, load-time pass over a document's STATIC msg-* channel // references, gated on the document declaring a `channels:` block. Only literal channel names are -// visible here; a dynamic `(msg-recv (expr))` name is the runtime policy's job (a follow-up). No-op -// without state_exec (there are no program lanes to lint, and the parser is unavailable). -#ifdef CVC_STATE_EXEC +// visible here; a dynamic `(msg-recv (expr))` name is the runtime policy's job (a follow-up). // Walk a parsed script's value_t tree, collecting the string-literal FIRST arg of every // (msg-send|msg-recv|msg-pending …) call — the static channel references. Depth-bounded. void collect_channel_refs(const cvc::state_exec::value_t &v, std::vector &out, @@ -1676,13 +1672,11 @@ void refs_from_widget(const Widget &w, std::vector &out) { for (const Widget &c : w.children) refs_from_widget(c, out); } -#endif // Run the §12 channel lint against a fully-built LoadResult (root + scripts populated). Sets // r.error on a STRICT violation (fails the load), else appends warnings. A no-op unless the doc // declared a `channels:` block and lint.channels != Off. void lint_channels(Ctx &ctx, LoadResult &r) { -#ifdef CVC_STATE_EXEC if (!r.has_channels_block) return; // no declaration -> enforcement is not active for this document if (r.lint.channels == LintConfig::Mode::Off) { @@ -1727,10 +1721,6 @@ void lint_channels(Ctx &ctx, LoadResult &r) { return; } } -#else - (void)ctx; - (void)r; -#endif } // Parse an already-loaded YAML node into a LoadResult, applying the meta gate. `base_dir` is diff --git a/src/cvc/ariadne/net_intrinsics.cpp b/src/cvc/ariadne/net_intrinsics.cpp index 93e008c6..87004ce0 100644 --- a/src/cvc/ariadne/net_intrinsics.cpp +++ b/src/cvc/ariadne/net_intrinsics.cpp @@ -1,22 +1,19 @@ // Ariadne — async HTTP host intrinsics for the state_exec program lanes (roadmap §13.8). See -// net_intrinsics.h. The whole implementation is gated on CVC_STATE_EXEC (no program lanes without -// it) and on a compiled cvc::net backend (cvc::net::have_http_backend()). +// net_intrinsics.h. The (http-get*) verbs are gated on a compiled cvc::net backend +// (cvc::net::have_http_backend()). -#include // register_action_intrinsics, have_state_exec +#include +#include +#include // register_action_intrinsics #include #include // resolve() — the generic (fetch uri) async resolver #include #include // launch_pool_task — the shared offload-and-park primitive -#include - -#ifdef CVC_STATE_EXEC - -#include -#include #include // exec_scheduler().post_message #include // register_fn — bind the host verb into the lanes #include // resolve_channel_key — scope the reply channel #include // value_t, make_dict/make_list/make_bytes +#include #include #include #include @@ -264,8 +261,6 @@ std::string launch_uri_fetch(cvc::app &app, const std::string &root, } // namespace void register_net_intrinsics(cvc::app &app) { - if (!have_state_exec()) - return; // Warm the lazy per-app singletons on THIS thread before any background worker touches them, so a // worker never races their first construction (the nav_compute discipline). app.computePool(); @@ -346,15 +341,3 @@ void register_net_intrinsics(cvc::app &app) { } // namespace ariadne } // namespace cvc - -#else // !CVC_STATE_EXEC - -namespace cvc { -namespace ariadne { -void register_net_intrinsics(cvc::app & /*app*/) { - // No program lanes without state_exec — nothing to register. -} -} // namespace ariadne -} // namespace cvc - -#endif // CVC_STATE_EXEC diff --git a/src/cvc/core/async_task.cpp b/src/cvc/core/async_task.cpp index 6ad7c965..1e787c8c 100644 --- a/src/cvc/core/async_task.cpp +++ b/src/cvc/core/async_task.cpp @@ -1,13 +1,10 @@ // cvc::async_task — offload a blocking kernel to the compute pool and resume a parked state_exec // process when it finishes. See inc/cvc/core/async_task.h for the contract and the two rules. -#include - -#ifdef CVC_STATE_EXEC - #include #include #include +#include #include // exec_scheduler().post_message #include // resolve_channel_key, make_future, park_on_channel #include @@ -64,5 +61,3 @@ se::value_t future_pool_task(app &a, se::async_scheduler &sched, const std::stri } } // namespace cvc - -#endif // CVC_STATE_EXEC diff --git a/src/cvc/tests/CMakeLists.txt b/src/cvc/tests/CMakeLists.txt index cde17d06..f1fadbc2 100644 --- a/src/cvc/tests/CMakeLists.txt +++ b/src/cvc/tests/CMakeLists.txt @@ -54,25 +54,23 @@ add_executable(state_list_test state_list_test.cpp) if(CVC_STATE_MEMORY_MANAGER) add_executable(state_memory_manager_test state_memory_manager_test.cpp) endif() -if(CVC_STATE_EXEC) - add_executable(state_exec_types_test state_exec_types_test.cpp) - add_executable(state_exec_parser_test state_exec_parser_test.cpp) - add_executable(state_exec_builtins_test state_exec_builtins_test.cpp) - add_executable(state_exec_evaluator_test state_exec_evaluator_test.cpp) - add_executable(state_exec_stackless_evaluator_test state_exec_stackless_evaluator_test.cpp) - add_executable(state_exec_codec_test state_exec_codec_test.cpp) - add_executable(state_exec_unicode_test state_exec_unicode_test.cpp) - add_executable(state_exec_async_test state_exec_async_test.cpp) - add_executable(state_exec_scheduler_test state_exec_scheduler_test.cpp) - add_executable(state_exec_intrinsics_test state_exec_intrinsics_test.cpp) - add_executable(state_exec_coordinator_test state_exec_coordinator_test.cpp) - add_executable(state_exec_bench_test state_exec_bench_test.cpp) - add_executable(state_exec_integration_test state_exec_integration_test.cpp) - add_executable(state_exec_advanced_integration_test state_exec_advanced_integration_test.cpp) - add_executable(state_exec_gaps_test state_exec_gaps_test.cpp) - if(NOT WIN32) - add_executable(state_exec_multiprocess_test state_exec_multiprocess_test.cpp) - endif() +add_executable(state_exec_types_test state_exec_types_test.cpp) +add_executable(state_exec_parser_test state_exec_parser_test.cpp) +add_executable(state_exec_builtins_test state_exec_builtins_test.cpp) +add_executable(state_exec_evaluator_test state_exec_evaluator_test.cpp) +add_executable(state_exec_stackless_evaluator_test state_exec_stackless_evaluator_test.cpp) +add_executable(state_exec_codec_test state_exec_codec_test.cpp) +add_executable(state_exec_unicode_test state_exec_unicode_test.cpp) +add_executable(state_exec_async_test state_exec_async_test.cpp) +add_executable(state_exec_scheduler_test state_exec_scheduler_test.cpp) +add_executable(state_exec_intrinsics_test state_exec_intrinsics_test.cpp) +add_executable(state_exec_coordinator_test state_exec_coordinator_test.cpp) +add_executable(state_exec_bench_test state_exec_bench_test.cpp) +add_executable(state_exec_integration_test state_exec_integration_test.cpp) +add_executable(state_exec_advanced_integration_test state_exec_advanced_integration_test.cpp) +add_executable(state_exec_gaps_test state_exec_gaps_test.cpp) +if(NOT WIN32) + add_executable(state_exec_multiprocess_test state_exec_multiprocess_test.cpp) endif() add_executable(state_delta_codec_test state_delta_codec_test.cpp) add_executable(state_volume_codec_test state_volume_codec_test.cpp) @@ -290,27 +288,25 @@ if(NOT WIN32) ) endif() -if(CVC_STATE_EXEC) - list(APPEND TEST_TARGETS - state_exec_types_test - state_exec_parser_test - state_exec_builtins_test - state_exec_evaluator_test - state_exec_stackless_evaluator_test - state_exec_codec_test - state_exec_unicode_test - state_exec_async_test - state_exec_scheduler_test - state_exec_intrinsics_test - state_exec_coordinator_test - state_exec_bench_test - state_exec_integration_test - state_exec_advanced_integration_test - state_exec_gaps_test - ) - if(NOT WIN32) - list(APPEND TEST_TARGETS state_exec_multiprocess_test) - endif() +list(APPEND TEST_TARGETS + state_exec_types_test + state_exec_parser_test + state_exec_builtins_test + state_exec_evaluator_test + state_exec_stackless_evaluator_test + state_exec_codec_test + state_exec_unicode_test + state_exec_async_test + state_exec_scheduler_test + state_exec_intrinsics_test + state_exec_coordinator_test + state_exec_bench_test + state_exec_integration_test + state_exec_advanced_integration_test + state_exec_gaps_test +) +if(NOT WIN32) + list(APPEND TEST_TARGETS state_exec_multiprocess_test) endif() if(CVC_STATE_MEMORY_MANAGER) @@ -1105,111 +1101,109 @@ target_link_libraries(state_telemetry_aggregator_test GTest::gtest_main ) -if(CVC_STATE_EXEC) - target_link_libraries(state_exec_parser_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_parser_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_types_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_types_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_builtins_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_builtins_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_evaluator_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_evaluator_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_stackless_evaluator_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_stackless_evaluator_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_codec_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) - target_link_libraries(state_exec_unicode_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_codec_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) +target_link_libraries(state_exec_unicode_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_async_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_async_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_scheduler_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_scheduler_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_intrinsics_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_intrinsics_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_coordinator_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_coordinator_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_bench_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_bench_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_integration_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) +target_link_libraries(state_exec_integration_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) - target_link_libraries(state_exec_advanced_integration_test +target_link_libraries(state_exec_advanced_integration_test + PRIVATE + cvc + GTest::gtest + GTest::gtest_main +) +if(TARGET state_exec_multiprocess_test) + target_link_libraries(state_exec_multiprocess_test PRIVATE cvc GTest::gtest GTest::gtest_main ) - if(TARGET state_exec_multiprocess_test) - target_link_libraries(state_exec_multiprocess_test - PRIVATE - cvc - GTest::gtest - GTest::gtest_main - ) - endif() endif() if(CVC_STATE_MEMORY_MANAGER) @@ -1421,24 +1415,22 @@ target_compile_features(state_transparent_link_index_test PRIVATE cxx_std_17) target_compile_features(state_sync_adapter_link_forwarding_test PRIVATE cxx_std_17) target_compile_features(state_cross_cluster_link_test PRIVATE cxx_std_17) target_compile_features(state_link_bench_test PRIVATE cxx_std_17) -if(CVC_STATE_EXEC) - target_compile_features(state_exec_parser_test PRIVATE cxx_std_20) - target_compile_features(state_exec_types_test PRIVATE cxx_std_20) - target_compile_features(state_exec_builtins_test PRIVATE cxx_std_20) - target_compile_features(state_exec_evaluator_test PRIVATE cxx_std_20) - target_compile_features(state_exec_stackless_evaluator_test PRIVATE cxx_std_20) - target_compile_features(state_exec_codec_test PRIVATE cxx_std_20) - target_compile_features(state_exec_unicode_test PRIVATE cxx_std_20) - target_compile_features(state_exec_async_test PRIVATE cxx_std_20) - target_compile_features(state_exec_scheduler_test PRIVATE cxx_std_20) - target_compile_features(state_exec_intrinsics_test PRIVATE cxx_std_20) - target_compile_features(state_exec_coordinator_test PRIVATE cxx_std_20) - target_compile_features(state_exec_bench_test PRIVATE cxx_std_20) - target_compile_features(state_exec_integration_test PRIVATE cxx_std_20) - target_compile_features(state_exec_advanced_integration_test PRIVATE cxx_std_20) - if(TARGET state_exec_multiprocess_test) - target_compile_features(state_exec_multiprocess_test PRIVATE cxx_std_20) - endif() +target_compile_features(state_exec_parser_test PRIVATE cxx_std_20) +target_compile_features(state_exec_types_test PRIVATE cxx_std_20) +target_compile_features(state_exec_builtins_test PRIVATE cxx_std_20) +target_compile_features(state_exec_evaluator_test PRIVATE cxx_std_20) +target_compile_features(state_exec_stackless_evaluator_test PRIVATE cxx_std_20) +target_compile_features(state_exec_codec_test PRIVATE cxx_std_20) +target_compile_features(state_exec_unicode_test PRIVATE cxx_std_20) +target_compile_features(state_exec_async_test PRIVATE cxx_std_20) +target_compile_features(state_exec_scheduler_test PRIVATE cxx_std_20) +target_compile_features(state_exec_intrinsics_test PRIVATE cxx_std_20) +target_compile_features(state_exec_coordinator_test PRIVATE cxx_std_20) +target_compile_features(state_exec_bench_test PRIVATE cxx_std_20) +target_compile_features(state_exec_integration_test PRIVATE cxx_std_20) +target_compile_features(state_exec_advanced_integration_test PRIVATE cxx_std_20) +if(TARGET state_exec_multiprocess_test) + target_compile_features(state_exec_multiprocess_test PRIVATE cxx_std_20) endif() if(CVC_STATE_MEMORY_MANAGER) target_compile_features(state_memory_manager_test PRIVATE cxx_std_17) @@ -1714,29 +1706,27 @@ cvc_discover_tests(state_transparent_link_index_test) cvc_discover_tests(state_sync_adapter_link_forwarding_test) cvc_discover_tests(state_cross_cluster_link_test) cvc_discover_tests(state_link_bench_test) -if(CVC_STATE_EXEC) - cvc_discover_tests(state_exec_parser_test) - cvc_discover_tests(state_exec_types_test) - cvc_discover_tests(state_exec_builtins_test) - cvc_discover_tests(state_exec_evaluator_test) - cvc_discover_tests(state_exec_stackless_evaluator_test) - cvc_discover_tests(state_exec_codec_test) - cvc_discover_tests(state_exec_unicode_test) - cvc_discover_tests(state_exec_async_test) - cvc_discover_tests(state_exec_scheduler_test) - cvc_discover_tests(state_exec_intrinsics_test) - cvc_discover_tests(state_exec_coordinator_test) - cvc_discover_tests(state_exec_bench_test) - cvc_discover_tests(state_exec_integration_test) - cvc_discover_tests(state_exec_advanced_integration_test) - if(TARGET state_exec_multiprocess_test) - # Forks real OS processes and replicates state over Unix-domain-socket IPC - # with internal pump deadlines. Under `ctest --parallel` on a loaded CI - # runner the forked children get starved and miss those windows, so - # StateTreeReplication intermittently fails all `until-pass` retries. - # RUN_SERIAL keeps this suite off the shared parallel scheduler. - cvc_discover_tests(state_exec_multiprocess_test PROPERTIES RUN_SERIAL TRUE) - endif() +cvc_discover_tests(state_exec_parser_test) +cvc_discover_tests(state_exec_types_test) +cvc_discover_tests(state_exec_builtins_test) +cvc_discover_tests(state_exec_evaluator_test) +cvc_discover_tests(state_exec_stackless_evaluator_test) +cvc_discover_tests(state_exec_codec_test) +cvc_discover_tests(state_exec_unicode_test) +cvc_discover_tests(state_exec_async_test) +cvc_discover_tests(state_exec_scheduler_test) +cvc_discover_tests(state_exec_intrinsics_test) +cvc_discover_tests(state_exec_coordinator_test) +cvc_discover_tests(state_exec_bench_test) +cvc_discover_tests(state_exec_integration_test) +cvc_discover_tests(state_exec_advanced_integration_test) +if(TARGET state_exec_multiprocess_test) + # Forks real OS processes and replicates state over Unix-domain-socket IPC + # with internal pump deadlines. Under `ctest --parallel` on a loaded CI + # runner the forked children get starved and miss those windows, so + # StateTreeReplication intermittently fails all `until-pass` retries. + # RUN_SERIAL keeps this suite off the shared parallel scheduler. + cvc_discover_tests(state_exec_multiprocess_test PROPERTIES RUN_SERIAL TRUE) endif() if(CVC_STATE_MEMORY_MANAGER) cvc_discover_tests(state_memory_manager_test) diff --git a/src/cvc/tests/ariadne_loader_test.cpp b/src/cvc/tests/ariadne_loader_test.cpp index 096e16f7..f138e1cc 100644 --- a/src/cvc/tests/ariadne_loader_test.cpp +++ b/src/cvc/tests/ariadne_loader_test.cpp @@ -3193,8 +3193,6 @@ TEST(AriadneChannels, MountGrantLowersToLinkHole) { TEST(AriadneChannelLint, StrictUndeclaredChannelFailsLoad) { SKIP_WITHOUT_YAML(); - if (!have_state_exec()) - GTEST_SKIP(); LoadResult r = load_string(R"( channels: - nav.done @@ -3210,8 +3208,6 @@ TEST(AriadneChannelLint, StrictUndeclaredChannelFailsLoad) { TEST(AriadneChannelLint, DeclaredChannelPasses) { SKIP_WITHOUT_YAML(); - if (!have_state_exec()) - GTEST_SKIP(); LoadResult r = load_string(R"( channels: - nav.done @@ -3226,8 +3222,6 @@ TEST(AriadneChannelLint, DeclaredChannelPasses) { TEST(AriadneChannelLint, WarnModeWarnsNotFails) { SKIP_WITHOUT_YAML(); - if (!have_state_exec()) - GTEST_SKIP(); LoadResult r = load_string(R"( lint: channels: warn @@ -3248,8 +3242,6 @@ TEST(AriadneChannelLint, WarnModeWarnsNotFails) { TEST(AriadneChannelLint, OffModeSkipsCheck) { SKIP_WITHOUT_YAML(); - if (!have_state_exec()) - GTEST_SKIP(); LoadResult r = load_string(R"( lint: channels: off @@ -3266,8 +3258,6 @@ TEST(AriadneChannelLint, OffModeSkipsCheck) { TEST(AriadneChannelLint, GlobalRefNeedsDeclaredGlobal) { SKIP_WITHOUT_YAML(); - if (!have_state_exec()) - GTEST_SKIP(); LoadResult ok = load_string(R"( channels: - channel: app.quit @@ -3295,8 +3285,6 @@ TEST(AriadneChannelLint, GlobalRefNeedsDeclaredGlobal) { TEST(AriadneChannelLint, HashChannelExempt) { SKIP_WITHOUT_YAML(); - if (!have_state_exec()) - GTEST_SKIP(); LoadResult r = load_string(R"( channels: - nav.done @@ -3311,8 +3299,6 @@ TEST(AriadneChannelLint, HashChannelExempt) { TEST(AriadneChannelLint, NoChannelsBlockNoEnforcement) { SKIP_WITHOUT_YAML(); - if (!have_state_exec()) - GTEST_SKIP(); LoadResult r = load_string(R"( windows: - window: W @@ -3325,8 +3311,6 @@ TEST(AriadneChannelLint, NoChannelsBlockNoEnforcement) { TEST(AriadneChannelLint, InitScriptLinted) { SKIP_WITHOUT_YAML(); - if (!have_state_exec()) - GTEST_SKIP(); LoadResult r = load_string("channels:\n - a\ninit: '(msg-send \"b\" \"x\")'\nwindows: []\n"); EXPECT_FALSE(r.ok); // the init: script is linted too; "b" is undeclared -> strict fail EXPECT_NE(r.error.find("'b'"), std::string::npos); diff --git a/src/cvc/tests/ariadne_runtime_test.cpp b/src/cvc/tests/ariadne_runtime_test.cpp index 572029ac..1fec596a 100644 --- a/src/cvc/tests/ariadne_runtime_test.cpp +++ b/src/cvc/tests/ariadne_runtime_test.cpp @@ -281,8 +281,6 @@ TEST(AriadneRuntime, ActionsDrainOffTheWalk) { // a flag toggle / reset is pure .ari. The program is chrooted to the widget's prefix, so // (state-set "paused") writes .paused, the same key a widget `bind: paused` resolves to. TEST(AriadneAction, ProgramOnTogglesStateThroughStateExec) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; cvc::app app; Runtime rt(app, "ui.demo"); MockBackend mb; @@ -304,8 +302,6 @@ TEST(AriadneAction, ProgramOnTogglesStateThroughStateExec) { // A program `on:` may sequence several state writes in one action (a reset button). TEST(AriadneAction, ProgramOnRunsMultiStatementReset) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -324,8 +320,6 @@ TEST(AriadneAction, ProgramOnRunsMultiStatementReset) { // via the thread-safe exec_scheduler().post_message ingress. This is the marquee async story // (a compute-pool worker waking a parked .ari action) exercised through the real Runtime. TEST(AriadneAction, ProgramActionParksOnMsgRecvAndResumesWhenWorkerDelivers) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; cvc::app app; Runtime rt(app, ""); // app-root prefix so the channel/state paths are used as-is MockBackend mb; @@ -360,8 +354,6 @@ TEST(AriadneAction, ProgramActionParksOnMsgRecvAndResumesWhenWorkerDelivers) { // the value the worker delivered. deliver_to_receivers patches the parent frame's pending result // slot (where pop_frame pushed msg-recv's nil placeholder), so the resumed state-set applies it. TEST(AriadneAction, ProgramActionCapturesDeliveredValueFromMsgRecv) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -392,8 +384,6 @@ TEST(AriadneAction, ProgramActionCapturesDeliveredValueFromMsgRecv) { // background-thread → parallel_for → post_message); here we hand-roll a joinable std::thread so the // test can join() deterministically before asserting (compute_async is fire-and-forget by design). TEST(AriadneAction, IntrinsicRunsNavStepOnComputePoolAndWakesMsgRecv) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; namespace se = cvc::state_exec; cvc::app app; app.computePool(); // warm the lazy per-app singletons on THIS thread before a worker touches @@ -447,8 +437,6 @@ TEST(AriadneAction, IntrinsicRunsNavStepOnComputePoolAndWakesMsgRecv) { // per drain (parking between frames), NOT a fresh action each frame. Proven by re-firing: reset a // flag the body sets, and the next drain sets it again from the SAME resident. TEST(AriadneResident, TickProgramRunsOncePerDrain) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -467,8 +455,6 @@ TEST(AriadneResident, TickProgramRunsOncePerDrain) { // set_tick_program("") clears the resident: after clearing, a drain no longer re-fires the body. TEST(AriadneResident, ClearingTickProgramStopsIt) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -487,8 +473,6 @@ TEST(AriadneResident, ClearingTickProgramStopsIt) { // reading the delivered event dict with (get-attr event ...). post_input BEFORE drain -> delivered // this frame (feed-before-drain contract). Keyboard events feed the key resident. TEST(AriadneInput, KeyProgramReceivesPostedEvent) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -507,8 +491,6 @@ TEST(AriadneInput, KeyProgramReceivesPostedEvent) { // A burst of events in one frame all deliver (FIFO — no coalescing on the input channel): three // keys posted before one drain, and the resident drains all three (last wins in state). TEST(AriadneInput, KeyBurstAllDeliveredInOrder) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -526,8 +508,6 @@ TEST(AriadneInput, KeyBurstAllDeliveredInOrder) { // Mouse events feed the pointer resident (a separate channel/handler); the body reads coords. TEST(AriadneInput, PointerProgramReceivesMouseEvent) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -551,8 +531,6 @@ TEST(AriadneInput, PointerProgramReceivesMouseEvent) { // and on wasm). Fired like a Button's on: (queued in render(), run in drain()) — NOT the SDL // document-level stream. This is the natively-working widget input path. TEST(AriadneInput, WidgetOnClickFiresWhenBackendReportsClick) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -672,8 +650,6 @@ TEST(AriadneInput, WidgetOnDragStartEndFireOnEdges) { // the item's pointer coords, read with (get-attr event "..."). Assert on button (int -> a stable // string), like the on_pointer test, to avoid double-format brittleness. TEST(AriadneInput, WidgetPointerProgramReceivesEventScope) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -721,8 +697,6 @@ TEST(AriadneRuntime, ImageWidgetResolvesNameAndFallsBackToText) { // The host-intrinsic seam: a host binds a native fn `(host-bump)` into the program lanes, and a // program on: calls it — so a .ari program can invoke a host capability (e.g. a nav verb) inline. TEST(AriadneAction, HostIntrinsicCallableFromProgram) { - if (!have_state_exec()) - GTEST_SKIP(); namespace se = cvc::state_exec; int calls = 0; register_action_intrinsics( @@ -749,8 +723,6 @@ TEST(AriadneAction, HostIntrinsicCallableFromProgram) { // A broken program action fails SAFE: it never throws out of drain(), and it surfaces a one-time // warning (mirroring the read-lane's fail-safe policy) rather than silently doing nothing. TEST(AriadneAction, ProgramOnBrokenWarnsOnceNoThrow) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -913,8 +885,6 @@ TEST(AriadneRuntime, UnregisteredCustomWidgetDrawsPlaceholder) { // --- the init: block runner (run_init, state_exec) --------------------------- TEST(AriadneInit, RunsScopedToPrefix) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; cvc::app app; std::vector errs; // A relative path under the chroot prefix -> writes .agents, the same key a @@ -925,8 +895,6 @@ TEST(AriadneInit, RunsScopedToPrefix) { } TEST(AriadneInit, SyntaxErrorReportedNotThrown) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; std::vector errs; const bool ok = run_init(app, "", "(state-set \"x\" ", &errs); // unbalanced @@ -992,8 +960,6 @@ TEST(AriadneRuntime, CustomWidgetUncommittedEscapeDoesNotWrite) { } TEST(AriadneInit, RunawayScriptIsBoundedNotHang) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; std::vector errs; // A non-terminating init must be bounded (step/time cap) and reported — never hang. @@ -1035,8 +1001,6 @@ TEST(AriadneReactive, EmptyPredicateAlwaysShows) { } TEST(AriadneReactive, PredicateShowsThenHidesAsStateChanges) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1080,8 +1044,6 @@ TEST(AriadneMountScope, BindsResolveAtSubPrefix) { } TEST(AriadneMountScope, ReactiveReadsResolveAtSubPrefix) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; cvc::app app; Runtime rt(app, "ui.demo"); MockBackend mb; @@ -1106,8 +1068,6 @@ TEST(AriadneMountScope, ReactiveReadsResolveAtSubPrefix) { } TEST(AriadneMountScope, RepeatedMountGetsPerInstanceSubPrefix) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; cvc::app app; Runtime rt(app, "ui.demo"); MockBackend mb; @@ -1219,8 +1179,6 @@ TEST(AriadneMountScope, ReconcileTearsDownStaleHoles) { } TEST(AriadneMountScope, RepeatedMountLinkTargetsAreIndexed) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec (CVC_STATE_EXEC=OFF)"; cvc::app app; Runtime rt(app, "ui.demo"); MockBackend mb; @@ -1242,8 +1200,8 @@ TEST(AriadneMountScope, RepeatedMountLinkTargetsAreIndexed) { } TEST(AriadneMountScope, MountInitSeedsAtSubPrefix) { - if (!have_state_exec() || !have_yaml()) - GTEST_SKIP() << "needs state_exec + yaml"; + if (!have_yaml()) + GTEST_SKIP() << "needs yaml"; namespace fs = std::filesystem; const fs::path dir = fs::temp_directory_path() / "ariadne_mount_init"; fs::create_directories(dir); @@ -1266,8 +1224,8 @@ TEST(AriadneMountScope, MountInitSeedsAtSubPrefix) { } TEST(AriadneMountScope, MountInitRunsOnceNotPerFrame) { - if (!have_state_exec() || !have_yaml()) - GTEST_SKIP() << "needs state_exec + yaml"; + if (!have_yaml()) + GTEST_SKIP() << "needs yaml"; namespace fs = std::filesystem; const fs::path dir = fs::temp_directory_path() / "ariadne_mount_init_once"; fs::create_directories(dir); @@ -1318,8 +1276,6 @@ TEST(AriadneMountScope, EndToEndLoadCommitsAtSubPrefix) { } TEST(AriadneReactive, FalsePredicateHidesTheWholeSubtree) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1334,8 +1290,6 @@ TEST(AriadneReactive, FalsePredicateHidesTheWholeSubtree) { } TEST(AriadneReactive, BrokenPredicateHidesFailSafeAndWarnsOnce) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1354,8 +1308,6 @@ TEST(AriadneReactive, BrokenPredicateHidesFailSafeAndWarnsOnce) { } TEST(AriadneReactive, WriteIntrinsicIsUnavailableInAPredicate) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1371,8 +1323,6 @@ TEST(AriadneReactive, WriteIntrinsicIsUnavailableInAPredicate) { } TEST(AriadneReactive, PredicateReadsArePrefixScoped) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, "ui.demo"); MockBackend mb; @@ -1389,8 +1339,6 @@ TEST(AriadneReactive, PredicateReadsArePrefixScoped) { } TEST(AriadneReactive, RunawayPredicateIsCappedNotHung) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1410,8 +1358,6 @@ TEST(AriadneReactive, RunawayPredicateIsCappedNotHung) { // scheduler is never pumped; a park there would silent-nil). render() must return, hidden + // reported. TEST(AriadneReactive, ParkVerbInPredicateIsDeniedNotHung) { - if (!have_state_exec()) - GTEST_SKIP(); // Both park verbs (msg-recv AND await) must be denied in the reactive read lane — neither is in // the read-lane allowlist, so a predicate using one is unbound → fail-safe hidden, render // returns. @@ -1433,8 +1379,6 @@ TEST(AriadneReactive, ParkVerbInPredicateIsDeniedNotHung) { // per-activation budget wired in ensure_resident — drain() returns, and the scheduler recovers so a // subsequently-installed healthy resident still fires. TEST(AriadneResident, RunawayTickResidentIsBoundedAndSchedulerRecovers) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1452,8 +1396,6 @@ TEST(AriadneResident, RunawayTickResidentIsBoundedAndSchedulerRecovers) { } TEST(AriadneReactive, StringValueIsTruthyAndUnsetKeyIsCleanlyFalsy) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1471,8 +1413,6 @@ TEST(AriadneReactive, StringValueIsTruthyAndUnsetKeyIsCleanlyFalsy) { } TEST(AriadneReactive, DistinctBrokenPredicatesWarnIndependently) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1490,8 +1430,6 @@ TEST(AriadneReactive, DistinctBrokenPredicatesWarnIndependently) { } TEST(AriadneReactive, HiddenMenuIsSkippedEntirely) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1505,8 +1443,6 @@ TEST(AriadneReactive, HiddenMenuIsSkippedEntirely) { } TEST(AriadneReactive, HiddenGridChildConsumesNoCell) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1527,8 +1463,6 @@ TEST(AriadneReactive, HiddenGridChildConsumesNoCell) { } TEST(AriadneReactive, ReadLaneStillExcludesMaterializersAndSideEffects) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1554,8 +1488,6 @@ TEST(AriadneReactive, ReadLaneStillExcludesMaterializersAndSideEffects) { } TEST(AriadneReactive, CompoundsAndAllowedFormsNowWork) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1577,8 +1509,6 @@ TEST(AriadneReactive, CompoundsAndAllowedFormsNowWork) { } TEST(AriadneReactive, DeniedSpecialFormsAreRejectedNotRun) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1601,8 +1531,6 @@ TEST(AriadneReactive, DeniedSpecialFormsAreRejectedNotRun) { } TEST(AriadneReactive, StateDataDagIsCopiedBoundedNotHung) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; // The full-env init lane plants a physically-tiny SHARED DAG (~30 nodes, 2^30 logical) in // typed data. A read-lane predicate then reads it via state-data-get -> deep_copy. deep_copy @@ -1624,8 +1552,6 @@ TEST(AriadneReactive, StateDataDagIsCopiedBoundedNotHung) { } TEST(AriadneReactive, EnabledWhenGreysOutAndReacts) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1647,8 +1573,6 @@ TEST(AriadneReactive, EnabledWhenGreysOutAndReacts) { } TEST(AriadneReactive, DisabledWhenDisablesWhenTrue) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1663,8 +1587,6 @@ TEST(AriadneReactive, DisabledWhenDisablesWhenTrue) { } TEST(AriadneReactive, EnabledWhenBrokenPredicateDisablesFailSafe) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1690,8 +1612,6 @@ TEST(AriadneReactive, NoEnableFieldsMeansNoDisabledScope) { } TEST(AriadneReactive, ComputedTextValueFromExpression) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1714,8 +1634,6 @@ TEST(AriadneReactive, ComputedTextValueFromExpression) { } TEST(AriadneReactive, ComputedTextBrokenExprIsEmptyAndWarns) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1781,8 +1699,6 @@ TEST(AriadneRuntime, ColorWidgetFallsBackToTextWhenBackendCantDraw) { } TEST(AriadneReactive, ComputedComboOptionsFromExpression) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1796,8 +1712,6 @@ TEST(AriadneReactive, ComputedComboOptionsFromExpression) { } TEST(AriadneReactive, ComputedComboOptionsBrokenExprSkips) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1811,8 +1725,6 @@ TEST(AriadneReactive, ComputedComboOptionsBrokenExprSkips) { } TEST(AriadneReactive, StaticAndComputedTooltips) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1840,8 +1752,6 @@ TEST(AriadneReactive, NoTooltipMeansNoSetTooltip) { } TEST(AriadneReactive, RepeatEmitsNInstancesWithIndexSubstituted) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1863,8 +1773,6 @@ TEST(AriadneReactive, RepeatEmitsNInstancesWithIndexSubstituted) { } TEST(AriadneReactive, RepeatSubstitutesIndexInBindsForDistinctState) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1880,8 +1788,6 @@ TEST(AriadneReactive, RepeatSubstitutesIndexInBindsForDistinctState) { } TEST(AriadneReactive, RepeatBrokenCountEmitsNothing) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1895,8 +1801,6 @@ TEST(AriadneReactive, RepeatBrokenCountEmitsNothing) { } TEST(AriadneReactive, LiteralTooltipStartingWithParenShownVerbatim) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1910,8 +1814,6 @@ TEST(AriadneReactive, LiteralTooltipStartingWithParenShownVerbatim) { } TEST(AriadneReactive, DisabledWidgetStillEmitsItsTooltip) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1935,8 +1837,6 @@ TEST(AriadneReactive, DisabledWidgetStillEmitsItsTooltip) { } TEST(AriadneReactive, RepeatCountOutOfRangeDoubleIsZero) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -1950,8 +1850,6 @@ TEST(AriadneReactive, RepeatCountOutOfRangeDoubleIsZero) { } TEST(AriadneReactive, PredicateEvalsAreIsolatedPerWidget) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -2158,8 +2056,6 @@ static bool msg_delivered(cvc::app &app, Runtime &rt, MockBackend &mb, const std } TEST(AriadneChannelPolicy, StrictRefusesUndeclaredChannel) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -2170,8 +2066,6 @@ TEST(AriadneChannelPolicy, StrictRefusesUndeclaredChannel) { } TEST(AriadneChannelPolicy, AllowsDeclaredChannel) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -2181,8 +2075,6 @@ TEST(AriadneChannelPolicy, AllowsDeclaredChannel) { } TEST(AriadneChannelPolicy, PermissiveWhenNotStrict) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -2192,8 +2084,6 @@ TEST(AriadneChannelPolicy, PermissiveWhenNotStrict) { } TEST(AriadneChannelPolicy, AllowsDeclaredGlobal) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -2204,8 +2094,6 @@ TEST(AriadneChannelPolicy, AllowsDeclaredGlobal) { } TEST(AriadneChannelPolicy, RefusesUndeclaredGlobal) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -2216,8 +2104,6 @@ TEST(AriadneChannelPolicy, RefusesUndeclaredGlobal) { } TEST(AriadneChannelPolicy, ExemptsHashChannel) { - if (!have_state_exec()) - GTEST_SKIP(); cvc::app app; Runtime rt(app, ""); MockBackend mb; @@ -2320,8 +2206,6 @@ std::string node_data_string(cvc::app &app, const char *path) { } // namespace TEST(AriadneNetIntrinsics, HttpGetAsyncAwaitsResponseDict) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; if (!cvc::net::have_http_backend()) GTEST_SKIP() << "libcvc built without an HTTP backend"; cvc::app app; @@ -2357,8 +2241,6 @@ TEST(AriadneNetIntrinsics, HttpGetAsyncAwaitsResponseDict) { } TEST(AriadneNetIntrinsics, HttpGetAsyncErrorPathResumesWithErrorDict) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; if (!cvc::net::have_http_backend()) GTEST_SKIP() << "libcvc built without an HTTP backend"; cvc::app app; @@ -2389,8 +2271,6 @@ TEST(AriadneNetIntrinsics, HttpGetAsyncErrorPathResumesWithErrorDict) { } TEST(AriadneNetIntrinsics, HttpGetAsyncDoesNotBlockTheScheduler) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; if (!cvc::net::have_http_backend()) GTEST_SKIP() << "libcvc built without an HTTP backend"; cvc::app app; @@ -2422,8 +2302,6 @@ TEST(AriadneNetIntrinsics, HttpGetAsyncDoesNotBlockTheScheduler) { // PR-A: the TRANSPARENT verb — (http-get url) self-parks and yields the dict directly, no msg-recv. TEST(AriadneNetIntrinsics, HttpGetTransparentReturnsDictNoMsgRecv) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; if (!cvc::net::have_http_backend()) GTEST_SKIP() << "libcvc built without an HTTP backend"; cvc::app app; @@ -2454,8 +2332,6 @@ TEST(AriadneNetIntrinsics, HttpGetTransparentReturnsDictNoMsgRecv) { // PR-A: PROPER await — (await (http-get-async url)) resolves the future returned by the async verb. TEST(AriadneNetIntrinsics, AwaitResolvesHttpGetAsyncFuture) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; if (!cvc::net::have_http_backend()) GTEST_SKIP() << "libcvc built without an HTTP backend"; cvc::app app; @@ -2486,8 +2362,6 @@ TEST(AriadneNetIntrinsics, AwaitResolvesHttpGetAsyncFuture) { // self-parks, returning { ok body(bytes) url error }. No HTTP backend needed (a custom scheme // here). TEST(AriadneNetIntrinsics, FetchTransparentResolvesAnyScheme) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; cvc::app app; struct Teardown { ~Teardown() { @@ -2522,8 +2396,6 @@ TEST(AriadneNetIntrinsics, FetchTransparentResolvesAnyScheme) { // PR-B: (await (fetch-async uri)) resolves the generic resolver's future. TEST(AriadneNetIntrinsics, FetchAsyncFutureAwaited) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; cvc::app app; struct Teardown { ~Teardown() { @@ -2565,8 +2437,6 @@ bool headers_have_prefix(const std::vector &h, const std::string &p // "form" (dict …))) — a form body is percent-encoded with a default Content-Type, and a bearer // token rides in as a header. This is the authenticated form-POST the user asked for. TEST(AriadneNetIntrinsics, HttpRequestPostFormBodyAndBearer) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; if (!cvc::net::have_http_backend()) GTEST_SKIP() << "libcvc built without an HTTP backend"; cvc::app app; @@ -2608,8 +2478,6 @@ TEST(AriadneNetIntrinsics, HttpRequestPostFormBodyAndBearer) { // (http-get URL (dict "query" (dict …))) percent-encodes the params into the URL's query string — // the caller never hand-encodes. A space becomes %20 and '&' is escaped so it can't inject a pair. TEST(AriadneNetIntrinsics, HttpGetQueryParamsEncodedIntoUrl) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; if (!cvc::net::have_http_backend()) GTEST_SKIP() << "libcvc built without an HTTP backend"; cvc::app app; @@ -2642,8 +2510,6 @@ TEST(AriadneNetIntrinsics, HttpGetQueryParamsEncodedIntoUrl) { // A raw string "body" option is sent verbatim and does NOT get a defaulted Content-Type (only the // "form" helper adds one) — the caller controls the content type. TEST(AriadneNetIntrinsics, HttpRequestRawBodyNoDefaultContentType) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; if (!cvc::net::have_http_backend()) GTEST_SKIP() << "libcvc built without an HTTP backend"; cvc::app app; @@ -2677,8 +2543,6 @@ TEST(AriadneNetIntrinsics, HttpRequestRawBodyNoDefaultContentType) { // prior (http-get) yielded, carried via the "body" option) rides through byte-exact — so a fetched // blob can be re-uploaded with a PUT without a lossy text round-trip. TEST(AriadneNetIntrinsics, HttpRequestAsyncPutWithBytesBodyAwaited) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; if (!cvc::net::have_http_backend()) GTEST_SKIP() << "libcvc built without an HTTP backend"; cvc::app app; @@ -2718,8 +2582,6 @@ TEST(AriadneNetIntrinsics, HttpRequestAsyncPutWithBytesBodyAwaited) { // a transparent (pool-double n) and a future (pool-double-async n) via the SAME action-intrinsics // seam register_net_intrinsics uses, so both land in the action lane only (never the render lane). TEST(AriadnePoolTask, OffloadedKernelResumesTransparentAndFuture) { - if (!have_state_exec()) - GTEST_SKIP() << "libcvc built without state_exec"; namespace se = cvc::state_exec; cvc::app app; NetIntrinsicsGuard guard; // clears the action intrinsics on teardown diff --git a/src/cvcGL/ariadne/AriRuntime.cpp b/src/cvcGL/ariadne/AriRuntime.cpp index baf1c9bd..3b1e2bde 100644 --- a/src/cvcGL/ariadne/AriRuntime.cpp +++ b/src/cvcGL/ariadne/AriRuntime.cpp @@ -140,7 +140,6 @@ void AriRuntime::post_pointer(int kind, double x, double y, double dx, double dy std::vector AriRuntime::take_warnings() { return app_rt_.take_warnings(); } bool AriRuntime::reload_if_changed() { return app_rt_.reload_if_changed(); } -bool AriRuntime::have_state_exec() { return ari::AppRuntime::have_state_exec(); } bool AriRuntime::have_yaml() { return ari::AppRuntime::have_yaml(); } } // namespace ariadne diff --git a/src/cvcGL/examples/nav_compute.cpp b/src/cvcGL/examples/nav_compute.cpp index 8c5d6c8c..c7295935 100644 --- a/src/cvcGL/examples/nav_compute.cpp +++ b/src/cvcGL/examples/nav_compute.cpp @@ -96,31 +96,28 @@ int main(int argc, char **argv) { // and post the result when it joins. Runs INLINE on the host thread inside Runtime::drain(), so // it MUST submit-and-return, never block — app.compute_async does exactly that (a background pool // worker owns the blocking parallel_for; the caller returns immediately). on_done runs on the - // worker and touches only the one thread-safe seam, exec_scheduler().post_message. Guarded so a - // CVC_STATE_EXEC=OFF build still links (the program lanes just don't run there). - if (ari::have_state_exec()) { - ari::register_action_intrinsics([&app](std::shared_ptr env, - se::intrinsics_context &ictx) { - // §12 channel scoping: the .ari's (msg-recv "nav.done") resolves against this document's - // chroot, so the host must post to the SAME scoped key. Resolve it on THIS (scheduler) - // thread and capture the string — the compute worker must never walk the state tree. - const std::string done = se::resolve_channel_key(ictx.root_path, "nav.done"); - se::builtins::register_fn(env, "nav-step-async", [&app, done](std::span) { - auto arrived = std::make_shared>(0); - const int n = 4096; - app.compute_async( - n, - [arrived](int i) { // stand-in nav kernel: count the "arrived" agents - if ((i % 3) == 0) - arrived->fetch_add(1, std::memory_order_relaxed); - }, - [&app, arrived, done] { - app.exec_scheduler().post_message(done, se::value_t(std::to_string(arrived->load()))); - }); - return se::value_t{}; // nil; the .ari action parks on (msg-recv "nav.done") - }); + // worker and touches only the one thread-safe seam, exec_scheduler().post_message. + ari::register_action_intrinsics([&app](std::shared_ptr env, + se::intrinsics_context &ictx) { + // §12 channel scoping: the .ari's (msg-recv "nav.done") resolves against this document's + // chroot, so the host must post to the SAME scoped key. Resolve it on THIS (scheduler) + // thread and capture the string — the compute worker must never walk the state tree. + const std::string done = se::resolve_channel_key(ictx.root_path, "nav.done"); + se::builtins::register_fn(env, "nav-step-async", [&app, done](std::span) { + auto arrived = std::make_shared>(0); + const int n = 4096; + app.compute_async( + n, + [arrived](int i) { // stand-in nav kernel: count the "arrived" agents + if ((i % 3) == 0) + arrived->fetch_add(1, std::memory_order_relaxed); + }, + [&app, arrived, done] { + app.exec_scheduler().post_message(done, se::value_t(std::to_string(arrived->load()))); + }); + return se::value_t{}; // nil; the .ari action parks on (msg-recv "nav.done") }); - } + }); ari::LoadResult lr = ari::load_file(docPath.c_str()); if (!lr.ok) { diff --git a/src/cvcGL/examples/wasm/build-wasm-demo.sh b/src/cvcGL/examples/wasm/build-wasm-demo.sh index 2246cdd0..a11b1baa 100755 --- a/src/cvcGL/examples/wasm/build-wasm-demo.sh +++ b/src/cvcGL/examples/wasm/build-wasm-demo.sh @@ -92,7 +92,6 @@ emcmake cmake -G Ninja -S "${REPO_ROOT}" -B "${BUILD_DIR}" \ ${CVC_WASM_NAV_WEIGHTS:+-DCVC_WASM_NAV_WEIGHTS="${CVC_WASM_NAV_WEIGHTS}"} \ -DCVC_ENABLE_MESHER=OFF \ -DCVC_ENABLE_SDF=ON \ - -DCVC_STATE_EXEC=ON \ -DCVC_BUILD_CVCGL=ON \ -DCVC_BUILD_EXAMPLES=ON \ -DCVC_WASM_PTHREADS=${PTHREAD}