From 99aa7dbbeb82f84f5af2e2c02161f411f9374f68 Mon Sep 17 00:00:00 2001 From: Igor Pugachevsky Date: Wed, 1 Jul 2026 10:34:22 +0700 Subject: [PATCH] docs: refresh public repo scorecard and onboarding --- .github/workflows/ci.yml | 1 + README.md | 14 ++- docs/launch-scorecard.md | 58 +++++++---- docs/llms-full.txt | 14 ++- docs/qa-onboarding-checklist.md | 5 + engine/tests/test_env_doctor.py | 35 +++++++ justfile | 173 ++++++++++++++++---------------- scripts/env_doctor.py | 80 +++++++++++++++ 8 files changed, 277 insertions(+), 103 deletions(-) create mode 100644 engine/tests/test_env_doctor.py create mode 100755 scripts/env_doctor.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2b0bd6..f0e9c2b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -161,6 +161,7 @@ jobs: test -f examples/intelligence-catalog-page/README.md test -f examples/intelligence-catalog-page/build.py test -f openapi/zero-paper-api.v1.yaml + test -x scripts/env_doctor.py test -x scripts/assemble_release_assets.sh test -x scripts/install.sh test -x scripts/demo_capture.sh diff --git a/README.md b/README.md index 14b1a1d..4c3b9f4 100644 --- a/README.md +++ b/README.md @@ -467,16 +467,28 @@ the local docs gate. See [.devcontainer/README.md](.devcontainer/README.md). ## Source Quickstart -Requirements: Python 3.11+, Rust stable, Cargo, and `just`. +Requirements: Python 3.11+, Rust stable, Cargo, and `just`. Run the environment +doctor first; it fails fast when macOS `python3` resolves to Apple Python 3.9 or +when Rust/`just` are missing. ```bash git clone https://github.com/zero-intel/zero.git cd zero +just env-doctor just bootstrap just demo just paper-api-smoke ``` +If `just env-doctor` reports an old system Python, use an explicit Python 3.11 +interpreter or run through `uv`: + +```bash +uv run --python 3.11 --directory engine --with pytest --with hypothesis --with cryptography \ + python -m pytest -q -p no:cacheprovider +uv run --python 3.11 --directory engine --with cryptography python -m zero_engine.demo +``` + Run the paper API: ```bash diff --git a/docs/launch-scorecard.md b/docs/launch-scorecard.md index 5f9bf5e..5ef3050 100644 --- a/docs/launch-scorecard.md +++ b/docs/launch-scorecard.md @@ -1,24 +1,37 @@ # Launch Scorecard -This scorecard keeps the public repo honest before launch. It scores only the -open ZERO Runtime, Protocol, and Proof substrate. It does not score the hosted -retail product, production wallets, raw exchange records, or commercial +This scorecard keeps the public repo honest before and after launch. It scores +only the open ZERO Runtime, Protocol, and Proof substrate. It does not score the +hosted retail product, production wallets, raw exchange records, or commercial Intelligence service operation. -## Current Launch Repository Score +## Current Public Repository Scores -**100/100** +Last refreshed: 2026-07-01 from a clean public checkout and live GitHub/Railway +checks. + +| Dimension | Score | Current proof | +| --- | ---: | --- | +| Engineering substrate | **98/100** | Engine tests, Rust workspace tests, MCP smoke, docs gates, and branch protection are green. | +| Public freshness | **95/100** | Dependency PRs are reviewed/merged or queued for automerge; the next release evidence refresh remains the only cadence item. | +| New-contributor onboarding | **96/100** | `just env-doctor` now catches Python/Rust/just issues before bootstrap; README includes a `uv` fallback for macOS Python 3.9. | +| Public credibility / product honesty | **97/100** | Scorecard is timestamped, release freshness is separated from launch baseline, and live-capital claims stay bounded by redacted proof. | +| Safety / proof posture | **99/100** | Paper-first defaults, fail-closed live boundary, proof privacy checks, journal integrity, CodeQL, secret scan, and OpenSSF Scorecard stay in CI. | + +## Launch Baseline Repository Score + +**100/100 as of the v0.1.2 launch baseline** ## Current Operating-Intelligence Trust Score -**100/100** +**97/100** -The launch repository is ready for serious contributors when the public +The launch repository remains ready for serious contributors when the public readiness gate passes with current doctrine. ZERO product truth is explicit: ZERO is the operating intelligence layer between humans and capital; this public repo is the open Runtime, Protocol, and Proof substrate. The stricter trust bar -requires runnable software, redacted evidence, clear custody boundaries, and -gates that reject stale category claims. +requires runnable software, redacted evidence, clear custody boundaries, +maintained dependency PRs, and gates that reject stale category claims. ## Ready @@ -126,16 +139,27 @@ gates that reject stale category claims. - Commercial intelligence connectors - Enterprise support and SLAs -## Remaining To Keep 100 +## Remaining To Keep Scores At Or Above 95 -- Keep the public GitHub Actions matrix green after every push -- Keep published release evidence green with `just release-evidence v0.1.2` +- Keep the public GitHub Actions matrix green after every push. +- Keep Dependency PRs reviewed within one maintenance cycle; green bot PRs should + be approved, automerged, or closed with a maintainer note. +- Publish refreshed release evidence for the next tag after material `main` + drift; `v0.1.2` remains the launch baseline, but the next release should + become the active install/documentation target. - Keep package-registry publication disabled until public name ownership, - Trusted Publishing, owner lists, and rollback procedure are secured -- Keep the committed Homebrew formula generated from release checksums + Trusted Publishing, owner lists, and rollback procedure are secured. +- Keep the committed Homebrew formula generated from release checksums. -## Definition Of 100 +## Definition Of 95+ -The repo is 100/100 when a new engineer can clone it, run one command, inspect a -paper engine through the CLI, pass CI locally, verify release artifacts, and pick +The repo is 95+/100 across public dimensions when a new engineer can clone it, +run an environment doctor before bootstrap, inspect a paper engine through the +CLI, pass CI locally with documented commands, verify release artifacts, and pick up a scoped contributor issue without private context. + +## Definition Of 100 + +The repo returns to 100/100 current score when the active GitHub Release, Docker +image, Homebrew formula, release evidence, and README install targets all point +to the same freshly verified tag with no open green maintenance PRs. diff --git a/docs/llms-full.txt b/docs/llms-full.txt index 9e6b059..6c8d5af 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -532,16 +532,28 @@ the local docs gate. See [.devcontainer/README.md](.devcontainer/README.md). ## Source Quickstart -Requirements: Python 3.11+, Rust stable, Cargo, and `just`. +Requirements: Python 3.11+, Rust stable, Cargo, and `just`. Run the environment +doctor first; it fails fast when macOS `python3` resolves to Apple Python 3.9 or +when Rust/`just` are missing. ```bash git clone https://github.com/zero-intel/zero.git cd zero +just env-doctor just bootstrap just demo just paper-api-smoke ``` +If `just env-doctor` reports an old system Python, use an explicit Python 3.11 +interpreter or run through `uv`: + +```bash +uv run --python 3.11 --directory engine --with pytest --with hypothesis --with cryptography \ + python -m pytest -q -p no:cacheprovider +uv run --python 3.11 --directory engine --with cryptography python -m zero_engine.demo +``` + Run the paper API: ```bash diff --git a/docs/qa-onboarding-checklist.md b/docs/qa-onboarding-checklist.md index 95587ef..cda3a7f 100644 --- a/docs/qa-onboarding-checklist.md +++ b/docs/qa-onboarding-checklist.md @@ -11,9 +11,14 @@ review. Run from repository root: ```bash +just env-doctor just bootstrap ``` +`just env-doctor` must pass before bootstrap. It checks the contributor-facing +prerequisites that most often make a fresh clone look broken: Python 3.11+, +`just`, and Cargo/Rust. + Quickly verify Python and pytest resolve to the same interpreter family: ```bash diff --git a/engine/tests/test_env_doctor.py b/engine/tests/test_env_doctor.py new file mode 100644 index 0000000..20fff6a --- /dev/null +++ b/engine/tests/test_env_doctor.py @@ -0,0 +1,35 @@ +import importlib.util +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +ENV_DOCTOR = ROOT / "scripts" / "env_doctor.py" + + +def load_env_doctor(): + spec = importlib.util.spec_from_file_location("zero_env_doctor", ENV_DOCTOR) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def test_python_version_status_requires_python_311_or_newer(): + doctor = load_env_doctor() + + assert doctor.python_status((3, 10, 14)).ok is False + assert "Python 3.11+" in doctor.python_status((3, 10, 14)).message + assert doctor.python_status((3, 11, 0)).ok is True + assert doctor.python_status((3, 12, 2)).ok is True + + +def test_overall_exit_code_fails_when_any_required_check_fails(): + doctor = load_env_doctor() + + checks = [doctor.Check("python", False, "old"), doctor.Check("just", True, "ok")] + + assert doctor.exit_code(checks) == 1 + assert doctor.exit_code([doctor.Check("python", True, "ok")]) == 0 diff --git a/justfile b/justfile index c2d4e08..f4dd4e9 100644 --- a/justfile +++ b/justfile @@ -1,68 +1,72 @@ set shell := ["bash", "-uc"] export PYTHONDONTWRITEBYTECODE := "1" +python := `if python3 -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 11) else 1)' 2>/dev/null; then echo python3; elif command -v python3.11 >/dev/null; then echo python3.11; else echo python3; fi` -bootstrap: - cd engine && python3 -m pip install -e ".[dev]" +env-doctor: + {{python}} scripts/env_doctor.py + +bootstrap: env-doctor + cd engine && {{python}} -m pip install -e ".[dev]" demo: - cd engine && python3 -m zero_engine.demo + cd engine && {{python}} -m zero_engine.demo example: - PYTHONPATH="$PWD/engine/src" python3 examples/paper-trading/run.py + PYTHONPATH="$PWD/engine/src" {{python}} examples/paper-trading/run.py strategy-example: - PYTHONPATH="$PWD/engine/src" python3 examples/paper-trading/strategy_demo.py + PYTHONPATH="$PWD/engine/src" {{python}} examples/paper-trading/strategy_demo.py strategy-plugin-example: - PYTHONPATH="$PWD/engine/src:$PWD/examples/strategy-plugin" python3 examples/strategy-plugin/run.py + PYTHONPATH="$PWD/engine/src:$PWD/examples/strategy-plugin" {{python}} examples/strategy-plugin/run.py momentum-strategy-plugin-example: - PYTHONPATH="$PWD/engine/src:$PWD/examples/momentum-strategy-plugin" python3 examples/momentum-strategy-plugin/run.py + PYTHONPATH="$PWD/engine/src:$PWD/examples/momentum-strategy-plugin" {{python}} examples/momentum-strategy-plugin/run.py strategy-runner-example: - PYTHONPATH="$PWD/engine/src" python3 examples/strategy-runner/run.py + PYTHONPATH="$PWD/engine/src" {{python}} examples/strategy-runner/run.py market-data-adapter-example: - PYTHONPATH="$PWD/engine/src:$PWD/examples/market-data-adapter" python3 examples/market-data-adapter/run.py + PYTHONPATH="$PWD/engine/src:$PWD/examples/market-data-adapter" {{python}} examples/market-data-adapter/run.py funding-rate-adapter-example: - PYTHONPATH="$PWD/examples/funding-rate-adapter" python3 examples/funding-rate-adapter/run.py + PYTHONPATH="$PWD/examples/funding-rate-adapter" {{python}} examples/funding-rate-adapter/run.py intelligence-catalog-page-example: - PYTHONPATH="$PWD/engine/src" python3 examples/intelligence-catalog-page/build.py + PYTHONPATH="$PWD/engine/src" {{python}} examples/intelligence-catalog-page/build.py network-leaderboard-example: - PYTHONPATH="$PWD/engine/src" python3 examples/network-leaderboard/build.py + PYTHONPATH="$PWD/engine/src" {{python}} examples/network-leaderboard/build.py network-profile-page-example: - PYTHONPATH="$PWD/engine/src" python3 examples/network-profile-page/build.py + PYTHONPATH="$PWD/engine/src" {{python}} examples/network-profile-page/build.py network-stale-profile-example: - PYTHONPATH="$PWD/engine/src" python3 examples/network-stale-profile/build.py --output /tmp/zero-stale-profile.json >/dev/null + PYTHONPATH="$PWD/engine/src" {{python}} examples/network-stale-profile/build.py --output /tmp/zero-stale-profile.json >/dev/null network-empty-profile-example: - PYTHONPATH="$PWD/engine/src" python3 examples/network-empty-profile/build.py --output /tmp/zero-empty-profile.json >/dev/null + PYTHONPATH="$PWD/engine/src" {{python}} examples/network-empty-profile/build.py --output /tmp/zero-empty-profile.json >/dev/null network-leaderboard-page-example: - PYTHONPATH="$PWD/engine/src" python3 examples/network-leaderboard-page/build.py + PYTHONPATH="$PWD/engine/src" {{python}} examples/network-leaderboard-page/build.py network-index-page-example: - PYTHONPATH="$PWD/engine/src" python3 examples/network-index-page/build.py + PYTHONPATH="$PWD/engine/src" {{python}} examples/network-index-page/build.py runtime-loop-example: - PYTHONPATH="$PWD/engine/src" python3 examples/runtime-loop/run.py + PYTHONPATH="$PWD/engine/src" {{python}} examples/runtime-loop/run.py memory-core-example: rm -rf artifacts/memory-example - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.memory extract --decisions examples/memory-core/decisions.jsonl --store artifacts/memory-example/memory.jsonl --knowledge artifacts/memory-example/knowledge.md --now 2026-05-01T00:00:00Z + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.memory extract --decisions examples/memory-core/decisions.jsonl --store artifacts/memory-example/memory.jsonl --knowledge artifacts/memory-example/knowledge.md --now 2026-05-01T00:00:00Z test -f artifacts/memory-example/memory.jsonl test -f artifacts/memory-example/knowledge.md ! rg '40500|1400|notional_usd|private_key|wallet_address' artifacts/memory-example genesis-example: rm -rf artifacts/genesis-example - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.genesis plan --proposals examples/genesis/proposals.jsonl --journal artifacts/genesis-example/genesis.jsonl --now 2026-05-01T00:00:00Z - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.genesis status --journal artifacts/genesis-example/genesis.jsonl --now 2026-05-01T00:00:00Z + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.genesis plan --proposals examples/genesis/proposals.jsonl --journal artifacts/genesis-example/genesis.jsonl --now 2026-05-01T00:00:00Z + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.genesis status --journal artifacts/genesis-example/genesis.jsonl --now 2026-05-01T00:00:00Z test -f artifacts/genesis-example/genesis.jsonl rg '"decision":"accepted"' artifacts/genesis-example/genesis.jsonl rg '"decision":"rejected"' artifacts/genesis-example/genesis.jsonl @@ -71,9 +75,9 @@ genesis-example: evolve-example: rm -rf artifacts/evolve-example - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.genesis plan --proposals examples/genesis/proposals.jsonl --journal artifacts/evolve-example/genesis.jsonl --now 2026-05-01T00:00:00Z - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.evolve run --genesis-journal artifacts/evolve-example/genesis.jsonl --output artifacts/evolve-example --repo-root "$PWD" --now 2026-05-01T00:00:00Z - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.evolve status --run artifacts/evolve-example/evolve-run.json --now 2026-05-01T00:00:00Z + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.genesis plan --proposals examples/genesis/proposals.jsonl --journal artifacts/evolve-example/genesis.jsonl --now 2026-05-01T00:00:00Z + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.evolve run --genesis-journal artifacts/evolve-example/genesis.jsonl --output artifacts/evolve-example --repo-root "$PWD" --now 2026-05-01T00:00:00Z + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.evolve status --run artifacts/evolve-example/evolve-run.json --now 2026-05-01T00:00:00Z test -f artifacts/evolve-example/evolve-run.json test -f artifacts/evolve-example/worktree/candidate.patch rg '"schema_version": "zero.evolve.run.v1"' artifacts/evolve-example/evolve-run.json @@ -83,8 +87,8 @@ evolve-example: research-example: rm -rf artifacts/research-example - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.research run --repo-root "$PWD" --output artifacts/research-example/research.json --now 2026-05-01T00:00:00Z - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.research status --report artifacts/research-example/research.json --now 2026-05-01T00:00:00Z + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.research run --repo-root "$PWD" --output artifacts/research-example/research.json --now 2026-05-01T00:00:00Z + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.research status --report artifacts/research-example/research.json --now 2026-05-01T00:00:00Z test -f artifacts/research-example/research.json rg '"schema_version": "zero.research.report.v1"' artifacts/research-example/research.json rg '"hunt"' artifacts/research-example/research.json @@ -96,7 +100,7 @@ research-example: decision-stack-example: rm -rf artifacts/decision-stack-example mkdir -p artifacts/decision-stack-example - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.decision --coin BTC --price 40500 --source paper:fixture --sample-size 4 > artifacts/decision-stack-example/decision-stack.json + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.decision --coin BTC --price 40500 --source paper:fixture --sample-size 4 > artifacts/decision-stack-example/decision-stack.json test -f artifacts/decision-stack-example/decision-stack.json rg '"schema_version": "zero.decision.stack.v1"' artifacts/decision-stack-example/decision-stack.json rg '"lenses"' artifacts/decision-stack-example/decision-stack.json @@ -106,13 +110,13 @@ decision-stack-example: ! rg 'private_key|wallet_address|exchange_order_id|notional_usd|idempotency_key' artifacts/decision-stack-example network-pages-smoke: - scripts/network_pages_smoke.py + {{python}} scripts/network_pages_smoke.py intelligence-catalog-page-smoke: - scripts/intelligence_catalog_page_smoke.py + {{python}} scripts/intelligence_catalog_page_smoke.py paper-api: - cd engine && python3 -m zero_engine.api + cd engine && {{python}} -m zero_engine.api paper-api-smoke: scripts/paper_api_smoke.sh @@ -124,31 +128,31 @@ demo-capture: scripts/demo_capture.sh issue-template-check: - scripts/issue_template_check.py + {{python}} scripts/issue_template_check.py label-taxonomy-check: - scripts/label_taxonomy_check.py + {{python}} scripts/label_taxonomy_check.py github-label-config-check: - scripts/github_label_sync.py --validate-config + {{python}} scripts/github_label_sync.py --validate-config github-label-check repo="zero-intel/zero": - scripts/github_label_sync.py --repo "{{repo}}" --check + {{python}} scripts/github_label_sync.py --repo "{{repo}}" --check github-label-sync repo="zero-intel/zero": - scripts/github_label_sync.py --repo "{{repo}}" --apply + {{python}} scripts/github_label_sync.py --repo "{{repo}}" --apply launch-issue-config-check: - scripts/github_launch_issues.py --validate-config + {{python}} scripts/github_launch_issues.py --validate-config github-launch-issue-check repo="zero-intel/zero": - scripts/github_launch_issues.py --repo "{{repo}}" --check + {{python}} scripts/github_launch_issues.py --repo "{{repo}}" --check github-launch-issue-sync repo="zero-intel/zero": - scripts/github_launch_issues.py --repo "{{repo}}" --apply + {{python}} scripts/github_launch_issues.py --repo "{{repo}}" --apply codeowners-check: - scripts/codeowners_check.py + {{python}} scripts/codeowners_check.py stale-artifact-check: scripts/stale_artifact_check.sh --check @@ -166,50 +170,50 @@ package-dry-run: scripts/package_dry_run.sh registry-readiness: - scripts/registry_readiness.py - scripts/registry_launch_packet.py --check - scripts/mcp_registry_packet.py --check - scripts/mcp_registry_listing_check.py --json >/dev/null + {{python}} scripts/registry_readiness.py + {{python}} scripts/registry_launch_packet.py --check + {{python}} scripts/mcp_registry_packet.py --check + {{python}} scripts/mcp_registry_listing_check.py --json >/dev/null railway-template-packet: - scripts/railway_template_packet.py --check + {{python}} scripts/railway_template_packet.py --check mcp-registry-listing-check: - scripts/mcp_registry_listing_check.py --json + {{python}} scripts/mcp_registry_listing_check.py --json release-rehearsal: scripts/release_rehearsal.sh release-verify dir: - scripts/release_verify.py "{{dir}}" + {{python}} scripts/release_verify.py "{{dir}}" release-provenance dir: - scripts/release_provenance.py "{{dir}}" + {{python}} scripts/release_provenance.py "{{dir}}" release-evidence tag: - scripts/release_evidence.py "{{tag}}" + {{python}} scripts/release_evidence.py "{{tag}}" release-preflight: public-proof registry-readiness package-dry-run release-rehearsal draft-release-rehearsal llms-full: - scripts/generate_llms_full.py + {{python}} scripts/generate_llms_full.py proof-pack: - PYTHONPATH="$PWD/engine/src" scripts/proof_pack.py + PYTHONPATH="$PWD/engine/src" {{python}} scripts/proof_pack.py network-proof-pack: - PYTHONPATH="$PWD/engine/src" scripts/network_proof_pack.py + PYTHONPATH="$PWD/engine/src" {{python}} scripts/network_proof_pack.py live-trading-evidence-check: - scripts/live_trading_evidence.py verify docs/proof/live/live-trading-evidence.json + {{python}} scripts/live_trading_evidence.py verify docs/proof/live/live-trading-evidence.json public-proof: - PYTHONPATH="$PWD/engine/src" scripts/proof_pack.py --check - PYTHONPATH="$PWD/engine/src" scripts/network_proof_pack.py --check - scripts/live_trading_evidence.py verify docs/proof/live/live-trading-evidence.json - PYTHONPATH="$PWD/engine/src" scripts/proof_privacy_regression.py - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.mcp --smoke - PYTHONPATH="$PWD/engine/src" scripts/mcp_transcript.py --check + PYTHONPATH="$PWD/engine/src" {{python}} scripts/proof_pack.py --check + PYTHONPATH="$PWD/engine/src" {{python}} scripts/network_proof_pack.py --check + {{python}} scripts/live_trading_evidence.py verify docs/proof/live/live-trading-evidence.json + PYTHONPATH="$PWD/engine/src" {{python}} scripts/proof_privacy_regression.py + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.mcp --smoke + PYTHONPATH="$PWD/engine/src" {{python}} scripts/mcp_transcript.py --check draft-release-rehearsal: scripts/draft_release_rehearsal.sh @@ -218,10 +222,10 @@ release-workflow-rehearsal: scripts/release_workflow_rehearsal.sh homebrew-formula release_dir tag: - scripts/homebrew_formula.py "{{release_dir}}" --tag "{{tag}}" + {{python}} scripts/homebrew_formula.py "{{release_dir}}" --tag "{{tag}}" homebrew-formula-check: - scripts/homebrew_formula_check.py + {{python}} scripts/homebrew_formula_check.py railway-smoke: scripts/railway_smoke.sh @@ -230,31 +234,31 @@ deployment-evidence url: scripts/deployment_evidence.sh "{{url}}" live-canary-rehearsal url: - scripts/live_canary_rehearsal.py "{{url}}" + {{python}} scripts/live_canary_rehearsal.py "{{url}}" live-canary-verify dir: - scripts/live_canary_verify.py "{{dir}}" + {{python}} scripts/live_canary_verify.py "{{dir}}" live-canary-exchange-evidence bundle source: - scripts/live_canary_exchange_evidence.py "{{bundle}}" "{{source}}" + {{python}} scripts/live_canary_exchange_evidence.py "{{bundle}}" "{{source}}" live-canary-operator url: - scripts/live_canary_operator.py "{{url}}" + {{python}} scripts/live_canary_operator.py "{{url}}" live-canary-operator-verify workflow: - scripts/live_canary_operator_verify.py "{{workflow}}" + {{python}} scripts/live_canary_operator_verify.py "{{workflow}}" live-cockpit-drill url="http://127.0.0.1:8765": - scripts/live_cockpit_drill.py "{{url}}" + {{python}} scripts/live_cockpit_drill.py "{{url}}" live-cockpit-drill-verify dir: - scripts/live_cockpit_drill_verify.py "{{dir}}" + {{python}} scripts/live_cockpit_drill_verify.py "{{dir}}" live-cockpit-drill-tamper-rehearsal dir: - scripts/live_cockpit_drill_tamper_rehearsal.py "{{dir}}" + {{python}} scripts/live_cockpit_drill_tamper_rehearsal.py "{{dir}}" checksum output *artifacts: - python3 scripts/write_sha256s.py "{{output}}" {{artifacts}} + {{python}} scripts/write_sha256s.py "{{output}}" {{artifacts}} engine-lint: cd engine && ruff check --no-cache . @@ -460,6 +464,7 @@ docs-check: test -f contracts/distribution/railway-template.json test -f server.json test -f openapi/zero-paper-api.v1.yaml + test -x scripts/env_doctor.py test -x scripts/assemble_release_assets.sh test -x scripts/install.sh test -x scripts/railway_template_packet.py @@ -516,21 +521,21 @@ docs-check: test -f compose.yaml test -f railway.toml test -f docs/railway-deploy.md - python3 scripts/openapi_contract_check.py - scripts/issue_template_check.py - scripts/label_taxonomy_check.py - scripts/github_label_sync.py --validate-config - scripts/github_launch_issues.py --validate-config - scripts/contributor_board_check.py - scripts/codeowners_check.py - scripts/homebrew_formula_check.py - PYTHONPATH="$PWD/engine/src" python3 -m zero_engine.mcp --smoke - PYTHONPATH="$PWD/engine/src" scripts/mcp_transcript.py --check - scripts/generate_llms_full.py --check - PYTHONPATH="$PWD/engine/src" scripts/proof_pack.py --check - PYTHONPATH="$PWD/engine/src" scripts/network_proof_pack.py --check - scripts/live_trading_evidence.py verify docs/proof/live/live-trading-evidence.json - PYTHONPATH="$PWD/engine/src" scripts/proof_privacy_regression.py + {{python}} scripts/openapi_contract_check.py + {{python}} scripts/issue_template_check.py + {{python}} scripts/label_taxonomy_check.py + {{python}} scripts/github_label_sync.py --validate-config + {{python}} scripts/github_launch_issues.py --validate-config + {{python}} scripts/contributor_board_check.py + {{python}} scripts/codeowners_check.py + {{python}} scripts/homebrew_formula_check.py + PYTHONPATH="$PWD/engine/src" {{python}} -m zero_engine.mcp --smoke + PYTHONPATH="$PWD/engine/src" {{python}} scripts/mcp_transcript.py --check + {{python}} scripts/generate_llms_full.py --check + PYTHONPATH="$PWD/engine/src" {{python}} scripts/proof_pack.py --check + PYTHONPATH="$PWD/engine/src" {{python}} scripts/network_proof_pack.py --check + {{python}} scripts/live_trading_evidence.py verify docs/proof/live/live-trading-evidence.json + PYTHONPATH="$PWD/engine/src" {{python}} scripts/proof_privacy_regression.py container-build: docker build -t zero-public:local . diff --git a/scripts/env_doctor.py b/scripts/env_doctor.py new file mode 100755 index 0000000..959ce53 --- /dev/null +++ b/scripts/env_doctor.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Check local prerequisites for ZERO source quickstarts. + +The public repo intentionally requires Python 3.11+ because the engine uses +stdlib APIs such as ``datetime.UTC``. This doctor fails early with a clear +message instead of letting new contributors reach an opaque import error during +``just bootstrap`` or ``just docs-check``. +""" + +from __future__ import annotations + +import shutil +import subprocess +import sys +from dataclasses import dataclass +from typing import Iterable, Tuple, Union + +MIN_PYTHON = (3, 11) + + +@dataclass(frozen=True) +class Check: + name: str + ok: bool + message: str + + +def python_status(version: Union[Tuple[int, int, int], Tuple[int, int]]) -> Check: + major, minor = version[:2] + current = ".".join(str(part) for part in version[:3]) + if (major, minor) < MIN_PYTHON: + return Check( + "python", + False, + f"Python 3.11+ required; found {current}. Install python3.11 or use uv.", + ) + return Check("python", True, f"Python {current} satisfies >=3.11") + + +def command_status(name: str, args: list[str], install_hint: str) -> Check: + if shutil.which(args[0]) is None: + return Check(name, False, f"{args[0]} not found. {install_hint}") + try: + output = subprocess.check_output(args, text=True, stderr=subprocess.STDOUT, timeout=10) + except (OSError, subprocess.SubprocessError) as exc: + return Check(name, False, f"{name} check failed: {exc}") + first_line = output.strip().splitlines()[0] if output.strip() else "available" + return Check(name, True, first_line) + + +def collect_checks() -> list[Check]: + return [ + python_status(sys.version_info[:3]), + command_status("just", ["just", "--version"], "Install with `brew install just`."), + command_status("cargo", ["cargo", "--version"], "Install Rust from https://rustup.rs/."), + ] + + +def exit_code(checks: Iterable[Check]) -> int: + return 0 if all(check.ok for check in checks) else 1 + + +def render(checks: Iterable[Check]) -> str: + lines = [] + for check in checks: + status = "ok" if check.ok else "fail" + lines.append(f"[{status:4}] {check.name:<8} {check.message}") + return "\n".join(lines) + + +def main() -> int: + checks = collect_checks() + print(render(checks)) + if exit_code(checks) != 0: + print("\nFix failed checks before running `just bootstrap`, or use the uv quickstart in README.md.") + return exit_code(checks) + + +if __name__ == "__main__": + raise SystemExit(main())