diff --git a/.agent/validation_result.schema.json b/.agent/validation_result.schema.json new file mode 100644 index 0000000..90824f5 --- /dev/null +++ b/.agent/validation_result.schema.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/nvidia-isaac/cuVSLAM/.agent/validation_result.schema.json", + "title": "cuVSLAM Agent Readiness Validation Result", + "type": "object", + "required": ["schema_version", "product", "mode", "status", "timestamp_utc", "repo", "checks"], + "properties": { + "schema_version": { + "type": "string", + "const": "1.0" + }, + "product": { + "type": "string", + "const": "cuVSLAM" + }, + "mode": { + "type": "string", + "enum": ["static", "gpu"] + }, + "status": { + "type": "string", + "enum": ["pass", "fail"] + }, + "timestamp_utc": { + "type": "string" + }, + "repo": { + "type": "object", + "required": ["commit", "root"], + "properties": { + "commit": { + "type": ["string", "null"] + }, + "root": { + "type": "string" + } + } + }, + "checks": { + "type": "array", + "items": { + "type": "object", + "required": ["name", "status", "detail"], + "properties": { + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": ["pass", "fail"] + }, + "detail": { + "type": "string" + } + } + } + } + } +} diff --git a/.codex/skills/cuvslam-agent-readiness/SKILL.md b/.codex/skills/cuvslam-agent-readiness/SKILL.md new file mode 100644 index 0000000..0f9b1d7 --- /dev/null +++ b/.codex/skills/cuvslam-agent-readiness/SKILL.md @@ -0,0 +1,59 @@ +--- +name: cuvslam-agent-readiness +description: Validate cuVSLAM agent-readiness scaffolding and route agents to static or GPU validation lanes. +--- + +# cuVSLAM Agent Readiness + +## Description + +Use this skill when an agent needs to validate cuVSLAM readiness, emit a +machine-readable validation result, or decide whether the current environment is +only suitable for static checks or full GPU runtime validation. + +## Prerequisites + +- Python 3.9 or newer for static validation. +- CUDA Toolkit 12 or 13 and NVIDIA GPU hardware for full runtime validation. +- A built or wheel-installed PyCuVSLAM package before running GPU validation. +- No secrets are required for either validation lane. + +## Commands + +Run the no-secret static lane: + +```bash +python3 scripts/agent_readiness_smoke.py --mode static --output reports/validation_result.json +``` + +Run the root readiness test: + +```bash +python3 -m unittest discover -s tests -p "test_*.py" -v +``` + +Run the GPU lane after installing or building PyCuVSLAM: + +```bash +python3 scripts/agent_readiness_smoke.py --mode gpu --output reports/validation_result.json +``` + +## Output + +The validation command writes `reports/validation_result.json`. The output uses +`.agent/validation_result.schema.json` and contains `status`, `mode`, +`timestamp_utc`, repository commit metadata, and a list of named checks. + +## Troubleshooting + +- For static failures, read each failed `checks[].name` in + `reports/validation_result.json` and restore the missing readiness file. +- For cache permission failures, set `PYTHONPYCACHEPREFIX=.cache/pycache`. +- For GPU import failures, install a cuVSLAM wheel matching Python, CUDA, OS, + and architecture, or build the repository first. + +## Safety + +The static lane is no-secret and does not download datasets or access cameras. +The GPU lane runs local imports and tests only; do not add live camera, +credential, destructive cleanup, or dataset deletion steps to the default lane. diff --git a/.env_example b/.env_example new file mode 100644 index 0000000..a229c64 --- /dev/null +++ b/.env_example @@ -0,0 +1,7 @@ +# cuVSLAM local development placeholders. +# No secrets are required for the default no-secret readiness lane. + +CUVSLAM_DATASET_ROOT=dataset +CUVSLAM_BUILD_DIR=build +CUVSLAM_VALIDATION_OUTPUT=reports/validation_result.json +PYTHONPYCACHEPREFIX=.cache/pycache diff --git a/.github/workflows/agent-readiness.yml b/.github/workflows/agent-readiness.yml new file mode 100644 index 0000000..57036a7 --- /dev/null +++ b/.github/workflows/agent-readiness.yml @@ -0,0 +1,49 @@ +name: Agent Readiness + +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + inputs: + run_gpu_validation: + description: Run the self-hosted GPU validation lane. + required: false + default: "false" + +jobs: + static-readiness: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Run readiness unit tests + run: python3 -m unittest discover -s tests -p "test_*.py" -v + + - name: Emit static validation result + run: python3 scripts/agent_readiness_smoke.py --mode static --output reports/validation_result.json + + - name: Upload validation result + uses: actions/upload-artifact@v4 + with: + name: cuvslam-static-validation-result + path: reports/validation_result.json + + gpu-validation: + if: github.event_name == 'workflow_dispatch' && inputs.run_gpu_validation == 'true' + runs-on: [self-hosted, linux, x64, gpu] + steps: + - uses: actions/checkout@v4 + + - name: Run GPU validation + run: python3 scripts/agent_readiness_smoke.py --mode gpu --output reports/validation_result.json + + - name: Upload GPU validation result + uses: actions/upload-artifact@v4 + with: + name: cuvslam-gpu-validation-result + path: reports/validation_result.json diff --git a/.gitignore b/.gitignore index 01fc39c..d81feb3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ __pycache__/ *.egg-info build/ +reports/ dataset/ /datasets/ ext_src diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..68ba0b9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,80 @@ +# Agent Instructions + +## Use This When + +Use this repository to build, test, or validate cuVSLAM, the CUDA-accelerated +visual odometry, visual-inertial odometry, mapping, and localization library. + +## Prerequisites + +- Ubuntu 22.04 or 24.04 on x86_64 or Jetson. +- Python 3.9 or newer. +- CMake 3.19 or newer, gcc/g++, git, and git-lfs. +- CUDA Toolkit 12 or 13 for full build and runtime validation. +- NVIDIA GPU hardware for cuVSLAM tracking, mapping, localization, and Python + API tests. +- No secrets are required for the no-secret static readiness lane. + +## Repo Map + +- `README.md`: product overview, installation, build, and first-run guidance. +- `examples/README.md`: dataset and live-camera examples. +- `python/pyproject.toml`: PyCuVSLAM package metadata. +- `python/test/`: Python API tracking, mapping, and binding tests. +- `TROUBLESHOOTING.md`: calibration, synchronization, image quality, and + debugging guidance. +- `agent-readiness.yaml`: agent-readiness CI contract. +- `scripts/agent_readiness_smoke.py`: emits `reports/validation_result.json`. + +## Commands + +Run the no-secret static readiness lane: + +```bash +python3 scripts/agent_readiness_smoke.py --mode static --output reports/validation_result.json +``` + +Run the root readiness unit test: + +```bash +python3 -m unittest discover -s tests -p "test_*.py" -v +``` + +Compile Python sources without running GPU code: + +```bash +PYTHONPYCACHEPREFIX=.cache/pycache python3 -m compileall python scripts tests -q +``` + +Run full Python API validation after building or installing PyCuVSLAM on a +CUDA-capable host: + +```bash +python3 scripts/agent_readiness_smoke.py --mode gpu --output reports/validation_result.json +``` + +## Output + +The readiness lanes write `reports/validation_result.json` using the schema in +`.agent/validation_result.schema.json`. A passing static result proves that +agent-readiness scaffolding, docs, and Python syntax are healthy. A passing GPU +result proves that PyCuVSLAM imports and the Python API regression tests pass in +the current environment. + +## Troubleshooting + +- If static validation fails, inspect the failed check names in + `reports/validation_result.json`. +- If Python bytecode writes fail on macOS or in a sandbox, set + `PYTHONPYCACHEPREFIX=.cache/pycache`. +- If GPU validation fails at `cuvslam_import`, install a release wheel matching + the Python, CUDA, OS, and architecture combination or build from source first. +- If tracking quality is poor, use `TROUBLESHOOTING.md` to check calibration, + synchronization, frame continuity, image quality, and coordinate frames. + +## Safety + +- Do not commit datasets, camera recordings, generated maps, `.env`, build + directories, or generated `reports/` outputs. +- Do not add secret-bearing environment variables to examples or CI. +- Keep destructive cleanup outside the default validation commands. diff --git a/README.md b/README.md index a7c6ae8..4170589 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,28 @@ cuVSLAM is the library by NVIDIA, providing various Visual Tracking Camera modes The quickest way to get started is to [install PyCuVSLAM from a pre-built wheel](#install-from-wheels) and explore the [examples](examples/). +## Agent Quick Start + +Use this path when validating the repository in a no-secret CI or coding-agent +environment before running CUDA/GPU workflows: + +```bash +python3 scripts/agent_readiness_smoke.py --mode static --output reports/validation_result.json +python3 -m unittest discover -s tests -p "test_*.py" -v +``` + +Expected static success signal: `reports/validation_result.json` exists and has +`status=pass` and `mode=static`. Full runtime validation still requires CUDA, +NVIDIA GPU hardware, and a built or wheel-installed PyCuVSLAM package: + +```bash +python3 scripts/agent_readiness_smoke.py --mode gpu --output reports/validation_result.json +``` + +Agent-specific routing, outputs, and troubleshooting are documented in +[`AGENTS.md`](AGENTS.md), [`llms.txt`](llms.txt), and +[`agent-readiness.yaml`](agent-readiness.yaml). + ## ROS2 Support To use cuVSLAM in a ROS2 environment: diff --git a/agent-readiness.yaml b/agent-readiness.yaml new file mode 100644 index 0000000..414420b --- /dev/null +++ b/agent-readiness.yaml @@ -0,0 +1,66 @@ +agent_readiness: + product: cuVSLAM + tier_target: agent_native + product_promise: CUDA-accelerated visual odometry, visual-inertial odometry, mapping, and localization for robotics camera workflows. + release_surface: open_source + validation_path: python3 scripts/agent_readiness_smoke.py --mode gpu --output reports/validation_result.json + success_criteria: + - reports/validation_result.json has status=pass and mode=gpu on a CUDA-capable host. + - Static no-secret readiness emits reports/validation_result.json without secrets, datasets, or GPU hardware. + - Python API tracking and map tests pass after a built or wheel-installed cuVSLAM package is available. + prerequisites: + runtime: Ubuntu 22.04 or 24.04, Python 3.9+, CMake 3.19+, CUDA Toolkit 12 or 13, and NVIDIA GPU for real tracking validation. + secrets: none + hardware: NVIDIA GPU required for full cuVSLAM runtime validation; CPU is sufficient for static syntax smoke. + runner: + needs: + - python>=3.9 + - cuda>=12 for runtime tests + - nvidia-gpu for full validation + - no-secret-lane + release_thresholds: + minimum_tier: agent_native + max_blocking_issues: 0 + jobs: + - name: static-agent-readiness + priority: P0 + prompt: Run the no-secret static readiness lane and report the validation_result.json path. + expected_artifacts: + - reports/validation_result.json + - name: pycuvslam-tracking-and-map-regression + priority: P0 + prompt: Run the Python tracking and map regression tests after installing or building cuVSLAM, then report the validation_result.json path. + expected_artifacts: + - reports/validation_result.json + entrypoints: + docs: + - README.md + - AGENTS.md + - llms.txt + - TROUBLESHOOTING.md + - examples/README.md + - python/pyproject.toml + - .codex/skills/cuvslam-agent-readiness/SKILL.md + skills: + - .codex/skills/cuvslam-agent-readiness + cli: + - python3 scripts/agent_readiness_smoke.py --help + - python3 python/setup.py --help + services: + - .agent/validation_result.schema.json + smoke_tests: + no_secret: + - python3 scripts/agent_readiness_smoke.py --mode static --output reports/validation_result.json + dry_run: + - PYTHONPYCACHEPREFIX=.cache/pycache python3 -m compileall python scripts tests -q + required_artifacts: + - reports/validation_result.json + agent_tasks: + - name: static-agent-readiness + prompt: Run the no-secret static readiness lane and report the validation_result.json path. + expected_artifacts: + - reports/validation_result.json + - name: pycuvslam-tracking-and-map-regression + prompt: Run the Python tracking and map regression tests after installing or building cuVSLAM, then report the validation_result.json path. + expected_artifacts: + - reports/validation_result.json diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..d0f0991 --- /dev/null +++ b/llms.txt @@ -0,0 +1,29 @@ +# cuVSLAM LLM Entry Point + +Product: cuVSLAM +Repository: https://github.com/nvidia-isaac/cuVSLAM + +Purpose: +cuVSLAM provides CUDA-accelerated visual odometry, visual-inertial odometry, +mapping, and localization for robotics camera workflows. + +Canonical files: +- README.md +- AGENTS.md +- agent-readiness.yaml +- TROUBLESHOOTING.md +- examples/README.md +- python/pyproject.toml +- python/test/test_tracking.py +- python/test/test_map.py +- .agent/validation_result.schema.json +- .codex/skills/cuvslam-agent-readiness/SKILL.md + +Fast no-secret validation: +python3 scripts/agent_readiness_smoke.py --mode static --output reports/validation_result.json + +GPU validation after building or installing PyCuVSLAM: +python3 scripts/agent_readiness_smoke.py --mode gpu --output reports/validation_result.json + +Expected primary artifact: +reports/validation_result.json diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6d529be --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +# Root manifest for agent-readiness and example setup discovery. +# Python binding package metadata lives in python/pyproject.toml. +# Python binding test dependencies live in python/test/requirements.txt. + +-r examples/requirements.txt diff --git a/scripts/agent_readiness_smoke.py b/scripts/agent_readiness_smoke.py new file mode 100644 index 0000000..aaa445a --- /dev/null +++ b/scripts/agent_readiness_smoke.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +"""Emit machine-readable agent-readiness validation evidence for cuVSLAM.""" + +from __future__ import annotations + +import argparse +import compileall +import json +import os +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +PRODUCT = "cuVSLAM" +SCHEMA_VERSION = "1.0" + + +def main(argv: list[str] | None = None) -> int: + args = _parser().parse_args(argv) + repo_root = Path(__file__).resolve().parents[1] + output_path = (repo_root / args.output).resolve() if not Path(args.output).is_absolute() else Path(args.output) + + checks = _static_checks(repo_root) + if args.mode == "gpu": + checks.extend(_gpu_checks(repo_root)) + + status = "pass" if all(check["status"] == "pass" for check in checks) else "fail" + payload = { + "schema_version": SCHEMA_VERSION, + "product": PRODUCT, + "mode": args.mode, + "status": status, + "timestamp_utc": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), + "repo": { + "commit": _git_commit(repo_root), + "root": str(repo_root), + }, + "checks": checks, + } + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") + print(f"wrote {output_path} status={status} checks={len(checks)}") + return 0 if status == "pass" else 1 + + +def _parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Run cuVSLAM agent-readiness smoke validation.") + parser.add_argument( + "--mode", + choices=("static", "gpu"), + default="static", + help="static checks repo readiness without CUDA; gpu also imports cuvslam and runs Python API tests.", + ) + parser.add_argument( + "--output", + default="reports/validation_result.json", + help="Path for the validation_result.json artifact.", + ) + return parser + + +def _static_checks(repo_root: Path) -> list[dict[str, Any]]: + checks: list[dict[str, Any]] = [] + required_files = [ + "README.md", + "AGENTS.md", + "llms.txt", + "agent-readiness.yaml", + ".env_example", + ".agent/validation_result.schema.json", + ".codex/skills/cuvslam-agent-readiness/SKILL.md", + ".github/workflows/agent-readiness.yml", + ] + for relpath in required_files: + path = repo_root / relpath + checks.append( + _check( + f"required_file:{relpath}", + path.exists(), + f"found {relpath}" if path.exists() else f"missing {relpath}", + ) + ) + + test_files = sorted((repo_root / "tests").glob("test_*.py")) if (repo_root / "tests").exists() else [] + checks.append(_check("root_tests", bool(test_files), f"found {len(test_files)} root test file(s)")) + checks.append(_check("python_syntax", _compile_python(repo_root), "compiled python, scripts, and tests")) + return checks + + +def _gpu_checks(repo_root: Path) -> list[dict[str, Any]]: + import_result = _run([sys.executable, "-c", "import cuvslam; print(cuvslam.__file__)"], repo_root, 60) + test_result = _run( + [sys.executable, "-m", "unittest", "discover", "-s", "python/test", "-p", "test_*.py", "-v"], + repo_root, + 600, + ) + return [ + _check("cuvslam_import", import_result.returncode == 0, _command_detail(import_result)), + _check("python_api_tests", test_result.returncode == 0, _command_detail(test_result)), + ] + + +def _compile_python(repo_root: Path) -> bool: + if "PYTHONPYCACHEPREFIX" not in os.environ: + sys.pycache_prefix = str(repo_root / ".cache" / "pycache") + paths = [repo_root / "python", repo_root / "scripts", repo_root / "tests"] + return all(compileall.compile_dir(str(path), quiet=1) for path in paths if path.exists()) + + +def _run(command: list[str], cwd: Path, timeout: int) -> subprocess.CompletedProcess[str]: + return subprocess.run(command, cwd=str(cwd), text=True, capture_output=True, timeout=timeout) + + +def _command_detail(result: subprocess.CompletedProcess[str]) -> str: + output = " ".join((result.stdout or result.stderr or "").split()) + return f"exit_code={result.returncode}" + (f" output={output[:300]}" if output else "") + + +def _git_commit(repo_root: Path) -> str | None: + result = _run(["git", "rev-parse", "HEAD"], repo_root, 30) + return result.stdout.strip() if result.returncode == 0 else None + + +def _check(name: str, passed: bool, detail: str) -> dict[str, Any]: + return {"name": name, "status": "pass" if passed else "fail", "detail": detail} + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_agent_readiness_smoke.py b/tests/test_agent_readiness_smoke.py new file mode 100644 index 0000000..7ed6e20 --- /dev/null +++ b/tests/test_agent_readiness_smoke.py @@ -0,0 +1,34 @@ +import importlib.util +import json +import unittest +from pathlib import Path + + +def load_smoke_module(): + repo_root = Path(__file__).resolve().parents[1] + script_path = repo_root / "scripts" / "agent_readiness_smoke.py" + spec = importlib.util.spec_from_file_location("agent_readiness_smoke", script_path) + module = importlib.util.module_from_spec(spec) + assert spec and spec.loader + spec.loader.exec_module(module) + return module + + +class AgentReadinessSmokeTest(unittest.TestCase): + def test_static_smoke_writes_validation_result(self): + smoke = load_smoke_module() + self.addCleanup(lambda: None) + import tempfile + + with tempfile.TemporaryDirectory() as tmpdir: + output = Path(tmpdir) / "validation_result.json" + exit_code = smoke.main(["--mode", "static", "--output", str(output)]) + + self.assertEqual(exit_code, 0) + payload = json.loads(output.read_text(encoding="utf-8")) + self.assertEqual(payload["product"], "cuVSLAM") + self.assertEqual(payload["mode"], "static") + self.assertEqual(payload["status"], "pass") + self.assertEqual(payload["schema_version"], "1.0") + self.assertTrue(payload["checks"]) + self.assertTrue(all(check["status"] == "pass" for check in payload["checks"]))