From b8e5916fb7324f6bf6094e86bdd31516c94d697b Mon Sep 17 00:00:00 2001 From: Rafael Soler Date: Wed, 22 Jul 2026 17:56:30 +0200 Subject: [PATCH] feat(depmap): add portable report snapshot exporter --- scripts/export_depmap_release_snapshot.py | 24 ++ .../functional_dependency/report_snapshot.py | 236 ++++++++++++++++++ .../release_configuration_identity.json | 1 + .../real-v6-release-closure-summary.json | 1 + ...eal-v6-run-a-vs-run-b-reproducibility.json | 1 + .../run/activation_readiness_summary.json | 1 + .../run/artifact_compatibility.json | 1 + .../run/benchmark/benchmark_coverage.json | 1 + .../run/benchmark/benchmark_report.md | 3 + .../run/integration/activation_readiness.json | 1 + .../integration/baseline_preservation.json | 1 + .../run/integration/candidate_overlay.tsv | 2 + .../integration_gate_decision.json | 1 + .../run/integration/integration_report.md | 3 + .../profiles/dependency_profile_summary.tsv | 2 + .../run/profiles/dependency_profiles.jsonl | 2 + .../run/release_closure_manifest.json | 1 + .../run/release_preflight.json | 1 + .../run/release_readiness.json | 1 + .../report_snapshot/run/release_report.md | 3 + .../run/reproducibility_summary.json | 1 + tests/test_depmap_report_snapshot.py | 150 +++++++++++ 22 files changed, 438 insertions(+) create mode 100644 scripts/export_depmap_release_snapshot.py create mode 100644 targetintel/functional_dependency/report_snapshot.py create mode 100644 tests/fixtures/depmap/report_snapshot/config/release_configuration_identity.json create mode 100644 tests/fixtures/depmap/report_snapshot/manifests/real-v6-release-closure-summary.json create mode 100644 tests/fixtures/depmap/report_snapshot/manifests/real-v6-run-a-vs-run-b-reproducibility.json create mode 100644 tests/fixtures/depmap/report_snapshot/run/activation_readiness_summary.json create mode 100644 tests/fixtures/depmap/report_snapshot/run/artifact_compatibility.json create mode 100644 tests/fixtures/depmap/report_snapshot/run/benchmark/benchmark_coverage.json create mode 100644 tests/fixtures/depmap/report_snapshot/run/benchmark/benchmark_report.md create mode 100644 tests/fixtures/depmap/report_snapshot/run/integration/activation_readiness.json create mode 100644 tests/fixtures/depmap/report_snapshot/run/integration/baseline_preservation.json create mode 100644 tests/fixtures/depmap/report_snapshot/run/integration/candidate_overlay.tsv create mode 100644 tests/fixtures/depmap/report_snapshot/run/integration/integration_gate_decision.json create mode 100644 tests/fixtures/depmap/report_snapshot/run/integration/integration_report.md create mode 100644 tests/fixtures/depmap/report_snapshot/run/profiles/dependency_profile_summary.tsv create mode 100644 tests/fixtures/depmap/report_snapshot/run/profiles/dependency_profiles.jsonl create mode 100644 tests/fixtures/depmap/report_snapshot/run/release_closure_manifest.json create mode 100644 tests/fixtures/depmap/report_snapshot/run/release_preflight.json create mode 100644 tests/fixtures/depmap/report_snapshot/run/release_readiness.json create mode 100644 tests/fixtures/depmap/report_snapshot/run/release_report.md create mode 100644 tests/fixtures/depmap/report_snapshot/run/reproducibility_summary.json create mode 100644 tests/test_depmap_report_snapshot.py diff --git a/scripts/export_depmap_release_snapshot.py b/scripts/export_depmap_release_snapshot.py new file mode 100644 index 0000000..f4dcdb9 --- /dev/null +++ b/scripts/export_depmap_release_snapshot.py @@ -0,0 +1,24 @@ +"""Command-line wrapper for the portable DepMap release snapshot exporter.""" +from __future__ import annotations +import argparse +from pathlib import Path +import sys + +# Permit direct invocation from a source checkout without an installed package. +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from targetintel.functional_dependency.report_snapshot import DEFAULT_SELECTED_TARGETS, DepMapReportSnapshotError, export_depmap_report_snapshot + +def main() -> int: + parser = argparse.ArgumentParser(description="Export a portable DepMap release snapshot.") + parser.add_argument("--run-dir", required=True); parser.add_argument("--config-dir", required=True) + parser.add_argument("--manifest-dir", required=True); parser.add_argument("--output-dir", required=True) + parser.add_argument("--selected-target", action="append", dest="selected_targets") + parser.add_argument("--overwrite", action="store_true") + args = parser.parse_args() + try: + summary = export_depmap_report_snapshot(run_dir=args.run_dir, config_dir=args.config_dir, manifest_dir=args.manifest_dir, output_dir=args.output_dir, selected_targets=tuple(args.selected_targets or DEFAULT_SELECTED_TARGETS), overwrite=args.overwrite) + except DepMapReportSnapshotError as error: + parser.error(str(error)) + print("Exported portable DepMap snapshot: " + summary["release_identifier"] + "; selected targets: " + str(len(summary["selected_targets"]))) + return 0 +if __name__ == "__main__": raise SystemExit(main()) diff --git a/targetintel/functional_dependency/report_snapshot.py b/targetintel/functional_dependency/report_snapshot.py new file mode 100644 index 0000000..fc09124 --- /dev/null +++ b/targetintel/functional_dependency/report_snapshot.py @@ -0,0 +1,236 @@ +"""Portable, fail-closed export of a completed DepMap release closure. + +This module deliberately reads only small closure artifacts and selected +profile records. It performs no DepMap calculation, ranking, or activation. +""" +from __future__ import annotations + +import csv +from hashlib import sha256 +import json +from pathlib import Path +import re +import shutil +import tempfile +from typing import Any, Iterable, Mapping + + +DEFAULT_SELECTED_TARGETS = ( + "CTLA4", "PDCD1", "CD274", "LAG3", "B2M", "JAK1", "JAK2", "BRAF", + "PTEN", "MITF", "TERT", "IL2RA", "GNAQ", "GNA11", +) +_OUTPUT_NAMES = ( + "README.md", "release_summary.json", "release_report.md", "benchmark_report.md", + "integration_report.md", "candidate_overlay.tsv", "dependency_profile_summary.tsv", + "selected_target_profiles.tsv", "checksums.json", +) +_INPUT_NAMES = ( + "release_preflight.json", "artifact_compatibility.json", "release_readiness.json", + "reproducibility_summary.json", "release_report.md", "release_closure_manifest.json", + "activation_readiness_summary.json", "profiles/dependency_profile_summary.tsv", + "profiles/dependency_profiles.jsonl", "integration/candidate_overlay.tsv", + "integration/baseline_preservation.json", "integration/integration_gate_decision.json", + "integration/activation_readiness.json", "integration/integration_report.md", + "benchmark/benchmark_report.md", "benchmark/benchmark_coverage.json", + "manifests/real-v6-release-closure-summary.json", + "manifests/real-v6-run-a-vs-run-b-reproducibility.json", +) +_PATH_LEAK = re.compile(r"(?:/home/|/media/|/mnt/|/tmp/|/Users/|/Volumes/|[A-Za-z]:[\\/])") +_PATH_VALUE = re.compile(r"(?:[A-Za-z]:[\\/][^\s`'\"<>]+|/(?:home|media|mnt|tmp|Users|Volumes)/[^\s`'\"<>]+)") + + +class DepMapReportSnapshotError(ValueError): + """A sanitized validation or publication failure.""" + + +def _fail(message: str) -> None: + raise DepMapReportSnapshotError(message) + + +def _json(path: Path) -> dict[str, Any]: + try: + value = json.loads(path.read_text(encoding="utf-8")) + except (OSError, UnicodeError, json.JSONDecodeError) as error: + raise DepMapReportSnapshotError("required JSON artifact is malformed") from error + if not isinstance(value, dict): + _fail("required JSON artifact must be an object") + return value + + +def _need(value: Mapping[str, Any], key: str, expected: Any = None) -> Any: + if key not in value: + _fail("required release invariant is missing: " + key) + result = value[key] + if expected is not None and result != expected: + _fail("release invariant failed: " + key) + return result + + +def _one_of(values: Iterable[Any], label: str) -> Any: + present = [value for value in values if value is not None] + if not present or any(value != present[0] for value in present): + _fail("incompatible " + label) + return present[0] + + +def _safe_text(text: str) -> str: + return _PATH_VALUE.sub("[local path removed]", text) + + +def _validate_no_paths(directory: Path) -> None: + for path in directory.iterdir(): + if path.is_file() and _PATH_LEAK.search(path.read_text(encoding="utf-8")): + _fail("portable output contains a local path") + + +def _copy_text(source: Path, destination: Path) -> None: + destination.write_text(_safe_text(source.read_text(encoding="utf-8")), encoding="utf-8", newline="") + + +def _profile_rows(jsonl_path: Path, selected: tuple[str, ...]) -> list[dict[str, str]]: + """Read the profile JSONL exactly once, retaining selected records only.""" + found: dict[str, Mapping[str, Any]] = {} + try: + with jsonl_path.open(encoding="utf-8") as handle: + for line in handle: + if not line.strip(): + continue + record = json.loads(line) + target = record.get("target_identity", {}).get("normalized_request") + if target in selected and target not in found: + found[target] = record + except (OSError, UnicodeError, json.JSONDecodeError, AttributeError) as error: + raise DepMapReportSnapshotError("dependency profile JSONL is malformed") from error + rows: list[dict[str, str]] = [] + for target in selected: + record = found.get(target) + if record is None: + rows.append({"target": target, "coverage_status": "not_available"}) + continue + payload = record.get("payload") + if not isinstance(payload, Mapping): + _fail("selected dependency profile lacks a payload") + summaries = payload.get("summaries", {}) + context = summaries.get("context", {}) if isinstance(summaries, Mapping) else {} + effect = context.get("gene_effect", {}) if isinstance(context, Mapping) else {} + probability = context.get("dependency_probability", {}) if isinstance(context, Mapping) else {} + coverage = payload.get("model_coverage", {}) + row = { + "target": target, + "resolution_status": str(payload.get("target_resolution_status", "")), + "coverage_status": str(payload.get("coverage_status", "")), + "profile_status": str(record.get("terminal_status", "")), + "context_model_count": str(coverage.get("context_model_count", "")) if isinstance(coverage, Mapping) else "", + "context_gene_effect_median": str(effect.get("median", "")), + "context_dependency_probability_median": str(probability.get("median", "")), + "limitations": json.dumps(payload.get("limitations", []), sort_keys=True, separators=(",", ":")), + } + rows.append(row) + return rows + + +def _write_tsv(path: Path, fields: list[str], rows: list[Mapping[str, str]]) -> None: + with path.open("w", encoding="utf-8", newline="") as handle: + writer = csv.DictWriter(handle, fieldnames=fields, delimiter="\t", lineterminator="\n") + writer.writeheader() + writer.writerows([{field: row.get(field, "") for field in fields} for row in rows]) + + +def _validate_paths(run: Path, config: Path, manifests: Path, output: Path) -> None: + for path, label in ((run, "run_dir"), (config, "config_dir"), (manifests, "manifest_dir")): + if not path.is_dir(): + _fail(label + " must be an existing directory") + for input_dir in (run, config, manifests): + if output == input_dir or output.is_relative_to(input_dir) or input_dir.is_relative_to(output): + _fail("output directory overlaps an input directory") + if output == Path("/") or output == Path.cwd().resolve() or output.is_symlink(): + _fail("unsafe output directory") + + +def _configuration_id(config_dir: Path) -> str: + """Obtain a declared identity without exposing configuration contents.""" + identity_file = config_dir / "release_configuration_identity.json" + if identity_file.is_file(): + value = _need(_json(identity_file), "configuration_id") + if not isinstance(value, str) or not value: + _fail("configuration identity is malformed") + return value + # The actual release configuration is read only to validate its derived + # identity; it is never copied because it can contain local paths. + real_config = config_dir / "real_run_config.json" + if real_config.is_file(): + try: + from .release_closure import V050ReleaseRunConfiguration + return V050ReleaseRunConfiguration.from_file(real_config).configuration_id + except (ValueError, OSError) as error: + raise DepMapReportSnapshotError("release configuration identity is invalid") from error + _fail("configuration identity artifact is missing") + + +def export_depmap_report_snapshot(*, run_dir: Path | str, config_dir: Path | str, + manifest_dir: Path | str, output_dir: Path | str, + selected_targets: Iterable[str] = DEFAULT_SELECTED_TARGETS, + overwrite: bool = False) -> dict[str, Any]: + """Export a deterministic, repository-safe derived release snapshot.""" + raw_output = Path(output_dir).absolute() + if any(part.is_symlink() for part in (raw_output, *raw_output.parents)): + _fail("unsafe output directory") + run, config, manifests, output = (Path(value).resolve() for value in (run_dir, config_dir, manifest_dir, raw_output)) + selected = tuple(selected_targets) + if not selected or any(not isinstance(item, str) or not item for item in selected) or len(set(selected)) != len(selected): + _fail("selected targets must be unique non-empty symbols") + _validate_paths(run, config, manifests, output) + sources = {name: (manifests / name[10:] if name.startswith("manifests/") else run / name) for name in _INPUT_NAMES} + if any(not path.is_file() for path in sources.values()): + _fail("a required release artifact is missing") + preflight, compatibility, readiness = (_json(sources[name]) for name in ("release_preflight.json", "artifact_compatibility.json", "release_readiness.json")) + reproducibility, closure, activation = (_json(sources[name]) for name in ("reproducibility_summary.json", "release_closure_manifest.json", "activation_readiness_summary.json")) + baseline, gate, candidate_readiness = (_json(sources[name]) for name in ("integration/baseline_preservation.json", "integration/integration_gate_decision.json", "integration/activation_readiness.json")) + coverage, closure_summary, external = (_json(sources[name]) for name in ("benchmark/benchmark_coverage.json", "manifests/real-v6-release-closure-summary.json", "manifests/real-v6-run-a-vs-run-b-reproducibility.json")) + _need(preflight, "status", "passed"); _need(preflight, "release_identifier", "DepMap_Public_26Q1") + _need(compatibility, "compatible", True); _need(compatibility, "expected_context_identity", "melanoma_anti_pd1:v1") + configuration_id = _one_of((_configuration_id(config), _need(preflight, "configuration_id"), _need(readiness, "configuration_id"), _need(closure, "configuration_id"), reproducibility.get("configuration_id"), closure_summary.get("configuration_id")), "configuration IDs") + manifest_id = _one_of((_need(preflight, "release_manifest_id"), closure_summary.get("release_manifest_id"), external.get("release_manifest_id")), "release manifest IDs") + scientific_identity = _one_of((closure_summary.get("scientific_closure_identity"), external.get("scientific_closure_identity")), "scientific closure identities") + if not scientific_identity: + _fail("scientific closure identity is missing") + _need(reproducibility, "result", "reproducible") + if _need(reproducibility, "differing_artifacts") != []: _fail("internal differing artifacts are not empty") + _need(external, "result", "reproducible") + if external.get("differing_scientific_artifacts", external.get("differing_artifacts")) != []: _fail("external differing scientific artifacts are not empty") + _need(closure, "successful_closure", True); _need(readiness, "release_state", "ready_research_preview_human_review") + for key in ("baseline_file_bytes_unchanged", "baseline_scores_retained_exactly", "baseline_ranks_retained_exactly", "production_scoring_configurations_unchanged", "production_ranking_configurations_unchanged"): + _need(baseline, key, True) + _need(readiness, "production_activation_enabled", False); _need(closure, "production_activation_enabled", False); _need(gate, "production_activation_enabled", False) + _need(activation, "approved_authorization_emitted", False); _need(candidate_readiness, "status", "blocked") + if not str(_need(gate, "decision_state")).startswith("blocked"): + _fail("integration state is not blocked") + _need(readiness, "human_review_required", True); _need(activation, "human_review_required", True); _need(gate, "human_review_required", True); _need(candidate_readiness, "human_review_required", True) + if output.exists() and not overwrite: _fail("output directory already exists") + if output.exists() and (not output.is_dir() or output.is_symlink()): _fail("unsafe existing output directory") + parent = output.parent + temporary = Path(tempfile.mkdtemp(prefix=".depmap-report-snapshot-", dir=parent)) + try: + _copy_text(sources["release_report.md"], temporary / "release_report.md") + _copy_text(sources["benchmark/benchmark_report.md"], temporary / "benchmark_report.md") + _copy_text(sources["integration/integration_report.md"], temporary / "integration_report.md") + shutil.copyfile(sources["integration/candidate_overlay.tsv"], temporary / "candidate_overlay.tsv") + shutil.copyfile(sources["profiles/dependency_profile_summary.tsv"], temporary / "dependency_profile_summary.tsv") + profile_fields = ["target", "resolution_status", "coverage_status", "profile_status", "context_model_count", "context_gene_effect_median", "context_dependency_probability_median", "limitations"] + profiles = _profile_rows(sources["profiles/dependency_profiles.jsonl"], selected) + _write_tsv(temporary / "selected_target_profiles.tsv", profile_fields, profiles) + metrics = compatibility.get("metrics", {}) + summary = {"snapshot_format_version": "v1", "release_identifier": "DepMap_Public_26Q1", "release_manifest_id": manifest_id, "configuration_id": configuration_id, "scientific_closure_identity": scientific_identity, "context_identity": "melanoma_anti_pd1:v1", "release_state": readiness["release_state"], "preflight_status": preflight["status"], "artifact_compatibility": True, "internal_reproducibility": reproducibility["result"], "external_reproducibility": external["result"], "differing_scientific_artifact_count": 0, "baseline_preserved": True, "production_activation_enabled": False, "approved_authorization_emitted": False, "human_review_required": True, "integration_state": activation.get("integration_state"), "candidate_activation_readiness": candidate_readiness["status"], "background_count": metrics.get("background_count"), "discovery_count": metrics.get("discovery_count"), "benchmark_count": coverage.get("total_benchmark_targets", metrics.get("benchmark_count")), "benchmark_coverage": coverage.get("benchmark_coverage", metrics.get("benchmark_coverage")), "holdout_coverage": metrics.get("holdout_coverage"), "unresolved_count": metrics.get("unresolved_count"), "selected_targets": list(selected), "missing_selected_targets": [row["target"] for row in profiles if row["coverage_status"] == "not_available"], "limitations": readiness.get("limitations", []), "source_artifact_names": sorted(_INPUT_NAMES)} + (temporary / "release_summary.json").write_text(json.dumps(summary, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8", newline="") + readme = "# Portable DepMap report snapshot\n\nThis repository-safe derived snapshot records `DepMap_Public_26Q1` for melanoma anti-PD-1 context (`melanoma_anti_pd1:v1`). Configuration identity: `" + configuration_id + "`. Release manifest identity: `" + manifest_id + "`. Scientific closure identity: `" + scientific_identity + "`.\n\nThe original productive baseline contains 300 genes and remains unchanged. The discovery universe contains 331 identities; 18,531 genes were used only as background, and no 18,531-gene productive ranking was generated. Production activation is disabled and human review is mandatory.\n\nDepMap cell-line dependency is not clinical anti-PD-1 response evidence. Absence of tumor-cell dependency does not invalidate an immune target. General dependency may reflect broad essentiality, and cell lines do not reproduce the full tumor microenvironment. Full matrices and `dependency_profiles.jsonl` are excluded.\n\nFiles: `release_summary.json` records validated closure state; the three Markdown reports preserve sanitized aggregate reports; `candidate_overlay.tsv` and `dependency_profile_summary.tsv` are derived aggregate tables; `selected_target_profiles.tsv` contains only requested descriptive profiles; `checksums.json` verifies the other eight files.\n" + (temporary / "README.md").write_text(readme, encoding="utf-8", newline="") + _validate_no_paths(temporary) + records = [{"name": name, "sha256": sha256((temporary / name).read_bytes()).hexdigest(), "byte_size": (temporary / name).stat().st_size} for name in sorted(_OUTPUT_NAMES[:-1])] + (temporary / "checksums.json").write_text(json.dumps(records, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8", newline="") + _validate_no_paths(temporary) + if output.exists(): shutil.rmtree(output) + temporary.replace(output) + return summary + except Exception: + shutil.rmtree(temporary, ignore_errors=True) + raise diff --git a/tests/fixtures/depmap/report_snapshot/config/release_configuration_identity.json b/tests/fixtures/depmap/report_snapshot/config/release_configuration_identity.json new file mode 100644 index 0000000..7357641 --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/config/release_configuration_identity.json @@ -0,0 +1 @@ +{"configuration_id":"v050rc_fixture","context_identity":"melanoma_anti_pd1:v1"} diff --git a/tests/fixtures/depmap/report_snapshot/manifests/real-v6-release-closure-summary.json b/tests/fixtures/depmap/report_snapshot/manifests/real-v6-release-closure-summary.json new file mode 100644 index 0000000..a452269 --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/manifests/real-v6-release-closure-summary.json @@ -0,0 +1 @@ +{"configuration_id":"v050rc_fixture","release_manifest_id":"dmrm_fixture","scientific_closure_identity":"v050closure_fixture"} diff --git a/tests/fixtures/depmap/report_snapshot/manifests/real-v6-run-a-vs-run-b-reproducibility.json b/tests/fixtures/depmap/report_snapshot/manifests/real-v6-run-a-vs-run-b-reproducibility.json new file mode 100644 index 0000000..3c8e498 --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/manifests/real-v6-run-a-vs-run-b-reproducibility.json @@ -0,0 +1 @@ +{"release_manifest_id":"dmrm_fixture","scientific_closure_identity":"v050closure_fixture","result":"reproducible","differing_scientific_artifacts":[]} diff --git a/tests/fixtures/depmap/report_snapshot/run/activation_readiness_summary.json b/tests/fixtures/depmap/report_snapshot/run/activation_readiness_summary.json new file mode 100644 index 0000000..6424fad --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/activation_readiness_summary.json @@ -0,0 +1 @@ +{"integration_state":"blocked_insufficient_evidence","approved_authorization_emitted":false,"human_review_required":true} diff --git a/tests/fixtures/depmap/report_snapshot/run/artifact_compatibility.json b/tests/fixtures/depmap/report_snapshot/run/artifact_compatibility.json new file mode 100644 index 0000000..638f6ee --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/artifact_compatibility.json @@ -0,0 +1 @@ +{"compatible":true,"expected_context_identity":"melanoma_anti_pd1:v1","metrics":{"background_count":18531,"discovery_count":331,"benchmark_count":56,"benchmark_coverage":1.0,"holdout_coverage":1.0,"unresolved_count":0}} diff --git a/tests/fixtures/depmap/report_snapshot/run/benchmark/benchmark_coverage.json b/tests/fixtures/depmap/report_snapshot/run/benchmark/benchmark_coverage.json new file mode 100644 index 0000000..b67b053 --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/benchmark/benchmark_coverage.json @@ -0,0 +1 @@ +{"total_benchmark_targets":56,"benchmark_coverage":1.0} diff --git a/tests/fixtures/depmap/report_snapshot/run/benchmark/benchmark_report.md b/tests/fixtures/depmap/report_snapshot/run/benchmark/benchmark_report.md new file mode 100644 index 0000000..548398a --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/benchmark/benchmark_report.md @@ -0,0 +1,3 @@ +# Benchmark report + +Coverage is descriptive and not clinical validation. diff --git a/tests/fixtures/depmap/report_snapshot/run/integration/activation_readiness.json b/tests/fixtures/depmap/report_snapshot/run/integration/activation_readiness.json new file mode 100644 index 0000000..4835d3b --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/integration/activation_readiness.json @@ -0,0 +1 @@ +{"status":"blocked","human_review_required":true} diff --git a/tests/fixtures/depmap/report_snapshot/run/integration/baseline_preservation.json b/tests/fixtures/depmap/report_snapshot/run/integration/baseline_preservation.json new file mode 100644 index 0000000..18b8ee7 --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/integration/baseline_preservation.json @@ -0,0 +1 @@ +{"baseline_file_bytes_unchanged":true,"baseline_scores_retained_exactly":true,"baseline_ranks_retained_exactly":true,"production_scoring_configurations_unchanged":true,"production_ranking_configurations_unchanged":true} diff --git a/tests/fixtures/depmap/report_snapshot/run/integration/candidate_overlay.tsv b/tests/fixtures/depmap/report_snapshot/run/integration/candidate_overlay.tsv new file mode 100644 index 0000000..f904f21 --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/integration/candidate_overlay.tsv @@ -0,0 +1,2 @@ +original_target_identifier baseline_rank baseline_score +BRAF 1 0.9 diff --git a/tests/fixtures/depmap/report_snapshot/run/integration/integration_gate_decision.json b/tests/fixtures/depmap/report_snapshot/run/integration/integration_gate_decision.json new file mode 100644 index 0000000..7f83af9 --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/integration/integration_gate_decision.json @@ -0,0 +1 @@ +{"decision_state":"blocked_insufficient_evidence","human_review_required":true,"production_activation_enabled":false} diff --git a/tests/fixtures/depmap/report_snapshot/run/integration/integration_report.md b/tests/fixtures/depmap/report_snapshot/run/integration/integration_report.md new file mode 100644 index 0000000..8c9b94d --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/integration/integration_report.md @@ -0,0 +1,3 @@ +# Integration gate + +The candidate overlay remains blocked. diff --git a/tests/fixtures/depmap/report_snapshot/run/profiles/dependency_profile_summary.tsv b/tests/fixtures/depmap/report_snapshot/run/profiles/dependency_profile_summary.tsv new file mode 100644 index 0000000..a48b962 --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/profiles/dependency_profile_summary.tsv @@ -0,0 +1,2 @@ +target coverage_status +BRAF profiled diff --git a/tests/fixtures/depmap/report_snapshot/run/profiles/dependency_profiles.jsonl b/tests/fixtures/depmap/report_snapshot/run/profiles/dependency_profiles.jsonl new file mode 100644 index 0000000..f86dc78 --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/profiles/dependency_profiles.jsonl @@ -0,0 +1,2 @@ +{"target_identity":{"normalized_request":"BRAF"},"terminal_status":"valid","payload":{"target_resolution_status":"resolved_exact","coverage_status":"profiled","model_coverage":{"context_model_count":4},"summaries":{"context":{"gene_effect":{"median":-0.8},"dependency_probability":{"median":0.7}}},"limitations":["Descriptive only."]}} +{"target_identity":{"normalized_request":"PTEN"},"terminal_status":"valid","payload":{"target_resolution_status":"resolved_exact","coverage_status":"profiled","model_coverage":{"context_model_count":4},"summaries":{"context":{"gene_effect":{"median":-0.4},"dependency_probability":{"median":0.3}}},"limitations":[]}} diff --git a/tests/fixtures/depmap/report_snapshot/run/release_closure_manifest.json b/tests/fixtures/depmap/report_snapshot/run/release_closure_manifest.json new file mode 100644 index 0000000..b94c51d --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/release_closure_manifest.json @@ -0,0 +1 @@ +{"configuration_id":"v050rc_fixture","successful_closure":true,"production_activation_enabled":false} diff --git a/tests/fixtures/depmap/report_snapshot/run/release_preflight.json b/tests/fixtures/depmap/report_snapshot/run/release_preflight.json new file mode 100644 index 0000000..c4a020c --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/release_preflight.json @@ -0,0 +1 @@ +{"status":"passed","release_identifier":"DepMap_Public_26Q1","configuration_id":"v050rc_fixture","release_manifest_id":"dmrm_fixture"} diff --git a/tests/fixtures/depmap/report_snapshot/run/release_readiness.json b/tests/fixtures/depmap/report_snapshot/run/release_readiness.json new file mode 100644 index 0000000..92d8a6d --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/release_readiness.json @@ -0,0 +1 @@ +{"configuration_id":"v050rc_fixture","release_state":"ready_research_preview_human_review","production_activation_enabled":false,"human_review_required":true,"limitations":["Research-preview evidence only."]} diff --git a/tests/fixtures/depmap/report_snapshot/run/release_report.md b/tests/fixtures/depmap/report_snapshot/run/release_report.md new file mode 100644 index 0000000..f7e7738 --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/release_report.md @@ -0,0 +1,3 @@ +# Release closure + +Portable aggregate release report. diff --git a/tests/fixtures/depmap/report_snapshot/run/reproducibility_summary.json b/tests/fixtures/depmap/report_snapshot/run/reproducibility_summary.json new file mode 100644 index 0000000..5fb19dd --- /dev/null +++ b/tests/fixtures/depmap/report_snapshot/run/reproducibility_summary.json @@ -0,0 +1 @@ +{"configuration_id":"v050rc_fixture","result":"reproducible","differing_artifacts":[]} diff --git a/tests/test_depmap_report_snapshot.py b/tests/test_depmap_report_snapshot.py new file mode 100644 index 0000000..78007e2 --- /dev/null +++ b/tests/test_depmap_report_snapshot.py @@ -0,0 +1,150 @@ +from __future__ import annotations +import csv +from hashlib import sha256 +import json +from pathlib import Path +import shutil +import subprocess +import sys +import pytest +from targetintel.functional_dependency.report_snapshot import DepMapReportSnapshotError, export_depmap_report_snapshot + +FIXTURE = Path("tests/fixtures/depmap/report_snapshot") + +def copied(tmp_path: Path) -> tuple[Path, Path, Path]: + tmp_path.mkdir(parents=True, exist_ok=True) + root = tmp_path / "fixture"; shutil.copytree(FIXTURE, root) + return root / "run", root / "config", root / "manifests" + +def export(tmp_path: Path, **kwargs: object) -> Path: + run, config, manifests = copied(tmp_path) + destination = tmp_path / "snapshot" + export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=destination, selected_targets=("BRAF", "MISSING"), **kwargs) + return destination + +def tree_bytes(root: Path) -> dict[Path, bytes]: + return {path.relative_to(root): path.read_bytes() for path in root.rglob("*") if path.is_file()} + +def mutate_json(root: Path, relative: str, **changes: object) -> None: + path = root / relative + data = json.loads(path.read_text(encoding="utf-8")) + data.update(changes) + path.write_text(json.dumps(data), encoding="utf-8") + +def assert_rejected(tmp_path: Path, relative: str, **changes: object) -> None: + run, config, manifests = copied(tmp_path) + root = manifests if relative.startswith("manifests/") else run + mutate_json(root, relative.removeprefix("manifests/"), **changes) + with pytest.raises(DepMapReportSnapshotError): + export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=tmp_path / "out") + +def test_success_inventory_determinism_checksums_and_missing_profiles(tmp_path: Path) -> None: + first = export(tmp_path / "one"); second = export(tmp_path / "two") + expected = {"README.md", "release_summary.json", "release_report.md", "benchmark_report.md", "integration_report.md", "candidate_overlay.tsv", "dependency_profile_summary.tsv", "selected_target_profiles.tsv", "checksums.json"} + assert {path.name for path in first.iterdir()} == expected + assert {p.name: p.read_bytes() for p in first.iterdir()} == {p.name: p.read_bytes() for p in second.iterdir()} + rows = list(csv.DictReader((first / "selected_target_profiles.tsv").open(), delimiter="\t")) + assert [row["target"] for row in rows] == ["BRAF", "MISSING"] and rows[1]["coverage_status"] == "not_available" + checksums = json.loads((first / "checksums.json").read_text()) + assert [item["name"] for item in checksums] == sorted(expected - {"checksums.json"}) + assert all(item["sha256"] == sha256((first / item["name"]).read_bytes()).hexdigest() for item in checksums) + +@pytest.mark.parametrize("path, key, value", [("release_preflight.json", "status", "failed"), ("artifact_compatibility.json", "compatible", False), ("release_readiness.json", "release_state", "wrong"), ("reproducibility_summary.json", "result", "nonreproducible")]) +def test_invalid_closure_invariants_fail_closed(tmp_path: Path, path: str, key: str, value: object) -> None: + run, config, manifests = copied(tmp_path); target = run / path; data = json.loads(target.read_text()); data[key] = value; target.write_text(json.dumps(data)) + with pytest.raises(DepMapReportSnapshotError): export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=tmp_path / "out") + +def test_identity_activation_paths_and_output_safety(tmp_path: Path) -> None: + run, config, manifests = copied(tmp_path) + before = {"run": tree_bytes(run), "config": tree_bytes(config), "manifests": tree_bytes(manifests)} + with pytest.raises(DepMapReportSnapshotError): export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=run / "nested") + out = tmp_path / "out"; export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=out) + with pytest.raises(DepMapReportSnapshotError): export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=out) + export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=out, overwrite=True) + assert before == {"run": tree_bytes(run), "config": tree_bytes(config), "manifests": tree_bytes(manifests)} + assert not any("/home/" in p.read_text() for p in out.iterdir()) + +def test_configuration_mismatch_and_local_path_leakage_are_rejected(tmp_path: Path) -> None: + run, config, manifests = copied(tmp_path) + identity = config / "release_configuration_identity.json"; data = json.loads(identity.read_text()); data["configuration_id"] = "wrong"; identity.write_text(json.dumps(data)) + with pytest.raises(DepMapReportSnapshotError): export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=tmp_path / "bad") + identity.write_text('{"configuration_id":"v050rc_fixture"}') + (run / "release_report.md").write_text("path /home/example/private") + safe = tmp_path / "safe"; export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=safe) + assert "/home/" not in (safe / "release_report.md").read_text() + +def test_cli_matches_module_and_returns_nonzero(tmp_path: Path) -> None: + run, config, manifests = copied(tmp_path); out = tmp_path / "cli"; direct = tmp_path / "direct" + command = [sys.executable, "scripts/export_depmap_release_snapshot.py", "--run-dir", str(run), "--config-dir", str(config), "--manifest-dir", str(manifests), "--output-dir", str(out), "--selected-target", "BRAF"] + assert subprocess.run(command, capture_output=True, text=True).returncode == 0 + assert (out / "selected_target_profiles.tsv").is_file() + export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=direct, selected_targets=("BRAF",)) + assert tree_bytes(out) == tree_bytes(direct) + bad = json.loads((run / "release_preflight.json").read_text()); bad["status"] = "failed"; (run / "release_preflight.json").write_text(json.dumps(bad)) + assert subprocess.run(command + ["--overwrite"], capture_output=True, text=True).returncode != 0 + +@pytest.mark.parametrize(("relative", "changes"), [ + ("release_preflight.json", {"release_manifest_id": "wrong"}), + ("manifests/real-v6-release-closure-summary.json", {"release_manifest_id": "wrong"}), + ("manifests/real-v6-run-a-vs-run-b-reproducibility.json", {"release_manifest_id": "wrong"}), + ("manifests/real-v6-release-closure-summary.json", {"scientific_closure_identity": "wrong"}), + ("manifests/real-v6-run-a-vs-run-b-reproducibility.json", {"scientific_closure_identity": "wrong"}), + ("manifests/real-v6-run-a-vs-run-b-reproducibility.json", {"result": "not_reproducible"}), + ("reproducibility_summary.json", {"differing_artifacts": ["profile"]}), + ("manifests/real-v6-run-a-vs-run-b-reproducibility.json", {"differing_scientific_artifacts": ["profile"]}), + ("release_closure_manifest.json", {"successful_closure": False}), + ("artifact_compatibility.json", {"expected_context_identity": "wrong_context"}), + ("release_readiness.json", {"human_review_required": False}), + ("activation_readiness_summary.json", {"human_review_required": False}), + ("integration/integration_gate_decision.json", {"human_review_required": False}), + ("integration/activation_readiness.json", {"human_review_required": False}), + ("activation_readiness_summary.json", {"approved_authorization_emitted": True}), + ("release_readiness.json", {"production_activation_enabled": True}), + ("release_closure_manifest.json", {"production_activation_enabled": True}), + ("integration/integration_gate_decision.json", {"production_activation_enabled": True}), +]) +def test_identity_reproducibility_and_activation_invariants_fail_closed(tmp_path: Path, relative: str, changes: dict[str, object]) -> None: + assert_rejected(tmp_path, relative, **changes) + +@pytest.mark.parametrize("key", [ + "baseline_file_bytes_unchanged", "baseline_scores_retained_exactly", + "baseline_ranks_retained_exactly", "production_scoring_configurations_unchanged", + "production_ranking_configurations_unchanged", +]) +def test_baseline_preservation_invariants_fail_closed(tmp_path: Path, key: str) -> None: + assert_rejected(tmp_path, "integration/baseline_preservation.json", **{key: False}) + +def test_dangerous_overwrite_targets_are_rejected(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + run, config, manifests = copied(tmp_path) + kwargs = dict(run_dir=run, config_dir=config, manifest_dir=manifests, overwrite=True) + with pytest.raises(DepMapReportSnapshotError): + export_depmap_report_snapshot(output_dir=Path.cwd(), **kwargs) + with pytest.raises(DepMapReportSnapshotError): + export_depmap_report_snapshot(output_dir=Path("/"), **kwargs) + link = tmp_path / "output-link"; link.symlink_to(tmp_path / "outside") + with pytest.raises(DepMapReportSnapshotError): + export_depmap_report_snapshot(output_dir=link, **kwargs) + +def test_profiles_are_streamed_once_and_matrices_are_not_opened(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + run, config, manifests = copied(tmp_path) + profiles = run / "profiles" / "dependency_profiles.jsonl" + matrix = run / "profiles" / "complete_matrix.csv"; matrix.write_text("must not be opened") + original_read_text, original_read_bytes, original_open = Path.read_text, Path.read_bytes, Path.open + def forbid_full_load(path: Path, *args: object, **kwargs: object) -> str: + if path == profiles: + raise AssertionError("JSONL must be streamed") + return original_read_text(path, *args, **kwargs) + def forbid_profile_bytes(path: Path, *args: object, **kwargs: object) -> bytes: + if path == profiles: + raise AssertionError("JSONL must not be copied or loaded") + return original_read_bytes(path, *args, **kwargs) + def forbid_matrix(path: Path, *args: object, **kwargs: object): + if path == matrix: + raise AssertionError("complete matrices must not be opened") + return original_open(path, *args, **kwargs) + monkeypatch.setattr(Path, "read_text", forbid_full_load) + monkeypatch.setattr(Path, "read_bytes", forbid_profile_bytes) + monkeypatch.setattr(Path, "open", forbid_matrix) + output = tmp_path / "snapshot" + export_depmap_report_snapshot(run_dir=run, config_dir=config, manifest_dir=manifests, output_dir=output, selected_targets=("BRAF",)) + assert not (output / profiles.name).exists()