# polymodel development commands set dotenv-load := false # Canonical SQLite database URL for the projection store and the compile-time # `query!` macros. Scoping it here (rather than via .env) keeps `just` recipes # hermetic — see `set dotenv-load := false` above. db-url := 'sqlite:./data/polymodel.db' # Public recipes are dispatch boundaries; implementations are underscore-prefixed # so Just hides them from --list while keeping the dependency graph in-shell. default: @just --list # Format Rust sources. Run clippy/check separately; clippy --fix can corrupt RSX. fix: @tools/in-dev-shell _fix # Run clippy without --fix; automatic clippy rewrites can corrupt Dioxus RSX. lint: @tools/in-dev-shell _lint # Compile checks across the whole workspace plus the app's server/wasm targets. # The server check resolves compile-time `query!` macros against the committed # `.sqlx` offline cache; regenerate it with `just sqlx-prepare` after changing # SQL or migrations. check: @tools/in-dev-shell _check # Create and migrate the SQLite projection database. # Required when regenerating the `.sqlx` cache, and re-run after editing migrations. migrate: @tools/in-dev-shell _migrate # Regenerate the committed `.sqlx` offline query cache after changing SQL. # Self-contained: migrates the local database first, then points `cargo sqlx # prepare` at it via DATABASE_URL so no manual env setup is required. sqlx-prepare: @tools/in-dev-shell _sqlx-prepare # Run unit tests across the workspace with the app's server-only code enabled. test: @tools/in-dev-shell _test # Test the Just/Nix-shell dispatch boundary without evaluating the flake. test-just-nix: bash tools/test-just-nix.sh # Guard the repair shell from depending on repository build outputs. This must # remain runnable even when Nix evaluation or repository builds are broken. check-devshell-tools-only: python3 tools/check-devshell-tools-only.py # Run server-feature tests (app only; polymodel-api has no server feature). test-server: @tools/in-dev-shell _test-server # Run browser-backed wasm tests for the renderer worker. test-renderer: @tools/in-dev-shell _test-renderer # Validate the authored PM-77 corpus, provenance, matrix, gates, and snapshots. ldraw-corpus: @tools/in-dev-shell _ldraw-corpus # Compile the semantic core and reusable corpus/gate code for the E-facing WASM target. ldraw-wasm-check: @tools/in-dev-shell _ldraw-wasm-check # Ensure expected/gate/harness/fuzz paths use authored data and the real core adapter. ldraw-oracle-integrity: @tools/in-dev-shell _ldraw-oracle-integrity # Check committed expected records against a fresh oracle-only generation. ldraw-oracle-check: @tools/in-dev-shell _ldraw-oracle-check # Run bounded, deterministic native adversarial/property smoke. ldraw-fuzz-smoke: @tools/in-dev-shell _ldraw-fuzz-smoke # Generate oracle-only v3 projections into a temporary directory by default. ldraw-oracle-generate: @tools/in-dev-shell _ldraw-oracle-generate # Require the native LDParse oracle toolchain before running oracle-backed gates. require-nix: @tools/in-dev-shell _require-nix # Run the offline native LDParse compatibility boundary. ldraw-differential: @tools/in-dev-shell _ldraw-differential # Run focused LDraw core tests. test-ldraw-core: @tools/in-dev-shell _test-ldraw-core # Run focused native renderer-worker unit tests, including scene picking. test-renderer-worker-native: @tools/in-dev-shell _test-renderer-worker-native # Run the recipe-owned native preview/readback contract. test-renderer-native: @tools/in-dev-shell _test-renderer-native # Render the pinned LDraw corpus with the exact native preview target. preview-ldraw *ARGS: @tools/in-dev-shell _preview-ldraw {{ ARGS }} # Run all local validation expected before review. Keep the PM-77 recipes explicit # so corpus, WASM, fuzz, and Nix-backed differential gates remain visible. test-all: @tools/in-dev-shell _test-all # Run browser end-to-end tests. e2e: @tools/in-dev-shell _e2e # Start the Dioxus dev server. Builds the renderer worker first if missing. serve *ARGS: @tools/in-dev-shell _serve {{ ARGS }} # Build the renderer worker WASM bundle (separate from the app WASM). build-renderer-worker: @tools/in-dev-shell _build-renderer-worker # Build and stage the single-threaded OpenCascade.js STEP conversion artifacts. build-opencascade-step: @tools/in-dev-shell _build-opencascade-step # Build the Dioxus app for web (includes renderer worker build). build-web: @tools/in-dev-shell _build-web # Build the Dioxus app for web (release, optimized). Strips debug symbols and runs wasm-opt. build-web-release: @tools/in-dev-shell _build-web-release # Verify the renderer split: no heavy deps in the app, worker wasm exists, bundle size under ceiling. verify-renderer-split: @tools/in-dev-shell _verify-renderer-split # Regenerate crates/polymodel-api from authored lexicons via the local Jacquard checkout. generate-api: @tools/in-dev-shell _generate-api # Validate portable event-sourced GC coordination state. validate-gc-state *ARGS: @tools/in-dev-shell _validate-gc-state {{ ARGS }} # Validate GC jj workspace/bookmark rails before or after forgetting workspaces. validate-gc-jj-state *ARGS: @tools/in-dev-shell _validate-gc-jj-state {{ ARGS }} # Check that build-time environment handling does not expose server secrets to the frontend. check-public-env *ARGS: @tools/in-dev-shell _check-public-env {{ ARGS }} # Guard compile-time SQL usage in handwritten production Rust. check-production-sql *ARGS: @tools/in-dev-shell _check-production-sql {{ ARGS }} # Validate current documentation references and architecture maps. check-doc-references *ARGS: @tools/in-dev-shell _check-doc-references {{ ARGS }} # Guard the single production appview content-type normalizer and its callers. check-content-type *ARGS: @tools/in-dev-shell _check-content-type {{ ARGS }} # Validate the borrowed proxy response-reference parser and raw forwarding boundary. check-proxy-response *ARGS: @tools/in-dev-shell _check-proxy-response {{ ARGS }} # Create a sibling jj workspace for a Jira ticket or feature slug, then start a fresh change. workspace-create name: @tools/in-dev-shell _workspace-create {{ name }} # List jj workspaces. workspace-list: @tools/in-dev-shell _workspace-list # Forget a jj workspace after the work is safely landed or intentionally abandoned. workspace-forget name: @tools/in-dev-shell _workspace-forget {{ name }} # Show current jj state and diff. status: @tools/in-dev-shell _status # Clean build artifacts. clean: @tools/in-dev-shell _clean _fix: cargo fmt --all _lint: cargo clippy --workspace --all-targets --features server -- -D warnings -A clippy::useless_format cargo clippy -p polymodel --all-targets --features server -- -D warnings -A clippy::useless_format cargo clippy -p polymodel --target wasm32-unknown-unknown --features web -- -D warnings -A clippy::useless_format _check: cargo check --workspace cargo check -p polymodel --features server cargo check -p polymodel --target wasm32-unknown-unknown --features web RUSTFLAGS='--cfg getrandom_backend="wasm_js"' cargo check -p polymodel-renderer-worker --target wasm32-unknown-unknown _migrate: mkdir -p ./data cargo sqlx migrate run --source migrations --database-url '{{ db-url }}' _sqlx-prepare: _migrate DATABASE_URL='{{ db-url }}' cargo sqlx prepare -- -p polymodel --features server _test: cargo nextest run --workspace --features server _test-server: cargo nextest run -p polymodel --features server _test-renderer: RUSTFLAGS='--cfg getrandom_backend="wasm_js"' cargo test -p polymodel-renderer-worker --target wasm32-unknown-unknown _test-ldraw-core: cargo test -p polymodel-ldraw-core cargo test -p polymodel-ldraw-testkit _test-renderer-worker-native: cargo test -p polymodel-renderer-worker --lib _test-renderer-native: @set -eu; \ test -n "${LDRAWDIR:-}"; \ test -d "$LDRAWDIR"; \ echo "[native-test] inherited official library: $LDRAWDIR"; \ Xvfb :99 -screen 0 2560x1440x24 -ac +extension GLX +render -noreset >/tmp/polymodel-xvfb.log 2>&1 & xvfb_pid=$!; \ mkdir -p "$PWD/target/ldraw-preview"; \ preview_log="$PWD/target/ldraw-preview/native-preview.log"; \ test_log="$PWD/target/ldraw-preview/native-test.log"; \ readback_prefix="$PWD/target/ldraw-preview/persistent"; \ rm -f "$preview_log" "$test_log" "$readback_prefix"-*.png; \ trap 'status=$?; kill $xvfb_pid 2>/dev/null || true; echo "[native-test] Xvfb stderr:"; tail -80 /tmp/polymodel-xvfb.log; echo "[native-test] preview log:"; tail -160 "$preview_log" 2>/dev/null || true; echo "[native-test] test log:"; tail -160 "$test_log" 2>/dev/null || true; exit $status' EXIT; \ ready=0; \ for attempt in $(seq 1 100); do \ kill -0 "$xvfb_pid" 2>/dev/null || { echo "[native-test] Xvfb exited before display :99 became ready" >&2; exit 1; }; \ if xset -display :99 q >/dev/null 2>&1; then ready=1; break; fi; \ sleep 0.1; \ done; \ test "$ready" -eq 1 || { echo "[native-test] timed out waiting for display :99" >&2; exit 1; }; \ echo "[native-test] display-ready=:99"; \ echo "[native-test] preview-start"; \ set +e; DISPLAY=:99 WINIT_UNIX_BACKEND=x11 WAYLAND_DISPLAY= XDG_RUNTIME_DIR= LIBGL_ALWAYS_SOFTWARE=1 LDRAWDIR="$LDRAWDIR" timeout --signal=TERM --kill-after=5s 180s cargo run -p polymodel-renderer-worker --bin ldraw_preview -- --width 2560 --height 1440 --camera-case identity_front --scene-variant identity --proof-ms 1500 --readback-prefix "$readback_prefix" >"$preview_log" 2>&1; status=$?; set -e; cat "$preview_log"; test "$status" -eq 0; \ test -n "$(find "$PWD/target/ldraw-preview" -maxdepth 1 -name 'persistent-*.png' -type f -print -quit)"; \ echo "[native-test] integration-start"; \ set +e; DISPLAY=:99 WINIT_UNIX_BACKEND=x11 WAYLAND_DISPLAY= XDG_RUNTIME_DIR= LIBGL_ALWAYS_SOFTWARE=1 LDRAWDIR="$LDRAWDIR" timeout --signal=TERM --kill-after=5s 180s cargo test -p polymodel-renderer-worker --test ldraw_preview -- --nocapture >"$test_log" 2>&1; status=$?; set -e; cat "$test_log"; test "$status" -eq 0 _preview-ldraw *ARGS: set -eu; test -n "${LDRAWDIR:-}" || { echo "[preview] missing LDRAWDIR" >&2; exit 2; }; test -d "$LDRAWDIR" || { echo "[preview] invalid LDRAWDIR=$LDRAWDIR" >&2; exit 2; }; echo "[preview] inherited official library: $LDRAWDIR"; echo "[preview] launching native renderer; child output follows"; cargo run -p polymodel-renderer-worker --bin ldraw_preview -- {{ ARGS }} _ldraw-corpus: _ldraw-oracle-integrity _ldraw-oracle-check cargo test -p polymodel-ldraw-testkit cargo run --manifest-path tools/ldraw-compat-harness/Cargo.toml -- validate cargo run --manifest-path tools/ldraw-compat-harness/Cargo.toml -- inventory cargo run --manifest-path tools/ldraw-compat-harness/Cargo.toml -- corpus _ldraw-wasm-check: cargo check --tests -p polymodel-ldraw-core --target wasm32-unknown-unknown cargo check --tests -p polymodel-ldraw-testkit --target wasm32-unknown-unknown _ldraw-oracle-integrity: @if grep -RInE '(^|[^[:alnum:]_])canonical[[:space:]]*\(|(^|[^[:alnum:]_])parse_syntax[[:space:]]*\(' crates/polymodel-ldraw-testkit tools/ldraw-compat-harness tools/ldraw-oracle-generator --include='*.rs' --include='*.json' | grep -vE 'serialize_canonical|serialize[[:space:]]*\('; then echo 'FAIL: heuristic oracle helper call found'; exit 1; else echo 'PASS: no canonical/parse_syntax helper calls'; fi @if grep -RInE 'polymodel_ldraw_core|InProcessRustAdapter|polymodel-ldraw-core' crates/polymodel-ldraw-testkit/corpus/expected tools/ldraw-oracle-generator --include='*.json' --include='*.rs'; then echo 'FAIL: oracle generator/records depend on Rust semantic core'; exit 1; else echo 'PASS: oracle generator/records are core-independent'; fi _ldraw-oracle-check: _require-nix cargo run --manifest-path tools/ldraw-oracle-generator/Cargo.toml -- check crates/polymodel-ldraw-testkit/corpus crates/polymodel-ldraw-testkit/corpus/expected _ldraw-fuzz-smoke: cargo run --manifest-path tools/ldraw-compat-harness/Cargo.toml -- fuzz _ldraw-oracle-generate: _require-nix cargo run --manifest-path tools/ldraw-oracle-generator/Cargo.toml -- generate _ldraw-oracle-update: _require-nix cargo run --manifest-path tools/ldraw-oracle-generator/Cargo.toml -- update crates/polymodel-ldraw-testkit/corpus crates/polymodel-ldraw-testkit/corpus/expected _require-nix: @command -v nix >/dev/null 2>&1 || { echo "FAIL: nix is required for PM-77 oracle gates"; exit 1; } _ldraw-differential: _require-nix cargo run --manifest-path tools/ldraw-compat-harness/Cargo.toml -- differential _test-all: _require-nix _fix _check _lint _test _test-server _test-renderer _test-ldraw-core _ldraw-wasm-check _test-renderer-native _e2e: cd e2e && npm test _serve *ARGS: @if [ ! -f public/renderer_worker_bg.wasm ] || [ ! -f public/renderer_worker_loader.js ]; then echo "renderer worker not built — running just _build-renderer-worker"; just _build-renderer-worker; fi dx serve {{ ARGS }} _build-renderer-worker: RUSTFLAGS='--cfg getrandom_backend="wasm_js"' cargo build -p polymodel-renderer-worker --bin renderer_worker --target wasm32-unknown-unknown --profile worker-release wasm-bindgen target/wasm32-unknown-unknown/worker-release/renderer_worker.wasm --target web --out-dir public --no-typescript @if command -v wasm-opt &>/dev/null; then wasm-opt public/renderer_worker_bg.wasm -O4 --enable-bulk-memory --enable-simd --enable-nontrapping-float-to-int --enable-sign-ext -o public/renderer_worker_bg.wasm; else echo "wasm-opt not found, skipping optimization"; fi _build-opencascade-step: node tools/renderer-step/build-and-stage-opencascade.mjs _build-web: _build-renderer-worker dx build --platform web _build-web-release: _build-renderer-worker _build-opencascade-step #!/usr/bin/env bash set -e dx build --platform web --release --debug-symbols=false if command -v wasm-opt &>/dev/null; then WASM=$(find target/dx/polymodel/release/web/public -name "polymodel_bg*.wasm" -print -quit 2>/dev/null) if [ -n "$WASM" ]; then echo "==> Running wasm-opt on $WASM" BEFORE=$(stat -c%s "$WASM") wasm-opt "$WASM" -Oz --enable-bulk-memory --enable-simd --enable-nontrapping-float-to-int --enable-sign-ext -o "$WASM" || { echo "==> wasm-opt failed, keeping unoptimized wasm"; exit 0; } AFTER=$(stat -c%s "$WASM") echo " $BEFORE -> $AFTER bytes" fi else echo "==> wasm-opt not found, skipping optimization" fi _verify-renderer-split: _build-web @echo "==> AC.1: checking app dependency tree for forbidden crates..." @if cargo tree -p polymodel --target wasm32-unknown-unknown --features web --edges normal 2>/dev/null | grep -qE '(^| )three-d v|three-d-asset v|stl_io v|polymodel-mesh v|polymodel-ldraw-core v'; then echo "FAIL: forbidden crate found in app dependency tree"; exit 1; else echo "PASS: no forbidden crates"; fi @echo "==> AC.1b: checking app Cargo.toml for forbidden deps..." @if grep -qE '^\s*(three-d|three-d-asset|stl_io|polymodel-mesh)\b' Cargo.toml; then echo "FAIL: forbidden dep found in Cargo.toml"; exit 1; else echo "PASS: no forbidden deps in Cargo.toml"; fi @echo "==> AC.2: checking app wasm bundle size (debug build, debuginfo-dominated)..." @APP_WASM="target/dx/polymodel/debug/web/public/wasm/polymodel_bg.wasm" && test -f "$APP_WASM" && SIZE=$(stat -c%s "$APP_WASM") && echo "app wasm: $SIZE bytes" && test "$SIZE" -lt 134217728 && echo "PASS: app wasm < 128MB ceiling" || { echo "FAIL: app wasm >= 128MB or not found"; exit 1; } @echo "==> AC.2b: checking worker wasm exists..." @test -f public/renderer_worker_bg.wasm && echo "PASS: worker wasm exists" || { echo "FAIL: worker wasm not found"; exit 1; } @echo "==> All renderer-split checks passed." _generate-api: nix run ../jacquard _validate-gc-state *ARGS: python3 tools/validate-gc-state.py {{ ARGS }} _validate-gc-jj-state *ARGS: python3 tools/validate-gc-jj-state.py {{ ARGS }} _check-public-env *ARGS: python3 tools/check-public-env.py {{ ARGS }} _check-production-sql *ARGS: python3 tools/check-production-sql.py {{ ARGS }} _check-doc-references *ARGS: python3 tools/check-doc-references.py {{ ARGS }} _check-content-type *ARGS: python3 tools/check-content-type.py {{ ARGS }} _check-proxy-response *ARGS: python3 tools/check-proxy-response.py {{ ARGS }} _workspace-create name: root="$(jj workspace root)" && workspace_path="$(dirname "$root")/{{ name }}" && jj workspace add "$workspace_path" -r @ root="$(jj workspace root)" && workspace_path="$(dirname "$root")/{{ name }}" && cd "$workspace_path" && jj desc -m "{{ name }}" root="$(jj workspace root)" && workspace_path="$(dirname "$root")/{{ name }}" && direnv allow "$workspace_path" && direnv exec "$workspace_path" true _workspace-list: jj workspace list _workspace-forget name: jj workspace forget {{ name }} _status: jj status jj diff --summary _clean: cargo clean rm -rf target/dx