From 310da1f0e11831f4517ccd17e332a130a0008cb8 Mon Sep 17 00:00:00 2001 From: StockerMC <44980366+StockerMC@users.noreply.github.com> Date: Sat, 26 Sep 2026 23:26:42 -0400 Subject: [PATCH 1/2] Remove unused imports and variables flagged by pyflakes All 24 were dead: unused imports, f-strings without placeholders, and locals that were assigned and never read. --- orchestrator/cli.py | 1 - orchestrator/supervisor.py | 1 - scripts/anchor_checks.py | 1 - scripts/finetune_import.py | 2 +- scripts/frame_align.py | 2 +- scripts/motion_jitter.py | 2 +- scripts/package_multiperson.py | 1 - scripts/place_solve.py | 5 ++--- scripts/prepare_lhm_person.py | 2 +- scripts/run_clip.py | 8 ++++---- scripts/score_reference_frames.py | 2 +- scripts/world_collision.py | 4 +--- scripts/world_ruler.py | 2 -- worker/stages/dense_pi3x.py | 2 +- worker/stages/track_people.py | 3 +-- worker/stages/vlm_judge.py | 2 +- 16 files changed, 15 insertions(+), 25 deletions(-) diff --git a/orchestrator/cli.py b/orchestrator/cli.py index a03b1a7..89d14e2 100644 --- a/orchestrator/cli.py +++ b/orchestrator/cli.py @@ -15,7 +15,6 @@ import argparse import json import os -import shlex import subprocess import sys import time diff --git a/orchestrator/supervisor.py b/orchestrator/supervisor.py index 6c986a1..0ce8181 100644 --- a/orchestrator/supervisor.py +++ b/orchestrator/supervisor.py @@ -13,7 +13,6 @@ import argparse import os -import sys import time from pathlib import Path diff --git a/scripts/anchor_checks.py b/scripts/anchor_checks.py index 307d2c7..41e92f0 100644 --- a/scripts/anchor_checks.py +++ b/scripts/anchor_checks.py @@ -241,7 +241,6 @@ def check_obs_mask(clip: str, sidecar: Path | None, frames: int = 3) -> dict: `inMask` stayed at 0.99: on that clip the shadow really is re-observed. A clip where `inMask` stays near 1 AND the mask changes nothing is the one to distrust. """ - import cv2 import export_observation_confidence as eo wd = ROOT / "public" / "worlds" / WORLD_DIR.get(clip, f"{clip}-4d") diff --git a/scripts/finetune_import.py b/scripts/finetune_import.py index 03cc888..8c6b208 100644 --- a/scripts/finetune_import.py +++ b/scripts/finetune_import.py @@ -15,7 +15,7 @@ import numpy as np sys.path.insert(0, str(Path(__file__).resolve().parent)) -from bake_video_colours import ROOT, SH_C0, SPZ_COLOR_SCALE, read_spz # noqa: E402 +from bake_video_colours import ROOT, SPZ_COLOR_SCALE, read_spz # noqa: E402 from finetune_export import quat_mul, rotmat_to_quat_xyzw # noqa: E402 diff --git a/scripts/frame_align.py b/scripts/frame_align.py index 8718a1a..4e9a39c 100644 --- a/scripts/frame_align.py +++ b/scripts/frame_align.py @@ -729,7 +729,7 @@ def scale_report(clip): Stature is the body's longest principal extent, which a rotation does not change, so the scale moves only because the camera ruler moved. """ - from place_solve import world_splats, person_frames, fit_plane + from place_solve import world_splats, person_frames world, pi3x, spz, floor, preset_scale = CLIPS[clip] fa = json.load(open(ROOT / "public/worlds" / world / "framealign.json")) diff --git a/scripts/motion_jitter.py b/scripts/motion_jitter.py index 423707b..83a6e6f 100644 --- a/scripts/motion_jitter.py +++ b/scripts/motion_jitter.py @@ -5,7 +5,7 @@ splat i at frame t is the same body point: position differences are real motion. """ -import argparse, json, sys +import argparse, json import numpy as np HDR_END = b"end_header\n" diff --git a/scripts/package_multiperson.py b/scripts/package_multiperson.py index 470355f..4cda982 100644 --- a/scripts/package_multiperson.py +++ b/scripts/package_multiperson.py @@ -35,7 +35,6 @@ orthonormal, quat_from_matrix, quat_mul, - transform_ply, ) from sfm_frame import camera0_reframe, describe as describe_frame # noqa: E402 diff --git a/scripts/place_solve.py b/scripts/place_solve.py index 5e03e27..1813cd5 100644 --- a/scripts/place_solve.py +++ b/scripts/place_solve.py @@ -751,7 +751,7 @@ def main(): tracks = load_tracks(cfg) print(f"[{a.clip}] {len(tracks)} track(s), world {cfg['world']}", flush=True) - feats, bodyH0, low0 = [], None, None + feats, low0 = [], None rng = np.random.default_rng(0) for tid, seq, tr, _man in tracks: fr = list(person_frames(seq)) @@ -759,7 +759,7 @@ def main(): ys = np.sort(op0[:, 1]) l0 = float(ys[int(len(ys) * 0.01)]) if low0 is None: - low0, bodyH0 = l0, float(fr[0][1][:, 1].max() - fr[0][1][:, 1].min()) + low0 = l0 feats.append([tid, fr, tr, l0, json.load(open(seq))]) print(f" {tid}: {len(fr)} frames, frame-0 low {l0:.4f}", flush=True) @@ -803,7 +803,6 @@ def main(): measure_pad(fr, scale, pos0 + tr, pad, mpu=mpu) for _t, fr, tr, _l, _s in feats ] normal = pad["n"] - probe_xyz = None else: probe = [] for _t, fr, tr, _l, _s in feats: diff --git a/scripts/prepare_lhm_person.py b/scripts/prepare_lhm_person.py index f734caa..150f33c 100644 --- a/scripts/prepare_lhm_person.py +++ b/scripts/prepare_lhm_person.py @@ -20,7 +20,7 @@ refused by the GPU. """ -import argparse, hashlib, json, os, subprocess, sys +import argparse, hashlib, json, os, sys from pathlib import Path import cv2 diff --git a/scripts/run_clip.py b/scripts/run_clip.py index 70096f6..15ca786 100644 --- a/scripts/run_clip.py +++ b/scripts/run_clip.py @@ -2504,8 +2504,8 @@ def summary(self, wall): if ps: sc, res = ps["sizeCheck"], ps["residualCm"] print( - f" place: add &place=1 for the per-sample solve " - f"(contact residual " + " place: add &place=1 for the per-sample solve " + "(contact residual " + ", ".join( f"{res['before'][k]['rms']:.1f}->{res['after'][k]['rms']:.1f}" for k in res["before"] @@ -2690,8 +2690,8 @@ def status(dep): return 1 if gated: print( - f"\nSTOPPED AT THE CLEAN-REVIEW GATE before spending any credits. Look at the " - f"frames listed above, then re-run the same command with --gate-pass." + "\nSTOPPED AT THE CLEAN-REVIEW GATE before spending any credits. Look at the " + "frames listed above, then re-run the same command with --gate-pass." ) return 2 return 0 diff --git a/scripts/score_reference_frames.py b/scripts/score_reference_frames.py index 8b33dcc..b2c02d9 100644 --- a/scripts/score_reference_frames.py +++ b/scripts/score_reference_frames.py @@ -35,7 +35,7 @@ detector substitutes for this: torchvision's keypoint R-CNN reads bedroom f198 at 0.999. """ -import argparse, json, sys +import argparse, json from pathlib import Path import cv2 diff --git a/scripts/world_collision.py b/scripts/world_collision.py index 02cbf36..f88293a 100644 --- a/scripts/world_collision.py +++ b/scripts/world_collision.py @@ -16,7 +16,7 @@ uv run --locked --group inference python scripts/world_collision.py --world elevator-4d --spz public/marble-elevator-clean.spz """ -import argparse, gzip, json, os, struct, sys +import argparse, gzip, json, os, struct import numpy as np ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -367,8 +367,6 @@ def ransac_walls(P, n, d, max_walls, tol, min_inliers, iters, rng, floor_pts): bs = np.floor(s / 0.1).astype(np.int64) bh = np.floor(h / 0.1).astype(np.int64) occ = np.unique(bs * 100000 + bh) - ubs = np.unique(bs) - ubh = np.unique(bh) # a bin column counts only when it holds enough of the sheet, so a stray splat a metre # away does not stretch the extent cols = np.bincount(bs - bs.min()) diff --git a/scripts/world_ruler.py b/scripts/world_ruler.py index 243d68f..bf49e96 100644 --- a/scripts/world_ruler.py +++ b/scripts/world_ruler.py @@ -53,7 +53,6 @@ import json import os import struct -import subprocess import sys from pathlib import Path @@ -882,7 +881,6 @@ def person_masks_for(clip_name: str, ds_shape=None): tdirs = sorted((p.parent).glob("track_*")) src = {} for td in tdirs: - t = int(td.name.split("_")[1]) for f in json.loads((td / "motion.json").read_text())["frames"]: src.setdefault(f["sample"], f["sourceIndex"]) out = {} diff --git a/worker/stages/dense_pi3x.py b/worker/stages/dense_pi3x.py index 335362d..32d559f 100644 --- a/worker/stages/dense_pi3x.py +++ b/worker/stages/dense_pi3x.py @@ -6,7 +6,7 @@ """ from __future__ import annotations -import argparse, gc, hashlib, json, os, shutil, sys, time +import argparse, gc, hashlib, json, os, sys, time from pathlib import Path import cv2 import numpy as np diff --git a/worker/stages/track_people.py b/worker/stages/track_people.py index ec64281..be507a5 100644 --- a/worker/stages/track_people.py +++ b/worker/stages/track_people.py @@ -220,7 +220,7 @@ def main(): estimator = PoseEstimator("./pretrained_models/human_model_files", device="cuda") cap = cv2.VideoCapture(a.video) - tracks, closed, next_id = [], [], 0 + tracks, next_id = [], 0 overlay_samples = {0, len(indices) // 2, len(indices) - 1} mask_store = {} unmatched_rcnn_total = 0 @@ -246,7 +246,6 @@ def main(): break raw = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB) h, w = raw.shape[:2] - diag = float(np.hypot(w, h)) padded, ow, oh = estimator.img_center_padding(raw) tensor, annotation = estimator._preprocess(padded) pl, pt, factor, _, _ = annotation diff --git a/worker/stages/vlm_judge.py b/worker/stages/vlm_judge.py index 7747488..02f7fb4 100755 --- a/worker/stages/vlm_judge.py +++ b/worker/stages/vlm_judge.py @@ -15,7 +15,7 @@ Requires OPENAI_API_KEY (see ~/.openai-env). """ -import argparse, base64, json, os, ssl, sys, time, urllib.error, urllib.request +import argparse, base64, json, os, ssl, time, urllib.error, urllib.request CTX = ssl.create_default_context() try: From 5260d27a0e05243542d9baf02c2f22fb897d518d Mon Sep 17 00:00:00 2001 From: StockerMC <44980366+StockerMC@users.noreply.github.com> Date: Sat, 26 Sep 2026 23:26:42 -0400 Subject: [PATCH 2/2] Add CI and a single test command bun run test runs every unit test that needs no browser or server (279 bun, 13 node). CI type-checks root, site and admin, runs format:check, the tests, pyflakes and uv lock --check on every PR. Ruff lint is limited to E9 and F until the wider backlog is cleared. DEVELOPING listed three test scripts that no longer exist. --- .github/workflows/ci.yml | 62 ++++++++++++++++++++++++++++++++++++++++ docs/DEVELOPING.md | 20 ++++++------- package.json | 8 ++++-- pyproject.toml | 4 +++ 4 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f5c1f1e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,62 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + viewer: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.2.21 + - uses: actions/setup-node@v4 + with: + node-version: 24 + - uses: astral-sh/setup-uv@v6 + - run: bun install --frozen-lockfile + - run: bunx --bun tsc --noEmit + - run: bun run format:check + - run: bun run test + + site: + runs-on: ubuntu-latest + defaults: + run: + working-directory: site + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.2.21 + - run: bun install --frozen-lockfile + - run: bunx --bun tsc --noEmit + - run: bun run format:check + + admin: + runs-on: ubuntu-latest + defaults: + run: + working-directory: admin + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.2.21 + - run: bun install --frozen-lockfile + - run: bunx --bun tsc --noEmit + + python: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v6 + - run: uv run --locked --only-group dev ruff check scripts worker orchestrator + - run: uv lock --check diff --git a/docs/DEVELOPING.md b/docs/DEVELOPING.md index 45f485a..fa76096 100644 --- a/docs/DEVELOPING.md +++ b/docs/DEVELOPING.md @@ -206,18 +206,18 @@ and recovery without duplicate Marble generations. ```sh bun run build bun run format:check -bun run test:shared-assets -bun run test:pull-assets -bun run test:publish-hook -bun run test:marble -bun run test:audio +bun run lint:python +bun run test +``` + +CI (`.github/workflows/ci.yml`) runs these, plus type checks in `site/` and `admin/`, on every +pull request. `bun run test` covers every unit test that needs no browser or server. The browser +checks need Chrome and a running server: + +```sh bun run test:audio-browser -bun run test:audio-package -bun run test:person-motion -bun run test:static-colliders -bun run test:walk-collision bun run test:walk-clearance-browser -bun run test:xr-turning +bun run test:xr-browser ``` Python checks and formatting need `uv`; viewer-only use needs just Bun. diff --git a/package.json b/package.json index 8f4090d..d72057f 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,10 @@ "prepare:worlds": "bun scripts/prepare-worlds.ts", "runs:publish": "node scripts/publish-runs.ts", "assets:pull": "bun scripts/pull-assets.ts", + "test": "bun run test:unit && bun run test:node", + "test:unit": "bun test ./scripts/test-admin-blocks.ts ./scripts/test-bottle-agent-client.ts ./scripts/test-bottle-agent.ts ./scripts/test-bottle-floor.ts ./scripts/test-bottle-physics.ts ./scripts/test-bottle-scene.ts ./scripts/test-bottle-visual.ts ./scripts/test-fourd-audio.ts ./scripts/test-gaussian-ply.ts ./scripts/test-interaction-approach.ts ./scripts/test-object-track.ts ./scripts/test-person-motion-roundtrip.ts ./scripts/test-person-motion.ts ./scripts/test-person-size.ts ./scripts/test-person-stance.ts ./scripts/test-person-visibility.ts ./scripts/test-playback-time.ts ./scripts/test-prepared-world-server.ts ./scripts/test-prepared-world.ts ./scripts/test-pull-assets.ts ./scripts/test-quest-view.ts ./scripts/test-recorded-object.ts ./scripts/test-scene-config.ts ./scripts/test-scene-session.js ./scripts/test-shared-assets.ts ./scripts/test-static-colliders.ts ./scripts/test-walk-collision.ts ./scripts/test-xr-animation-cadence.ts ./scripts/test-xr-body-geometry.ts ./scripts/test-xr-body.ts ./scripts/test-xr-hands.ts ./scripts/test-xr-physical-walk.ts ./scripts/test-xr-render-policy.ts ./scripts/test-xr-replay-button.ts ./scripts/test-xr-scene-sidebar.ts ./scripts/test-xr-teleport.ts", + "test:node": "node --test scripts/test-publish-transport.ts scripts/test-publish-policy.ts", + "lint:python": "uv run --locked --only-group dev ruff check scripts worker orchestrator", "test:pull-assets": "bun test ./scripts/test-pull-assets.ts", "test:shared-assets": "bun test ./scripts/test-shared-assets.ts", "test:publish-transport": "node --test scripts/test-publish-transport.ts", @@ -36,8 +40,8 @@ "capture:audio": "bun scripts/capture-audio-demo.ts", "test:person-motion": "bun test ./scripts/test-person-motion.ts ./scripts/test-gaussian-ply.ts ./scripts/test-person-motion-roundtrip.ts ./scripts/test-person-stance.ts ./scripts/test-person-visibility.ts", "format": "bun run format:python && bun run format:web", - "format:check": "uv run --locked --group dev ruff format --check scripts worker && bunx --bun prettier --check \"src/**/*.{ts,js}\" \"scripts/**/*.{ts,js}\" \"server/**/*.ts\" \"*.html\" vite.config.ts package.json tsconfig.json", - "format:python": "uv run --locked --group dev ruff format scripts worker", + "format:check": "uv run --locked --only-group dev ruff format --check scripts worker orchestrator && bunx --bun prettier --check \"src/**/*.{ts,js}\" \"scripts/**/*.{ts,js}\" \"server/**/*.ts\" \"*.html\" vite.config.ts package.json tsconfig.json", + "format:python": "uv run --locked --only-group dev ruff format scripts worker orchestrator", "format:web": "bunx --bun prettier --write \"src/**/*.{ts,js}\" \"scripts/**/*.{ts,js}\" \"server/**/*.ts\" \"*.html\" vite.config.ts package.json tsconfig.json" }, "dependencies": { diff --git a/pyproject.toml b/pyproject.toml index f2093d9..edd49ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,3 +66,7 @@ required-version = "==0.16.8" target-version = "py311" line-length = 100 cache-dir = ".context/ruff-cache" + +[tool.ruff.lint] +# Pyflakes and syntax errors only for now; widen once the backlog is cleared. +select = ["E9", "F"]