surgeval 0.3.0a0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- surgeval-0.3.0a0/.dist-smoke/.gitignore +1 -0
- surgeval-0.3.0a0/.dist-smoke/surgeval-0.3.0a0-py3-none-any.whl +0 -0
- surgeval-0.3.0a0/.dist-smoke/surgeval-0.3.0a0.tar.gz +0 -0
- surgeval-0.3.0a0/.dockerignore +16 -0
- surgeval-0.3.0a0/.github/workflows/ci.yml +68 -0
- surgeval-0.3.0a0/.github/workflows/droid-review.yml +31 -0
- surgeval-0.3.0a0/.github/workflows/droid.yml +38 -0
- surgeval-0.3.0a0/.github/workflows/install-smoke.yml +238 -0
- surgeval-0.3.0a0/.github/workflows/publish.yml +198 -0
- surgeval-0.3.0a0/.github/workflows/sbom.yml +42 -0
- surgeval-0.3.0a0/.gitignore +46 -0
- surgeval-0.3.0a0/CODE_OF_CONDUCT.md +56 -0
- surgeval-0.3.0a0/CONTRIBUTING.md +85 -0
- surgeval-0.3.0a0/LICENSE +202 -0
- surgeval-0.3.0a0/NOTICE +5 -0
- surgeval-0.3.0a0/PKG-INFO +599 -0
- surgeval-0.3.0a0/PRODUCT.md +33 -0
- surgeval-0.3.0a0/README.md +378 -0
- surgeval-0.3.0a0/SECURITY.md +42 -0
- surgeval-0.3.0a0/docs/ASSESSMENT.md +441 -0
- surgeval-0.3.0a0/docs/BUILD.md +457 -0
- surgeval-0.3.0a0/docs/CONFORMANCE.md +147 -0
- surgeval-0.3.0a0/docs/DATASETS.md +77 -0
- surgeval-0.3.0a0/docs/NEXT_STATUS.md +182 -0
- surgeval-0.3.0a0/docs/ONRAMP.md +213 -0
- surgeval-0.3.0a0/docs/OSS_GOVERNANCE.md +143 -0
- surgeval-0.3.0a0/docs/PLAN.md +345 -0
- surgeval-0.3.0a0/docs/V0.3.md +79 -0
- surgeval-0.3.0a0/docs/examples/agents/example-counterfactual-world-model/agent.toml +21 -0
- surgeval-0.3.0a0/docs/examples/agents/example-counterfactual-world-model/predictions.json +43 -0
- surgeval-0.3.0a0/docs/examples/agents/example-counterfactual-world-model/predictor.py +21 -0
- surgeval-0.3.0a0/docs/examples/agents/example-cvs-detector/agent.toml +21 -0
- surgeval-0.3.0a0/docs/examples/agents/example-cvs-detector/predictions.json +34 -0
- surgeval-0.3.0a0/docs/examples/agents/example-cvs-detector/predictor.py +23 -0
- surgeval-0.3.0a0/docs/examples/agents/example-video-predictor/agent.toml +20 -0
- surgeval-0.3.0a0/docs/examples/agents/example-video-predictor/predictions.json +23 -0
- surgeval-0.3.0a0/docs/examples/agents/example-video-predictor/predictor.py +21 -0
- surgeval-0.3.0a0/docs/examples/agents/seldingermed-cath-seg/agent.toml +19 -0
- surgeval-0.3.0a0/docs/examples/agents/seldingermed-cath-seg/predictions.json +18 -0
- surgeval-0.3.0a0/docs/examples/agents/seldingermed-cath-seg/predictor.py +164 -0
- surgeval-0.3.0a0/docs/examples/agents/seldingermed-cath-seg/weights.json +27 -0
- surgeval-0.3.0a0/docs/examples/agents/seldingermed-lumen-linear/agent.toml +19 -0
- surgeval-0.3.0a0/docs/examples/agents/seldingermed-lumen-linear/policy.py +29 -0
- surgeval-0.3.0a0/docs/examples/agents/seldingermed-lumen-linear/weights.json +8 -0
- surgeval-0.3.0a0/docs/examples/agents/seldingermed-random/agent.toml +11 -0
- surgeval-0.3.0a0/docs/examples/datasets/angiostress-v0/dataset.toml +10 -0
- surgeval-0.3.0a0/docs/examples/datasets/lumen-nav-v0/dataset.toml +11 -0
- surgeval-0.3.0a0/docs/examples/datasets/video-nextstep-v0/dataset.toml +10 -0
- surgeval-0.3.0a0/docs/examples/jobs/lumen-nav-random/job.toml +16 -0
- surgeval-0.3.0a0/docs/examples/shelves/endovascular.toml +62 -0
- surgeval-0.3.0a0/docs/examples/tasks/angiostress-dias/contract.json +17 -0
- surgeval-0.3.0a0/docs/examples/tasks/angiostress-dias/inputs.json +5 -0
- surgeval-0.3.0a0/docs/examples/tasks/angiostress-dias/instruction.md +5 -0
- surgeval-0.3.0a0/docs/examples/tasks/angiostress-dias/labels.json +5 -0
- surgeval-0.3.0a0/docs/examples/tasks/angiostress-dias/task.toml +100 -0
- surgeval-0.3.0a0/docs/examples/tasks/angiostress-dias/verifier.py +46 -0
- surgeval-0.3.0a0/docs/examples/tasks/broncho-airway-nav/instruction.md +7 -0
- surgeval-0.3.0a0/docs/examples/tasks/broncho-airway-nav/task.toml +94 -0
- surgeval-0.3.0a0/docs/examples/tasks/broncho-airway-nav/verifier.py +93 -0
- surgeval-0.3.0a0/docs/examples/tasks/counterfactual-recovery/inputs.json +45 -0
- surgeval-0.3.0a0/docs/examples/tasks/counterfactual-recovery/instruction.md +1 -0
- surgeval-0.3.0a0/docs/examples/tasks/counterfactual-recovery/labels.json +22 -0
- surgeval-0.3.0a0/docs/examples/tasks/counterfactual-recovery/task.toml +114 -0
- surgeval-0.3.0a0/docs/examples/tasks/counterfactual-recovery/verifier.py +39 -0
- surgeval-0.3.0a0/docs/examples/tasks/laparoscopic-cholec-cvs/inputs.json +29 -0
- surgeval-0.3.0a0/docs/examples/tasks/laparoscopic-cholec-cvs/instruction.md +5 -0
- surgeval-0.3.0a0/docs/examples/tasks/laparoscopic-cholec-cvs/labels.json +29 -0
- surgeval-0.3.0a0/docs/examples/tasks/laparoscopic-cholec-cvs/task.toml +79 -0
- surgeval-0.3.0a0/docs/examples/tasks/laparoscopic-cholec-cvs/verifier.py +56 -0
- surgeval-0.3.0a0/docs/examples/tasks/lumen-nav-safe/instruction.md +11 -0
- surgeval-0.3.0a0/docs/examples/tasks/lumen-nav-safe/task.toml +103 -0
- surgeval-0.3.0a0/docs/examples/tasks/lumen-nav-safe/verifier.py +42 -0
- surgeval-0.3.0a0/docs/examples/tasks/lumen-nav-safe/verifier.toml +6 -0
- surgeval-0.3.0a0/docs/examples/tasks/orbit-needle-lift/instruction.md +19 -0
- surgeval-0.3.0a0/docs/examples/tasks/orbit-needle-lift/task.toml +80 -0
- surgeval-0.3.0a0/docs/examples/tasks/orbit-needle-lift/verifier.py +83 -0
- surgeval-0.3.0a0/docs/examples/tasks/orbit-needle-lift/wrap.json +20 -0
- surgeval-0.3.0a0/docs/examples/tasks/ortho-burr-safe/instruction.md +7 -0
- surgeval-0.3.0a0/docs/examples/tasks/ortho-burr-safe/task.toml +94 -0
- surgeval-0.3.0a0/docs/examples/tasks/ortho-burr-safe/verifier.py +44 -0
- surgeval-0.3.0a0/docs/examples/tasks/video-nextstep/inputs.json +7 -0
- surgeval-0.3.0a0/docs/examples/tasks/video-nextstep/instruction.md +9 -0
- surgeval-0.3.0a0/docs/examples/tasks/video-nextstep/labels.json +22 -0
- surgeval-0.3.0a0/docs/examples/tasks/video-nextstep/task.toml +77 -0
- surgeval-0.3.0a0/docs/examples/tasks/video-nextstep/verifier.py +37 -0
- surgeval-0.3.0a0/docs/examples/tasksets/consequence-rank-v1/taskset.toml +10 -0
- surgeval-0.3.0a0/docs/examples/tasksets/counterfactual-recovery-v1/taskset.toml +10 -0
- surgeval-0.3.0a0/next.md +223 -0
- surgeval-0.3.0a0/pyproject.toml +116 -0
- surgeval-0.3.0a0/scripts/check_identity.py +112 -0
- surgeval-0.3.0a0/scripts/check_license_allowlist.py +56 -0
- surgeval-0.3.0a0/scripts/check_pypi_attestation.py +137 -0
- surgeval-0.3.0a0/scripts/check_world_signals.py +536 -0
- surgeval-0.3.0a0/scripts/gen_sbom.py +205 -0
- surgeval-0.3.0a0/scripts/validate_sbom.py +102 -0
- surgeval-0.3.0a0/site/app.js +41 -0
- surgeval-0.3.0a0/site/favicon.svg +8 -0
- surgeval-0.3.0a0/site/index.html +246 -0
- surgeval-0.3.0a0/site/styles.css +655 -0
- surgeval-0.3.0a0/src/or_audit/__init__.py +21 -0
- surgeval-0.3.0a0/src/or_audit/audit/__init__.py +27 -0
- surgeval-0.3.0a0/src/or_audit/audit/canonical.py +222 -0
- surgeval-0.3.0a0/src/or_audit/audit/trail.py +417 -0
- surgeval-0.3.0a0/src/or_audit/cli.py +619 -0
- surgeval-0.3.0a0/src/or_audit/commands/__init__.py +50 -0
- surgeval-0.3.0a0/src/or_audit/commands/concierge.py +286 -0
- surgeval-0.3.0a0/src/or_audit/commands/conformance.py +109 -0
- surgeval-0.3.0a0/src/or_audit/commands/doctor.py +46 -0
- surgeval-0.3.0a0/src/or_audit/commands/export_verifiers.py +54 -0
- surgeval-0.3.0a0/src/or_audit/commands/onramp.py +292 -0
- surgeval-0.3.0a0/src/or_audit/commands/quickstart.py +182 -0
- surgeval-0.3.0a0/src/or_audit/commands/shelf.py +250 -0
- surgeval-0.3.0a0/src/or_audit/commands/worlds.py +193 -0
- surgeval-0.3.0a0/src/or_audit/commands/wrap.py +225 -0
- surgeval-0.3.0a0/src/or_audit/concierge/__init__.py +137 -0
- surgeval-0.3.0a0/src/or_audit/concierge/adapt.py +627 -0
- surgeval-0.3.0a0/src/or_audit/concierge/assess.py +206 -0
- surgeval-0.3.0a0/src/or_audit/concierge/intake.py +929 -0
- surgeval-0.3.0a0/src/or_audit/concierge/select.py +409 -0
- surgeval-0.3.0a0/src/or_audit/decision/__init__.py +24 -0
- surgeval-0.3.0a0/src/or_audit/decision/record.py +323 -0
- surgeval-0.3.0a0/src/or_audit/decision/rule.py +187 -0
- surgeval-0.3.0a0/src/or_audit/deid/__init__.py +50 -0
- surgeval-0.3.0a0/src/or_audit/deid/attestation.py +125 -0
- surgeval-0.3.0a0/src/or_audit/deid/detectors.py +353 -0
- surgeval-0.3.0a0/src/or_audit/deid/pipeline.py +459 -0
- surgeval-0.3.0a0/src/or_audit/deid/plan.py +145 -0
- surgeval-0.3.0a0/src/or_audit/deid/policy.py +234 -0
- surgeval-0.3.0a0/src/or_audit/deid/writer.py +90 -0
- surgeval-0.3.0a0/src/or_audit/demo.py +303 -0
- surgeval-0.3.0a0/src/or_audit/domain/__init__.py +43 -0
- surgeval-0.3.0a0/src/or_audit/domain/entities.py +289 -0
- surgeval-0.3.0a0/src/or_audit/domain/enums.py +136 -0
- surgeval-0.3.0a0/src/or_audit/domain/ids.py +137 -0
- surgeval-0.3.0a0/src/or_audit/errors.py +48 -0
- surgeval-0.3.0a0/src/or_audit/eval/__init__.py +205 -0
- surgeval-0.3.0a0/src/or_audit/eval/adapters/__init__.py +65 -0
- surgeval-0.3.0a0/src/or_audit/eval/adapters/base.py +172 -0
- surgeval-0.3.0a0/src/or_audit/eval/adapters/endoluminal.py +139 -0
- surgeval-0.3.0a0/src/or_audit/eval/adapters/fluoroscopy.py +141 -0
- surgeval-0.3.0a0/src/or_audit/eval/adapters/kinematics.py +164 -0
- surgeval-0.3.0a0/src/or_audit/eval/adapters/manifest.py +157 -0
- surgeval-0.3.0a0/src/or_audit/eval/adapters/video.py +146 -0
- surgeval-0.3.0a0/src/or_audit/eval/agent.py +106 -0
- surgeval-0.3.0a0/src/or_audit/eval/bind.py +28 -0
- surgeval-0.3.0a0/src/or_audit/eval/cartesian.py +206 -0
- surgeval-0.3.0a0/src/or_audit/eval/conformance.py +965 -0
- surgeval-0.3.0a0/src/or_audit/eval/contracts.py +301 -0
- surgeval-0.3.0a0/src/or_audit/eval/dataset.py +84 -0
- surgeval-0.3.0a0/src/or_audit/eval/enums.py +153 -0
- surgeval-0.3.0a0/src/or_audit/eval/equivalence.py +559 -0
- surgeval-0.3.0a0/src/or_audit/eval/evidence.py +178 -0
- surgeval-0.3.0a0/src/or_audit/eval/export_rl.py +161 -0
- surgeval-0.3.0a0/src/or_audit/eval/export_verifiers.py +1058 -0
- surgeval-0.3.0a0/src/or_audit/eval/gate_dsl.py +246 -0
- surgeval-0.3.0a0/src/or_audit/eval/gym_world.py +248 -0
- surgeval-0.3.0a0/src/or_audit/eval/integrity.py +48 -0
- surgeval-0.3.0a0/src/or_audit/eval/job.py +343 -0
- surgeval-0.3.0a0/src/or_audit/eval/job_config.py +119 -0
- surgeval-0.3.0a0/src/or_audit/eval/leaderboard.py +219 -0
- surgeval-0.3.0a0/src/or_audit/eval/licensing.py +631 -0
- surgeval-0.3.0a0/src/or_audit/eval/loader.py +240 -0
- surgeval-0.3.0a0/src/or_audit/eval/plugin_host.py +94 -0
- surgeval-0.3.0a0/src/or_audit/eval/plugins.py +291 -0
- surgeval-0.3.0a0/src/or_audit/eval/predict.py +66 -0
- surgeval-0.3.0a0/src/or_audit/eval/provenance.py +442 -0
- surgeval-0.3.0a0/src/or_audit/eval/reconstitute.py +112 -0
- surgeval-0.3.0a0/src/or_audit/eval/registry.py +219 -0
- surgeval-0.3.0a0/src/or_audit/eval/runner.py +701 -0
- surgeval-0.3.0a0/src/or_audit/eval/scorecard.py +263 -0
- surgeval-0.3.0a0/src/or_audit/eval/shelf.py +968 -0
- surgeval-0.3.0a0/src/or_audit/eval/sim/__init__.py +76 -0
- surgeval-0.3.0a0/src/or_audit/eval/sim/base.py +448 -0
- surgeval-0.3.0a0/src/or_audit/eval/sim/gym_bridge.py +92 -0
- surgeval-0.3.0a0/src/or_audit/eval/sim/isaac_bridge.py +616 -0
- surgeval-0.3.0a0/src/or_audit/eval/sim/sofa_bridge.py +234 -0
- surgeval-0.3.0a0/src/or_audit/eval/sim/warp_bridge.py +242 -0
- surgeval-0.3.0a0/src/or_audit/eval/task.py +830 -0
- surgeval-0.3.0a0/src/or_audit/eval/trace.py +158 -0
- surgeval-0.3.0a0/src/or_audit/eval/vector.py +207 -0
- surgeval-0.3.0a0/src/or_audit/eval/verifier.py +188 -0
- surgeval-0.3.0a0/src/or_audit/eval/worlds.py +333 -0
- surgeval-0.3.0a0/src/or_audit/eval/wrap.py +1226 -0
- surgeval-0.3.0a0/src/or_audit/ingest/__init__.py +16 -0
- surgeval-0.3.0a0/src/or_audit/ingest/alignment.py +109 -0
- surgeval-0.3.0a0/src/or_audit/ingest/manifest.py +156 -0
- surgeval-0.3.0a0/src/or_audit/install/__init__.py +73 -0
- surgeval-0.3.0a0/src/or_audit/install/catalog.py +769 -0
- surgeval-0.3.0a0/src/or_audit/install/catalog.toml +1034 -0
- surgeval-0.3.0a0/src/or_audit/install/doctor.py +648 -0
- surgeval-0.3.0a0/src/or_audit/install/installer.py +516 -0
- surgeval-0.3.0a0/src/or_audit/media/__init__.py +23 -0
- surgeval-0.3.0a0/src/or_audit/media/frames.py +193 -0
- surgeval-0.3.0a0/src/or_audit/metrics/__init__.py +43 -0
- surgeval-0.3.0a0/src/or_audit/metrics/agreement.py +223 -0
- surgeval-0.3.0a0/src/or_audit/metrics/harness.py +358 -0
- surgeval-0.3.0a0/src/or_audit/metrics/icc.py +151 -0
- surgeval-0.3.0a0/src/or_audit/perception/__init__.py +47 -0
- surgeval-0.3.0a0/src/or_audit/perception/backend.py +210 -0
- surgeval-0.3.0a0/src/or_audit/perception/observations.py +321 -0
- surgeval-0.3.0a0/src/or_audit/pipeline.py +213 -0
- surgeval-0.3.0a0/src/or_audit/primitives.py +80 -0
- surgeval-0.3.0a0/src/or_audit/report.py +285 -0
- surgeval-0.3.0a0/src/or_audit/scoring/__init__.py +43 -0
- surgeval-0.3.0a0/src/or_audit/scoring/gates.py +533 -0
- surgeval-0.3.0a0/src/or_audit/scoring/skill.py +206 -0
- surgeval-0.3.0a0/src/or_audit/version.py +23 -0
- surgeval-0.3.0a0/src/surgeval/__init__.py +134 -0
- surgeval-0.3.0a0/src/surgeval/client.py +301 -0
- surgeval-0.3.0a0/src/surgeval/decorators.py +475 -0
- surgeval-0.3.0a0/src/surgeval/integrations.py +113 -0
- surgeval-0.3.0a0/tests/__init__.py +4 -0
- surgeval-0.3.0a0/tests/conftest.py +159 -0
- surgeval-0.3.0a0/tests/test_agreement_gate.py +432 -0
- surgeval-0.3.0a0/tests/test_artifact_portability.py +253 -0
- surgeval-0.3.0a0/tests/test_audit_trail.py +389 -0
- surgeval-0.3.0a0/tests/test_canonical.py +165 -0
- surgeval-0.3.0a0/tests/test_canonical_conformance.py +143 -0
- surgeval-0.3.0a0/tests/test_check_pypi_attestation.py +105 -0
- surgeval-0.3.0a0/tests/test_check_world_signals.py +659 -0
- surgeval-0.3.0a0/tests/test_cli_branding.py +29 -0
- surgeval-0.3.0a0/tests/test_concierge.py +1524 -0
- surgeval-0.3.0a0/tests/test_conformance.py +1217 -0
- surgeval-0.3.0a0/tests/test_cvs_end_to_end.py +67 -0
- surgeval-0.3.0a0/tests/test_decision.py +416 -0
- surgeval-0.3.0a0/tests/test_deid_detectors.py +194 -0
- surgeval-0.3.0a0/tests/test_deid_leaks.py +986 -0
- surgeval-0.3.0a0/tests/test_deid_pipeline.py +436 -0
- surgeval-0.3.0a0/tests/test_end_to_end.py +409 -0
- surgeval-0.3.0a0/tests/test_entities.py +274 -0
- surgeval-0.3.0a0/tests/test_eval_contracts.py +177 -0
- surgeval-0.3.0a0/tests/test_eval_p3.py +442 -0
- surgeval-0.3.0a0/tests/test_eval_p4.py +186 -0
- surgeval-0.3.0a0/tests/test_eval_plugin_host.py +246 -0
- surgeval-0.3.0a0/tests/test_eval_run.py +434 -0
- surgeval-0.3.0a0/tests/test_eval_task.py +490 -0
- surgeval-0.3.0a0/tests/test_eval_v03.py +444 -0
- surgeval-0.3.0a0/tests/test_export_verifiers.py +504 -0
- surgeval-0.3.0a0/tests/test_frames.py +154 -0
- surgeval-0.3.0a0/tests/test_gate_dsl.py +369 -0
- surgeval-0.3.0a0/tests/test_gate_evidence_discipline.py +432 -0
- surgeval-0.3.0a0/tests/test_ids.py +79 -0
- surgeval-0.3.0a0/tests/test_ingest.py +206 -0
- surgeval-0.3.0a0/tests/test_install_ladder.py +1072 -0
- surgeval-0.3.0a0/tests/test_licensing_expressions.py +146 -0
- surgeval-0.3.0a0/tests/test_metrics.py +217 -0
- surgeval-0.3.0a0/tests/test_modality_adapters.py +340 -0
- surgeval-0.3.0a0/tests/test_multi_modality_contracts.py +408 -0
- surgeval-0.3.0a0/tests/test_onramp.py +567 -0
- surgeval-0.3.0a0/tests/test_perception.py +352 -0
- surgeval-0.3.0a0/tests/test_public_registry.py +44 -0
- surgeval-0.3.0a0/tests/test_safety_gates.py +498 -0
- surgeval-0.3.0a0/tests/test_sbom_gen.py +179 -0
- surgeval-0.3.0a0/tests/test_shelf_equivalence.py +1442 -0
- surgeval-0.3.0a0/tests/test_simulation_bridges.py +1240 -0
- surgeval-0.3.0a0/tests/test_skill_scoring.py +159 -0
- surgeval-0.3.0a0/tests/test_surgeval_sdk.py +270 -0
- surgeval-0.3.0a0/tests/test_world_kinds.py +562 -0
- surgeval-0.3.0a0/tests/test_world_signals.py +597 -0
- surgeval-0.3.0a0/tests/test_wrap_kit.py +660 -0
- surgeval-0.3.0a0/uv.lock +947 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
name: Lint and type-check
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
22
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
- name: Install
|
|
26
|
+
run: uv sync --all-extras
|
|
27
|
+
- name: Ruff format check
|
|
28
|
+
run: uv run ruff format --check .
|
|
29
|
+
- name: Ruff lint
|
|
30
|
+
run: uv run ruff check --output-format=github .
|
|
31
|
+
- name: Mypy
|
|
32
|
+
run: uv run mypy
|
|
33
|
+
|
|
34
|
+
test:
|
|
35
|
+
name: Test (py${{ matrix.python-version }})
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
strategy:
|
|
38
|
+
fail-fast: false
|
|
39
|
+
matrix:
|
|
40
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
43
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
44
|
+
with:
|
|
45
|
+
enable-cache: true
|
|
46
|
+
- name: Install
|
|
47
|
+
run: uv sync --all-extras --python ${{ matrix.python-version }}
|
|
48
|
+
- name: Pytest
|
|
49
|
+
run: uv run --python ${{ matrix.python-version }} pytest --cov --cov-report=term-missing --cov-fail-under=90
|
|
50
|
+
|
|
51
|
+
packaging:
|
|
52
|
+
name: OSS packaging gates
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
56
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
57
|
+
with:
|
|
58
|
+
enable-cache: true
|
|
59
|
+
- name: Runtime license allowlist
|
|
60
|
+
run: uv run python scripts/check_license_allowlist.py
|
|
61
|
+
- name: Identity consolidation + wheel contents
|
|
62
|
+
run: uv run python scripts/check_identity.py
|
|
63
|
+
- name: SBOM generation + structural SPDX-2.3 validation
|
|
64
|
+
run: |
|
|
65
|
+
uv run python scripts/gen_sbom.py --out sbom.spdx.json
|
|
66
|
+
uv run python scripts/validate_sbom.py --sbom sbom.spdx.json
|
|
67
|
+
- name: Validate neutral example taskset
|
|
68
|
+
run: uv run surgeval tasksets validate docs/examples/tasksets/consequence-rank-v1
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Droid Auto Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, ready_for_review, reopened]
|
|
6
|
+
|
|
7
|
+
concurrency:
|
|
8
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
9
|
+
cancel-in-progress: true
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
droid-review:
|
|
13
|
+
if: github.event.pull_request.draft == false
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
|
17
|
+
pull-requests: write
|
|
18
|
+
issues: write
|
|
19
|
+
id-token: write
|
|
20
|
+
actions: read
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout repository
|
|
23
|
+
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 1
|
|
26
|
+
- name: Run Droid Auto Review
|
|
27
|
+
uses: Factory-AI/droid-action@c6951275d02429bb172eadc7302352d21b45a694 # main
|
|
28
|
+
with:
|
|
29
|
+
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
|
|
30
|
+
automatic_review: true
|
|
31
|
+
automatic_security_review: true
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: Droid Tag
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issue_comment:
|
|
5
|
+
types: [created]
|
|
6
|
+
pull_request_review_comment:
|
|
7
|
+
types: [created]
|
|
8
|
+
issues:
|
|
9
|
+
types: [opened, assigned]
|
|
10
|
+
pull_request_review:
|
|
11
|
+
types: [submitted]
|
|
12
|
+
pull_request:
|
|
13
|
+
types: [opened, edited]
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
droid:
|
|
17
|
+
if: |
|
|
18
|
+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@droid')) ||
|
|
19
|
+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@droid')) ||
|
|
20
|
+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@droid')) ||
|
|
21
|
+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@droid') || contains(github.event.issue.title, '@droid'))) ||
|
|
22
|
+
(github.event_name == 'pull_request' && (contains(github.event.pull_request.body, '@droid') || contains(github.event.pull_request.title, '@droid')))
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
pull-requests: write
|
|
27
|
+
issues: write
|
|
28
|
+
id-token: write
|
|
29
|
+
actions: read
|
|
30
|
+
steps:
|
|
31
|
+
- name: Checkout repository
|
|
32
|
+
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
33
|
+
with:
|
|
34
|
+
fetch-depth: 1
|
|
35
|
+
- name: Run Droid Exec
|
|
36
|
+
uses: Factory-AI/droid-action@c6951275d02429bb172eadc7302352d21b45a694 # main
|
|
37
|
+
with:
|
|
38
|
+
factory_api_key: ${{ secrets.FACTORY_API_KEY }}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
name: install-smoke
|
|
2
|
+
|
|
3
|
+
# Deliberately a separate workflow from kernel CI (§6.5 pattern).
|
|
4
|
+
#
|
|
5
|
+
# Kernel CI must stay fast, hermetic, and green on every PR: it proves the
|
|
6
|
+
# harness contract. This workflow proves something different and structurally
|
|
7
|
+
# flakier — that the *install ladder* still works against the outside world.
|
|
8
|
+
# Its jobs depend on registries, vendor accounts, EULA gates, and GPU hosts we
|
|
9
|
+
# do not control, so a broken upstream must never be able to red-flag a kernel
|
|
10
|
+
# change. Hence: no `push`/`pull_request` trigger, manual dispatch plus a
|
|
11
|
+
# weekly schedule, and a plan-only default for anything that would touch a
|
|
12
|
+
# third-party registry.
|
|
13
|
+
#
|
|
14
|
+
# One job per install strategy *the catalog actually uses*, discovered from the
|
|
15
|
+
# data rather than hardcoded here. The list moved once already — the audit
|
|
16
|
+
# deleted both `pip-extra` rows (neither package is on PyPI) and added
|
|
17
|
+
# `source-build` — and a hardcoded matrix answered that by failing on an empty
|
|
18
|
+
# strategy instead of by testing the new one. Plus a real `surgeval quickstart`
|
|
19
|
+
# job, the only one that runs a world end to end (CPU-only, no sim deps, no GPU).
|
|
20
|
+
#
|
|
21
|
+
# Until a strategy's entries are license-audited and pin-recorded, its plan
|
|
22
|
+
# step *refuses*. That is a pass here: the ladder's job is to name the missing
|
|
23
|
+
# artifact, and this workflow asserts it names one rather than guessing.
|
|
24
|
+
|
|
25
|
+
on:
|
|
26
|
+
workflow_dispatch:
|
|
27
|
+
schedule:
|
|
28
|
+
# Mondays 05:17 UTC. Off-hour so a registry hiccup does not land in
|
|
29
|
+
# the middle of a review cycle.
|
|
30
|
+
- cron: "17 5 * * 1"
|
|
31
|
+
|
|
32
|
+
permissions:
|
|
33
|
+
contents: read
|
|
34
|
+
|
|
35
|
+
concurrency:
|
|
36
|
+
group: install-smoke-${{ github.ref }}
|
|
37
|
+
cancel-in-progress: true
|
|
38
|
+
|
|
39
|
+
jobs:
|
|
40
|
+
signals:
|
|
41
|
+
name: audited signal citations resolve at their pins
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
# Lives here, not in kernel CI, for the same reason as the ladder: it
|
|
44
|
+
# fetches third-party source trees over the network (git, against each
|
|
45
|
+
# catalog pin), so it must not sit in front of a PR. It is also the only
|
|
46
|
+
# check that can catch the class of defect it was written
|
|
47
|
+
# for - a citation pointing at code the pinned revision does not contain -
|
|
48
|
+
# because a prose evidence field cannot be checked by a machine.
|
|
49
|
+
#
|
|
50
|
+
# It fails closed, and being off the PR path is exactly what buys that: an
|
|
51
|
+
# unreachable upstream exits 2 and turns this job red. The alternative was
|
|
52
|
+
# tried and was worse - the first version printed SKIP and still exited 0
|
|
53
|
+
# as long as some other world checked, so a registry outage could have
|
|
54
|
+
# reported success while verifying none of the seven wrap targets. A green
|
|
55
|
+
# tick here means all of them were fetched and checked, or it means
|
|
56
|
+
# nothing at all.
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
59
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
60
|
+
with:
|
|
61
|
+
enable-cache: true
|
|
62
|
+
- name: Verify every audited env path and signal line at its world pin
|
|
63
|
+
# Exit 1 = a citation does not resolve. Exit 2 = something could not be
|
|
64
|
+
# checked. Neither is success; do not add continue-on-error here.
|
|
65
|
+
run: uv run python scripts/check_world_signals.py
|
|
66
|
+
|
|
67
|
+
quickstart:
|
|
68
|
+
name: quickstart (CPU-only, no sim deps)
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
72
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
73
|
+
with:
|
|
74
|
+
enable-cache: true
|
|
75
|
+
|
|
76
|
+
# The N10 contract is "first vector in minutes on any machine" in <=3
|
|
77
|
+
# commands, so this installs the distribution the way a user would rather
|
|
78
|
+
# than syncing the dev environment. The wheel force-includes the
|
|
79
|
+
# reference pair under `or_audit/_examples`, so every step below runs
|
|
80
|
+
# from a scratch directory with no checkout in sight: if quickstart only
|
|
81
|
+
# worked inside a clone, the ladder's first rung would be a lie.
|
|
82
|
+
- name: Install the distribution the way a user would
|
|
83
|
+
run: |
|
|
84
|
+
uv tool install .
|
|
85
|
+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
86
|
+
|
|
87
|
+
- name: Leave the checkout
|
|
88
|
+
run: mkdir -p "$RUNNER_TEMP/elsewhere"
|
|
89
|
+
|
|
90
|
+
- name: Doctor
|
|
91
|
+
working-directory: ${{ runner.temp }}/elsewhere
|
|
92
|
+
run: surgeval doctor
|
|
93
|
+
|
|
94
|
+
- name: Quickstart and record time-to-first-vector
|
|
95
|
+
working-directory: ${{ runner.temp }}/elsewhere
|
|
96
|
+
run: |
|
|
97
|
+
surgeval quickstart --out "$RUNNER_TEMP/quickstart" --json \
|
|
98
|
+
| tee "$RUNNER_TEMP/ttfv.json"
|
|
99
|
+
|
|
100
|
+
- name: Assert the metric is real and the vector replays
|
|
101
|
+
working-directory: ${{ runner.temp }}/elsewhere
|
|
102
|
+
run: |
|
|
103
|
+
python3 - "$RUNNER_TEMP/ttfv.json" > "$RUNNER_TEMP/replay.args" <<'PY'
|
|
104
|
+
import json
|
|
105
|
+
import sys
|
|
106
|
+
|
|
107
|
+
with open(sys.argv[1], encoding="utf-8") as handle:
|
|
108
|
+
record = json.load(handle)
|
|
109
|
+
assert record["time_to_first_vector_sec"] > 0, record
|
|
110
|
+
assert record["task_id"], record
|
|
111
|
+
print(record["out"], record["head"])
|
|
112
|
+
PY
|
|
113
|
+
read -r OUT HEAD < "$RUNNER_TEMP/replay.args"
|
|
114
|
+
surgeval replay "$OUT" --expect-head "$HEAD"
|
|
115
|
+
|
|
116
|
+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
117
|
+
with:
|
|
118
|
+
name: time-to-first-vector
|
|
119
|
+
path: ${{ runner.temp }}/ttfv.json
|
|
120
|
+
|
|
121
|
+
strategies:
|
|
122
|
+
name: discover strategies in use
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
outputs:
|
|
125
|
+
list: ${{ steps.read.outputs.list }}
|
|
126
|
+
steps:
|
|
127
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
128
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
129
|
+
with:
|
|
130
|
+
enable-cache: true
|
|
131
|
+
- name: Install
|
|
132
|
+
run: uv sync --all-extras
|
|
133
|
+
- name: Read the strategies the catalog uses
|
|
134
|
+
id: read
|
|
135
|
+
run: |
|
|
136
|
+
uv run python - <<'PY' >> "$GITHUB_OUTPUT"
|
|
137
|
+
import json
|
|
138
|
+
|
|
139
|
+
from or_audit.install.catalog import load_catalog
|
|
140
|
+
|
|
141
|
+
used = sorted({pkg.strategy.value for pkg in load_catalog().worlds})
|
|
142
|
+
assert used, "catalog has no worlds at all"
|
|
143
|
+
print(f"list={json.dumps(used)}")
|
|
144
|
+
PY
|
|
145
|
+
|
|
146
|
+
ladder:
|
|
147
|
+
name: plan ${{ matrix.strategy }}
|
|
148
|
+
runs-on: ubuntu-latest
|
|
149
|
+
needs: strategies
|
|
150
|
+
strategy:
|
|
151
|
+
fail-fast: false
|
|
152
|
+
matrix:
|
|
153
|
+
strategy: ${{ fromJSON(needs.strategies.outputs.list) }}
|
|
154
|
+
steps:
|
|
155
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
156
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
157
|
+
with:
|
|
158
|
+
enable-cache: true
|
|
159
|
+
- name: Install
|
|
160
|
+
run: uv sync --all-extras
|
|
161
|
+
|
|
162
|
+
- name: List the worlds using this strategy
|
|
163
|
+
run: uv run surgeval worlds list --strategy "${{ matrix.strategy }}"
|
|
164
|
+
|
|
165
|
+
- name: Plan every install for this strategy (dry run, no network)
|
|
166
|
+
env:
|
|
167
|
+
STRATEGY: ${{ matrix.strategy }}
|
|
168
|
+
run: |
|
|
169
|
+
uv run python - <<'PY'
|
|
170
|
+
import os
|
|
171
|
+
import subprocess
|
|
172
|
+
import sys
|
|
173
|
+
|
|
174
|
+
from or_audit.install.catalog import (
|
|
175
|
+
InstallStrategy,
|
|
176
|
+
VendorRuntimeInstall,
|
|
177
|
+
iter_packages,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
strategy = InstallStrategy(os.environ["STRATEGY"])
|
|
181
|
+
packages = iter_packages(strategy=strategy)
|
|
182
|
+
assert packages, f"catalog has no {strategy.value} entries"
|
|
183
|
+
|
|
184
|
+
planned, refused = 0, 0
|
|
185
|
+
for pkg in packages:
|
|
186
|
+
# Accepting the vendor EULA in CI is exactly what a user does
|
|
187
|
+
# interactively, and only a vendor runtime has one to accept. The
|
|
188
|
+
# plan still pulls nothing without --execute. Derived per package
|
|
189
|
+
# rather than per matrix row so a mixed strategy cannot silently
|
|
190
|
+
# skip the acceptance path.
|
|
191
|
+
extra = (
|
|
192
|
+
["--accept-vendor-eula"]
|
|
193
|
+
if isinstance(pkg.install, VendorRuntimeInstall)
|
|
194
|
+
else []
|
|
195
|
+
)
|
|
196
|
+
argv = ["uv", "run", "surgeval", "worlds", "install", pkg.id, *extra]
|
|
197
|
+
done = subprocess.run(argv, capture_output=True, text=True)
|
|
198
|
+
sys.stdout.write(done.stdout)
|
|
199
|
+
sys.stderr.write(done.stderr)
|
|
200
|
+
if done.returncode == 0:
|
|
201
|
+
planned += 1
|
|
202
|
+
# A dry run must never execute anything.
|
|
203
|
+
assert "dry run" in done.stdout, pkg.id
|
|
204
|
+
elif done.returncode == 1:
|
|
205
|
+
refused += 1
|
|
206
|
+
# A refusal is a pass only when it names the fix.
|
|
207
|
+
assert "REFUSED:" in done.stderr, pkg.id
|
|
208
|
+
assert "Fix:" in done.stderr or "re-run with" in done.stderr, pkg.id
|
|
209
|
+
else:
|
|
210
|
+
raise SystemExit(f"{pkg.id}: unexpected exit {done.returncode}")
|
|
211
|
+
print(f"{strategy.value}: {planned} planned, {refused} refused with a named fix")
|
|
212
|
+
PY
|
|
213
|
+
|
|
214
|
+
- name: Doctor for each world using this strategy
|
|
215
|
+
env:
|
|
216
|
+
STRATEGY: ${{ matrix.strategy }}
|
|
217
|
+
run: |
|
|
218
|
+
uv run python - <<'PY'
|
|
219
|
+
import os
|
|
220
|
+
import subprocess
|
|
221
|
+
|
|
222
|
+
from or_audit.install.catalog import Disposition, InstallStrategy, iter_packages
|
|
223
|
+
|
|
224
|
+
strategy = InstallStrategy(os.environ["STRATEGY"])
|
|
225
|
+
for pkg in iter_packages(strategy=strategy):
|
|
226
|
+
if pkg.disposition not in {Disposition.SHIPPED, Disposition.WRAP}:
|
|
227
|
+
continue
|
|
228
|
+
# Exit 1 is expected on a runner with no SOFA/Isaac; the point is
|
|
229
|
+
# that every reported problem carries a fix, which the report's
|
|
230
|
+
# own contract enforces.
|
|
231
|
+
done = subprocess.run(
|
|
232
|
+
["uv", "run", "surgeval", "doctor", "--world", pkg.id, "--json"],
|
|
233
|
+
capture_output=True,
|
|
234
|
+
text=True,
|
|
235
|
+
)
|
|
236
|
+
assert done.returncode in {0, 1}, (pkg.id, done.returncode, done.stderr)
|
|
237
|
+
print(pkg.id, "->", "ok" if done.returncode == 0 else "needs work")
|
|
238
|
+
PY
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
# Publisher-signature verification at the release-artifact layer.
|
|
4
|
+
#
|
|
5
|
+
# Trust model: no dormant in-band signing key. Publisher provenance is
|
|
6
|
+
# established out-of-band and enforced end to end:
|
|
7
|
+
#
|
|
8
|
+
# 1. PyPI trusted publishing (OIDC) — secretless upload; GitHub's OIDC
|
|
9
|
+
# id-token proves workflow identity to PyPI.
|
|
10
|
+
# 2. Sigstore build attestations, two independent records over the SAME
|
|
11
|
+
# built bytes:
|
|
12
|
+
# - a GitHub Artifact Attestation (``actions/attest-build-provenance``),
|
|
13
|
+
# verified cryptographically by ``gh attestation verify`` against
|
|
14
|
+
# Sigstore's public-good root (this is the publisher-signature check);
|
|
15
|
+
# - a PEP 740 attestation stored on PyPI by ``gh-action-pypi-publish``,
|
|
16
|
+
# whose *presence* and digest-consistency are confirmed by
|
|
17
|
+
# ``scripts/check_pypi_attestation.py`` (the crypto verification of the
|
|
18
|
+
# same bytes is the GitHub attestation above).
|
|
19
|
+
#
|
|
20
|
+
# Least privilege: only jobs that publish/attest hold ``id-token``; the build
|
|
21
|
+
# job that executes project code is unprivileged and hands over immutable
|
|
22
|
+
# artifacts via upload/download. Project code is never built or installed in a
|
|
23
|
+
# job that holds OIDC release credentials.
|
|
24
|
+
#
|
|
25
|
+
# Task-registry (taskset/agent) signature verification is a separate trust
|
|
26
|
+
# domain; see docs/OSS_GOVERNANCE.md.
|
|
27
|
+
|
|
28
|
+
on:
|
|
29
|
+
push:
|
|
30
|
+
tags: ["v*"]
|
|
31
|
+
workflow_dispatch:
|
|
32
|
+
inputs:
|
|
33
|
+
version:
|
|
34
|
+
description: "Release version (e.g. 0.4.0). Must match pyproject/package version."
|
|
35
|
+
required: true
|
|
36
|
+
type: string
|
|
37
|
+
|
|
38
|
+
permissions:
|
|
39
|
+
contents: read
|
|
40
|
+
|
|
41
|
+
jobs:
|
|
42
|
+
build:
|
|
43
|
+
# Manual releases must originate on main; the tag-triggered path is the
|
|
44
|
+
# sole other entry (pushed v* tags). Skipping this job cascades to every
|
|
45
|
+
# dependent job, so a dispatch from a feature branch runs nothing.
|
|
46
|
+
if: github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main'
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
outputs:
|
|
49
|
+
version: ${{ steps.version.outputs.value }}
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
52
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
53
|
+
with:
|
|
54
|
+
enable-cache: true
|
|
55
|
+
- name: Resolve release version
|
|
56
|
+
id: version
|
|
57
|
+
env:
|
|
58
|
+
DISPATCH_VERSION: ${{ github.event.inputs.version }}
|
|
59
|
+
run: |
|
|
60
|
+
if [ "${{ github.event_name }}" = "push" ]; then
|
|
61
|
+
echo "value=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
|
62
|
+
else
|
|
63
|
+
# The dispatch input is user-controlled; never splice it into a
|
|
64
|
+
# shell program. Validate it looks like a version, then pass it
|
|
65
|
+
# through the environment rather than the command string.
|
|
66
|
+
if ! printf '%s' "$DISPATCH_VERSION" | grep -Eq '^[0-9][A-Za-z0-9._+]*$'; then
|
|
67
|
+
echo "invalid release version: $DISPATCH_VERSION" >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
echo "value=$DISPATCH_VERSION" >> "$GITHUB_OUTPUT"
|
|
71
|
+
fi
|
|
72
|
+
- name: Enforce pre-publication version gate
|
|
73
|
+
run: uv run python scripts/check_identity.py --expected-version "${{ steps.version.outputs.value }}"
|
|
74
|
+
- name: Install
|
|
75
|
+
run: uv sync --all-extras
|
|
76
|
+
- name: Test
|
|
77
|
+
run: uv run pytest -q
|
|
78
|
+
- name: Verify runtime licenses (commercial allowlist)
|
|
79
|
+
run: uv run python scripts/check_license_allowlist.py
|
|
80
|
+
- name: Verify identity consolidation
|
|
81
|
+
run: uv run python scripts/check_identity.py
|
|
82
|
+
- name: Build sdist + wheel
|
|
83
|
+
run: uv build
|
|
84
|
+
- name: Twine metadata check
|
|
85
|
+
run: uvx twine check dist/*
|
|
86
|
+
- name: Generate SPDX SBOM
|
|
87
|
+
run: uv run python scripts/gen_sbom.py --out sbom.spdx.json
|
|
88
|
+
- name: Validate SBOM against SPDX 2.3 schema
|
|
89
|
+
run: |
|
|
90
|
+
curl -fsSL \
|
|
91
|
+
https://raw.githubusercontent.com/spdx/spdx-spec/v2.3/schemas/spdx-schema.json \
|
|
92
|
+
-o spdx-schema.json
|
|
93
|
+
uv pip install jsonschema
|
|
94
|
+
uv run python scripts/validate_sbom.py --sbom sbom.spdx.json --schema spdx-schema.json
|
|
95
|
+
- name: Record artifact digests
|
|
96
|
+
run: sha256sum dist/* sbom.spdx.json > RELEASE_DIGESTS.txt
|
|
97
|
+
- name: Upload build artifacts
|
|
98
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
99
|
+
with:
|
|
100
|
+
name: surgeval-dist
|
|
101
|
+
path: |
|
|
102
|
+
dist/
|
|
103
|
+
sbom.spdx.json
|
|
104
|
+
RELEASE_DIGESTS.txt
|
|
105
|
+
|
|
106
|
+
attest:
|
|
107
|
+
name: Attest + verify (GitHub Sigstore)
|
|
108
|
+
runs-on: ubuntu-latest
|
|
109
|
+
needs: build
|
|
110
|
+
permissions:
|
|
111
|
+
contents: read
|
|
112
|
+
id-token: write
|
|
113
|
+
attestations: write
|
|
114
|
+
steps:
|
|
115
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
116
|
+
with:
|
|
117
|
+
name: surgeval-dist
|
|
118
|
+
- name: Attest build provenance (GitHub artifact attestation)
|
|
119
|
+
uses: actions/attest-build-provenance@ef244123eb79f2f7a7e75d99086184180e6d0018 # v1
|
|
120
|
+
with:
|
|
121
|
+
subject-path: dist/*
|
|
122
|
+
- name: Verify GitHub attestation (Sigstore public-good root)
|
|
123
|
+
env:
|
|
124
|
+
GH_TOKEN: ${{ github.token }}
|
|
125
|
+
run: |
|
|
126
|
+
for file in dist/*; do
|
|
127
|
+
gh attestation verify "$file" \
|
|
128
|
+
--repo "${{ github.repository }}" \
|
|
129
|
+
--predicate-type https://slsa.dev/provenance/v1
|
|
130
|
+
done
|
|
131
|
+
|
|
132
|
+
publish:
|
|
133
|
+
name: Publish to PyPI (minimal, OIDC)
|
|
134
|
+
runs-on: ubuntu-latest
|
|
135
|
+
needs: attest
|
|
136
|
+
environment: release
|
|
137
|
+
permissions:
|
|
138
|
+
contents: read
|
|
139
|
+
id-token: write
|
|
140
|
+
steps:
|
|
141
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
142
|
+
with:
|
|
143
|
+
name: surgeval-dist
|
|
144
|
+
- name: Publish to PyPI (trusted publishing + PEP 740 attestations)
|
|
145
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
146
|
+
with:
|
|
147
|
+
attestations: "true"
|
|
148
|
+
|
|
149
|
+
verify-pypi:
|
|
150
|
+
name: Check PyPI PEP 740 bundle + digest (unprivileged)
|
|
151
|
+
runs-on: ubuntu-latest
|
|
152
|
+
needs: [publish, build]
|
|
153
|
+
permissions:
|
|
154
|
+
contents: read
|
|
155
|
+
steps:
|
|
156
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
157
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
158
|
+
with:
|
|
159
|
+
enable-cache: true
|
|
160
|
+
- name: Sync (for scripts + uv)
|
|
161
|
+
run: uv sync --no-dev
|
|
162
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
163
|
+
with:
|
|
164
|
+
name: surgeval-dist
|
|
165
|
+
- name: Check PyPI serves built digests with PEP 740 bundles present
|
|
166
|
+
run: |
|
|
167
|
+
version="${{ needs.build.outputs.version }}"
|
|
168
|
+
# PEP 740 index entry may lag upload by a few seconds; retry before failing.
|
|
169
|
+
for attempt in 1 2 3 4 5; do
|
|
170
|
+
if uv run python scripts/check_pypi_attestation.py \
|
|
171
|
+
"$version" RELEASE_DIGESTS.txt \
|
|
172
|
+
--publisher-repo "${{ github.repository }}"; then
|
|
173
|
+
exit 0
|
|
174
|
+
fi
|
|
175
|
+
echo "provenance not yet visible (attempt $attempt); retrying"
|
|
176
|
+
sleep 10
|
|
177
|
+
done
|
|
178
|
+
exit 1
|
|
179
|
+
|
|
180
|
+
release:
|
|
181
|
+
runs-on: ubuntu-latest
|
|
182
|
+
needs: [attest, verify-pypi, build]
|
|
183
|
+
permissions:
|
|
184
|
+
contents: write
|
|
185
|
+
steps:
|
|
186
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
187
|
+
with:
|
|
188
|
+
name: surgeval-dist
|
|
189
|
+
- name: Create GitHub Release
|
|
190
|
+
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
|
|
191
|
+
with:
|
|
192
|
+
tag_name: ${{ github.event_name == 'push' && github.ref_name || format('v{0}', needs.build.outputs.version) }}
|
|
193
|
+
files: |
|
|
194
|
+
dist/surgeval-*.whl
|
|
195
|
+
dist/surgeval-*.tar.gz
|
|
196
|
+
sbom.spdx.json
|
|
197
|
+
RELEASE_DIGESTS.txt
|
|
198
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: SBOM
|
|
2
|
+
|
|
3
|
+
# On-demand / branch SPDX SBOM generation: resolves uv.lock into an SPDX 2.3
|
|
4
|
+
# JSON document, validates it against the official SPDX 2.3 JSON schema plus
|
|
5
|
+
# the SurgEval structural checks, gates the license allowlist, and uploads the
|
|
6
|
+
# SBOM as an artifact. The release path (Publish.yml) also generates and
|
|
7
|
+
# validates the SBOM; this workflow covers ad-hoc dispatch and branch pushes.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
push:
|
|
12
|
+
branches: [main]
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
sbom:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
22
|
+
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
- name: Install
|
|
26
|
+
run: uv sync --all-extras
|
|
27
|
+
- name: Generate SPDX SBOM
|
|
28
|
+
run: uv run python scripts/gen_sbom.py --out sbom.spdx.json
|
|
29
|
+
- name: Validate against SPDX 2.3 schema
|
|
30
|
+
run: |
|
|
31
|
+
curl -fsSL \
|
|
32
|
+
https://raw.githubusercontent.com/spdx/spdx-spec/v2.3/schemas/spdx-schema.json \
|
|
33
|
+
-o spdx-schema.json
|
|
34
|
+
uv pip install jsonschema
|
|
35
|
+
uv run python scripts/validate_sbom.py --sbom sbom.spdx.json --schema spdx-schema.json
|
|
36
|
+
- name: License allowlist gate
|
|
37
|
+
run: uv run python scripts/check_license_allowlist.py
|
|
38
|
+
- name: Upload SBOM
|
|
39
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
40
|
+
with:
|
|
41
|
+
name: surgeval-sbom
|
|
42
|
+
path: sbom.spdx.json
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
|
|
8
|
+
# Environments
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
|
|
12
|
+
# Tooling caches
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
.coverage.*
|
|
18
|
+
htmlcov/
|
|
19
|
+
coverage.xml
|
|
20
|
+
|
|
21
|
+
# Editors / OS
|
|
22
|
+
.DS_Store
|
|
23
|
+
.idea/
|
|
24
|
+
.vscode/
|
|
25
|
+
|
|
26
|
+
# Agent tool state
|
|
27
|
+
.opencode/
|
|
28
|
+
|
|
29
|
+
# Local data. Surgical media must never enter version control.
|
|
30
|
+
/data/
|
|
31
|
+
/scratch/
|
|
32
|
+
*.mp4
|
|
33
|
+
*.mkv
|
|
34
|
+
*.mov
|
|
35
|
+
*.avi
|
|
36
|
+
*.dcm
|
|
37
|
+
|
|
38
|
+
.projects/cache
|
|
39
|
+
.projects/vault
|
|
40
|
+
.projects/state.test.json
|
|
41
|
+
.projects/state.local.test.json
|
|
42
|
+
.env
|
|
43
|
+
.env.*
|
|
44
|
+
!.env.example
|
|
45
|
+
.projects/
|
|
46
|
+
.pnpm-store/
|