Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| import argparse | |
| import re | |
| import sys | |
| from pathlib import Path | |
| from . import __version__ | |
| from .calibration_profile import available_policy_names, load_calibration_profile, load_calibration_profile_file | |
| from .policy_intent import derive_policy_intent, simulate_policy_outcome | |
| from .render import write_outputs | |
| from .scanner import audit_repository | |
| _LEVEL_MAP = { | |
| 1: ("brief", 1), | |
| 2: ("detailed", 5), | |
| 3: ("detailed", 7), | |
| } | |
| _FORMAT_CHOICES = ["json", "md", "html", "pdf", "all"] | |
| _SUMMARY_CHOICES = ["full", "compact", "off"] | |
| _ADVISORY_CHOICES = ["none", "validate", "packet", "call"] | |
| _TIER_CHOICES = ["T0", "T1", "T2", "T3", "T4"] | |
| _TIER_ORDER = {"T0": 0, "T1": 1, "T2": 2, "T3": 3, "T4": 4} | |
| def _policy_choices() -> list[str]: | |
| names = available_policy_names() | |
| return names or ["default"] | |
| def _build_parser() -> argparse.ArgumentParser: | |
| parser = argparse.ArgumentParser( | |
| prog="stem", | |
| description=( | |
| "STEM BIO-AI local evidence-surface scanner for bio/medical AI repositories.\n\n" | |
| "Workflow-oriented commands:\n" | |
| " scan Generate JSON / Markdown / PDF artifacts for a local repository\n" | |
| " gate Enforce a minimum triage tier for CI/CD pipelines\n" | |
| " advisory Run provider-neutral advisory validation and packet workflows\n\n" | |
| "Time-to-value shortcut:\n" | |
| " stem <folder> behaves the same as stem scan <folder>" | |
| ), | |
| epilog=( | |
| "Common examples:\n" | |
| " stem /path/to/repo\n" | |
| " stem scan /path/to/repo --level 3 --format all --explain\n" | |
| " stem gate /path/to/repo --min-tier T2\n" | |
| " stem advisory packet /path/to/repo --output advisory_out\n" | |
| " stem advisory check-response /path/to/repo --response provider_advisory.json\n" | |
| ), | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| ) | |
| parser.add_argument("--version", action="version", version=f"STEM BIO-AI {__version__}") | |
| subparsers = parser.add_subparsers(dest="command") | |
| scan = subparsers.add_parser( | |
| "scan", | |
| help="Run a local scan and generate report artifacts", | |
| description="Scan a local repository and generate deterministic STEM BIO-AI artifacts.", | |
| ) | |
| _add_scan_arguments(scan, default_format="all", default_summary="full") | |
| audit = subparsers.add_parser( | |
| "audit", | |
| help="Backward-compatible alias for `scan`", | |
| description="Alias for `scan`. Kept for backward compatibility.", | |
| ) | |
| _add_scan_arguments(audit, default_format="all", default_summary="full") | |
| gate = subparsers.add_parser( | |
| "gate", | |
| help="Run a scan and fail if the repository tier is below the required threshold", | |
| description="CI/CD-oriented workflow: run the scan and enforce a minimum triage tier.", | |
| ) | |
| _add_shared_arguments(gate, default_format="json", default_summary="compact") | |
| gate.add_argument("target", help="Path to a local repository") | |
| gate.add_argument( | |
| "--min-tier", | |
| "--tier-gate", | |
| dest="min_tier", | |
| choices=_TIER_CHOICES, | |
| required=True, | |
| help="Minimum required tier; exit code 1 if the result is below this threshold", | |
| ) | |
| advisory = subparsers.add_parser( | |
| "advisory", | |
| help="Provider-neutral advisory workflows", | |
| description="Prepare, validate, or execute advisory workflows without letting advisory output override deterministic scores.", | |
| ) | |
| advisory_subparsers = advisory.add_subparsers(dest="advisory_command") | |
| advisory_subparsers.required = True | |
| advisory_validate = advisory_subparsers.add_parser( | |
| "validate", | |
| help="Run offline advisory validation against the deterministic result", | |
| description="Run the audit and attach offline advisory-validation metadata with no provider call.", | |
| ) | |
| _add_shared_arguments(advisory_validate, default_format="json", default_summary="compact") | |
| advisory_validate.add_argument("target", help="Path to a local repository") | |
| advisory_packet = advisory_subparsers.add_parser( | |
| "packet", | |
| help="Export a provider-neutral advisory packet", | |
| description="Run the audit and export a sanitized advisory input packet for downstream provider review.", | |
| ) | |
| _add_shared_arguments(advisory_packet, default_format="json", default_summary="compact") | |
| advisory_packet.add_argument("target", help="Path to a local repository") | |
| advisory_call = advisory_subparsers.add_parser( | |
| "call", | |
| help="Enter the explicit provider-call advisory workflow", | |
| description="Run the audit and execute the explicit provider-call advisory workflow.", | |
| ) | |
| _add_shared_arguments(advisory_call, default_format="json", default_summary="compact") | |
| advisory_call.add_argument("target", help="Path to a local repository") | |
| advisory_response = advisory_subparsers.add_parser( | |
| "check-response", | |
| help="Validate a provider-produced advisory response JSON", | |
| description="Run the audit and validate a provider-produced advisory response file against the evidence ledger.", | |
| ) | |
| _add_shared_arguments(advisory_response, default_format="json", default_summary="compact") | |
| advisory_response.add_argument("target", help="Path to a local repository") | |
| advisory_response.add_argument( | |
| "--response", | |
| "--advisory-response", | |
| dest="response", | |
| required=True, | |
| help="Path to a provider-produced advisory JSON response file", | |
| ) | |
| policy = subparsers.add_parser( | |
| "policy", | |
| help="Inspect available calibration profiles", | |
| description="List or explain named calibration profiles used by STEM BIO-AI policy surfaces.", | |
| ) | |
| policy_subparsers = policy.add_subparsers(dest="policy_command") | |
| policy_subparsers.required = True | |
| policy_subparsers.add_parser( | |
| "list", | |
| help="List available calibration profiles", | |
| description="Show the named calibration profiles that can be selected with --policy.", | |
| ) | |
| policy_explain = policy_subparsers.add_parser( | |
| "explain", | |
| help="Explain one calibration profile", | |
| description="Show the current contents and bounded differences of a named calibration profile.", | |
| ) | |
| policy_explain.add_argument("profile_name", choices=_policy_choices(), help="Calibration profile name") | |
| policy_derive = policy_subparsers.add_parser( | |
| "derive", | |
| help="Translate researcher intent into a named profile or preview-only delta", | |
| description="Apply the documented top-down rule table to a 1-5 researcher intent surface.", | |
| ) | |
| _add_intent_arguments(policy_derive) | |
| policy_simulate = policy_subparsers.add_parser( | |
| "simulate", | |
| help="Preview how a derived posture would change the current repository outcome", | |
| description="Run the deterministic scan, then simulate a named-profile or preview-only policy outcome without changing the stored scan contract.", | |
| ) | |
| _add_intent_arguments(policy_simulate, required=False) | |
| policy_simulate.add_argument("target", help="Path to a local repository") | |
| policy_simulate.add_argument( | |
| "--profile-file", | |
| default=None, | |
| help="Path to a local calibration profile JSON for simulation-only use", | |
| ) | |
| return parser | |
| def _add_scan_arguments(parser: argparse.ArgumentParser, *, default_format: str, default_summary: str) -> None: | |
| _add_shared_arguments(parser, default_format=default_format, default_summary=default_summary) | |
| parser.add_argument("target", help="Path to a local repository") | |
| parser.add_argument( | |
| "--advisory", | |
| choices=_ADVISORY_CHOICES, | |
| default="none", | |
| help="Compatibility path for advisory workflows; prefer `stem advisory validate|packet|call`", | |
| ) | |
| parser.add_argument( | |
| "--advisory-response", | |
| default=None, | |
| help="Compatibility path for provider response validation; prefer `stem advisory check-response ... --response FILE`", | |
| ) | |
| parser.add_argument( | |
| "--tier-gate", | |
| choices=_TIER_CHOICES, | |
| default=None, | |
| help="Compatibility path for CI gating; prefer `stem gate <folder> --min-tier ...`", | |
| ) | |
| def _add_shared_arguments(parser: argparse.ArgumentParser, *, default_format: str, default_summary: str) -> None: | |
| parser.set_defaults(default_summary=default_summary) | |
| parser.add_argument( | |
| "--level", | |
| type=int, | |
| choices=[1, 2, 3], | |
| default=3, | |
| help=( | |
| "Report depth: " | |
| "1=brief 1-page executive summary, " | |
| "2=standard 5-page review packet, " | |
| "3=full 7-page evidence packet (default)" | |
| ), | |
| ) | |
| parser.add_argument( | |
| "--format", | |
| choices=_FORMAT_CHOICES, | |
| default=default_format, | |
| help="Artifact format to write", | |
| ) | |
| parser.add_argument( | |
| "--out", | |
| "--output", | |
| dest="out", | |
| default="stem_output", | |
| help="Output directory for generated artifacts", | |
| ) | |
| parser.add_argument( | |
| "--policy", | |
| choices=_policy_choices(), | |
| default="default", | |
| help="Named calibration profile to surface in the result (1.8.0 keeps scan-time policy selection mirror-only)", | |
| ) | |
| parser.add_argument( | |
| "--explain", | |
| action="store_true", | |
| default=False, | |
| help="Write a {stem}_explain.txt proof trace with file, line, snippet, and reason", | |
| ) | |
| parser.add_argument( | |
| "--summary", | |
| choices=_SUMMARY_CHOICES, | |
| default=None, | |
| help="stdout summary mode: full, compact, or off", | |
| ) | |
| parser.add_argument( | |
| "--quiet", | |
| action="store_true", | |
| default=False, | |
| help="Alias for `--summary off`", | |
| ) | |
| def _add_intent_arguments(parser: argparse.ArgumentParser, *, required: bool = True) -> None: | |
| parser.add_argument("--baseline", choices=_policy_choices(), default="default", help="Baseline named profile") | |
| parser.add_argument("--clinical-strictness", type=int, choices=range(1, 6), required=required, help="1-5 scale") | |
| parser.add_argument("--code-integrity-priority", type=int, choices=range(1, 6), required=required, help="1-5 scale") | |
| parser.add_argument("--reproducibility-priority", type=int, choices=range(1, 6), required=required, help="1-5 scale") | |
| parser.add_argument("--structured-limitations-requirement", type=int, choices=range(1, 6), required=required, help="1-5 scale") | |
| def _normalize_argv(argv: list[str]) -> list[str]: | |
| if not argv: | |
| return argv | |
| if argv[0] in {"scan", "audit", "gate", "advisory", "policy", "-h", "--help", "--version"}: | |
| return argv | |
| if argv[0].startswith("-"): | |
| return argv | |
| return ["scan", *argv] | |
| def _validate_target(raw_target: str) -> Path: | |
| if raw_target.startswith(("http://", "https://")): | |
| raise SystemExit( | |
| "GitHub URL auditing is not enabled in the local CLI yet. " | |
| "Clone the repository first, then run: stem scan <local-folder>" | |
| ) | |
| target = Path(raw_target).expanduser().resolve() | |
| if not target.exists(): | |
| raise SystemExit(f"Target path does not exist: {target}") | |
| if not target.is_dir(): | |
| raise SystemExit(f"Target must be a directory: {target}") | |
| return target | |
| def _validate_optional_file(raw_path: str | None) -> Path | None: | |
| if raw_path is None: | |
| return None | |
| path = Path(raw_path).expanduser().resolve() | |
| if not path.exists(): | |
| raise SystemExit(f"Advisory response file does not exist: {path}") | |
| if not path.is_file(): | |
| raise SystemExit(f"Advisory response path must be a file: {path}") | |
| return path | |
| def _safe_output_segment(name: str) -> str: | |
| slug = re.sub(r"[^A-Za-z0-9._-]+", "-", name.strip()).strip("-") | |
| return slug or "scan_target" | |
| def _resolve_output_dir(raw_out_dir: str, target: Path) -> Path: | |
| output_dir = Path(raw_out_dir).expanduser().resolve() | |
| if output_dir.name != "stem_output": | |
| return output_dir | |
| return output_dir / _safe_output_segment(target.name) | |
| def _resolve_summary_mode(args: argparse.Namespace) -> str: | |
| if getattr(args, "quiet", False): | |
| return "off" | |
| if getattr(args, "summary", None) is not None: | |
| return args.summary | |
| return getattr(args, "default_summary", "full") | |
| def _bio_summary_line(result: dict) -> str | None: | |
| by_detector = result.get("detector_summary", {}).get("by_detector", {}) | |
| hits = [key.replace("BIO_", "") for key, value in by_detector.items() if key.startswith("BIO_") and value.get("detected")] | |
| if not hits: | |
| return None | |
| return ", ".join(hits[:4]) + (f" (+{len(hits) - 4} more)" if len(hits) > 4 else "") | |
| def _integrity_summary_line(result: dict) -> str | None: | |
| integrity = result.get("code_integrity", {}) | |
| warnings = [key for key, value in integrity.items() if value.get("status") not in ("PASS", None)] | |
| if not warnings: | |
| return None | |
| return ", ".join(item.split("_", 1)[0] for item in warnings) | |
| def _ast_scope_summary_line(result: dict) -> str | None: | |
| ast = result.get("ast_signal_summary", {}) | |
| if not ast or not ast.get("file_limit_exceeded"): | |
| return None | |
| return ( | |
| f"capped at {ast.get('files_considered', 'unknown')} / " | |
| f"{ast.get('files_total', 'unknown')} python files" | |
| ) | |
| def _workflow_label(command: str, advisory_command: str | None = None) -> str: | |
| if command == "gate": | |
| return "gate" | |
| if command == "advisory" and advisory_command is not None: | |
| return f"advisory {advisory_command}" | |
| return "scan" | |
| def _mirror_only_notice(calibration: dict) -> str | None: | |
| if calibration.get("profile_read_mode") != "mirror_only": | |
| return None | |
| if calibration.get("profile_name") == "default": | |
| return None | |
| return ( | |
| "mirror-only preview; scan scoring still follows authoritative runtime constants. " | |
| "Use `stem policy simulate` for score deltas." | |
| ) | |
| def _evaluate_tier_gate(result: dict, required_tier: str | None) -> tuple[bool, str | None]: | |
| if required_tier is None: | |
| return True, None | |
| formal_tier = result["score"]["formal_tier"] | |
| actual_tier_key = formal_tier.split()[0] | |
| actual_rank = _TIER_ORDER.get(actual_tier_key, 0) | |
| required_rank = _TIER_ORDER[required_tier] | |
| passed = actual_rank >= required_rank | |
| verdict = "PASS" if passed else "FAIL" | |
| return passed, f"{verdict} ({formal_tier} vs required {required_tier})" | |
| def _print_full_summary( | |
| result: dict, | |
| *, | |
| workflow: str, | |
| level: int, | |
| mode: str, | |
| pages: int, | |
| output_dir: Path, | |
| created: list[Path], | |
| advisory_mode: str, | |
| advisory_response: Path | None, | |
| gate_message: str | None, | |
| ) -> None: | |
| score = result["score"] | |
| classification = result.get("classification", {}) | |
| print("STEM BIO-AI scan complete") | |
| print(f"Workflow: {workflow}") | |
| print(f"Target: {result['target']['name']}") | |
| print(f"Level: {level} ({mode}, {pages}p)") | |
| calibration = result.get("calibration_profile", {}) | |
| print( | |
| "Policy: " | |
| f"{calibration.get('profile_name', 'unknown')} " | |
| f"({calibration.get('profile_status', 'unknown')}, {calibration.get('profile_read_mode', 'unknown')})" | |
| ) | |
| policy_notice = _mirror_only_notice(calibration) | |
| if policy_notice: | |
| print(f"Policy Mode: {policy_notice}") | |
| print() | |
| print(f"Score: {score['final_score']} / 100 ({score['formal_tier']})") | |
| print(f" Stage 1: {score['stage_1_readme_intent']} / 100 (README evidence)") | |
| print(f" Stage 2R: {score['stage_2_repo_local_consistency']} / 100 (repo consistency)") | |
| print(f" Stage 3: {score['stage_3_code_bio']} / 100 (code + bio)") | |
| rep_score = result.get("replication_score") | |
| rep_tier = result.get("replication_tier") | |
| if rep_score is not None: | |
| print(f" Stage 4: {rep_score} / 100 (replication {rep_tier})") | |
| ca = classification.get("ca_severity", "none") | |
| if ca != "none": | |
| clinical_line = f"Clinical: {ca}" | |
| if classification.get("t0_hard_floor"): | |
| clinical_line += " [T0 HARD FLOOR]" | |
| elif classification.get("score_cap") is not None: | |
| clinical_line += f" [cap={classification['score_cap']}]" | |
| print(clinical_line) | |
| integrity_line = _integrity_summary_line(result) | |
| if integrity_line: | |
| print(f"Integrity: WARN ({integrity_line})") | |
| bio_line = _bio_summary_line(result) | |
| if bio_line: | |
| print(f"Bio: {bio_line}") | |
| ast_line = _ast_scope_summary_line(result) | |
| if ast_line: | |
| print(f"AST Scope: {ast_line}") | |
| regulatory = result.get("regulatory_basis", {}) | |
| if regulatory.get("review_required"): | |
| reasons = ", ".join(regulatory.get("review_reasons", [])) | |
| print(f"Regulatory: review required ({reasons})") | |
| if advisory_mode == "none" and advisory_response is None: | |
| print("AI: not used (deterministic only)") | |
| else: | |
| ai_status = result.get("ai_advisory", {}) | |
| provider = ai_status.get("provider", "none") | |
| status = "packet_ready" if advisory_mode == "packet" else ai_status.get("status", "not_run") | |
| print(f"AI: {status} (provider={provider})") | |
| if gate_message is not None: | |
| print(f"Gate: {gate_message}") | |
| risks = result.get("notable_risks", []) | |
| if risks: | |
| print() | |
| limit = min(len(risks), 3) | |
| print(f"Action items ({len(risks)} total):") | |
| for risk in risks[:limit]: | |
| print(f" - {risk}") | |
| if len(risks) > limit: | |
| print(f" ... +{len(risks) - limit} more (see report)") | |
| print() | |
| print(f"Output: {output_dir}") | |
| for path in created: | |
| print(f" {path.name}") | |
| def _print_compact_summary( | |
| result: dict, | |
| *, | |
| workflow: str, | |
| output_dir: Path, | |
| created: list[Path], | |
| gate_message: str | None, | |
| ) -> None: | |
| score = result["score"] | |
| print(f"STEM BIO-AI | {workflow} | {score['formal_tier']} | {score['final_score']}/100") | |
| print(f"Target: {result['target']['name']}") | |
| calibration = result.get("calibration_profile", {}) | |
| print( | |
| f"Policy: {calibration.get('profile_name', 'unknown')} " | |
| f"[{calibration.get('profile_status', 'unknown')}; {calibration.get('profile_read_mode', 'unknown')}]" | |
| ) | |
| policy_notice = _mirror_only_notice(calibration) | |
| if policy_notice: | |
| print(f"Policy Mode: {policy_notice}") | |
| classification = result.get("classification", {}) | |
| if classification.get("ca_severity", "none") != "none": | |
| suffix = "" | |
| if classification.get("t0_hard_floor"): | |
| suffix = " [T0 HARD FLOOR]" | |
| elif classification.get("score_cap") is not None: | |
| suffix = f" [cap={classification['score_cap']}]" | |
| print(f"Clinical: {classification['ca_severity']}{suffix}") | |
| if gate_message is not None: | |
| print(f"Gate: {gate_message}") | |
| ast_line = _ast_scope_summary_line(result) | |
| if ast_line: | |
| print(f"AST: {ast_line}") | |
| print(f"Artifacts: {len(created)} -> {output_dir}") | |
| def _print_summary( | |
| result: dict, | |
| *, | |
| workflow: str, | |
| level: int, | |
| mode: str, | |
| pages: int, | |
| output_dir: Path, | |
| created: list[Path], | |
| advisory_mode: str, | |
| advisory_response: Path | None, | |
| summary_mode: str, | |
| gate_message: str | None, | |
| ) -> None: | |
| if summary_mode == "off": | |
| return | |
| if summary_mode == "compact": | |
| _print_compact_summary( | |
| result, | |
| workflow=workflow, | |
| output_dir=output_dir, | |
| created=created, | |
| gate_message=gate_message, | |
| ) | |
| return | |
| _print_full_summary( | |
| result, | |
| workflow=workflow, | |
| level=level, | |
| mode=mode, | |
| pages=pages, | |
| output_dir=output_dir, | |
| created=created, | |
| advisory_mode=advisory_mode, | |
| advisory_response=advisory_response, | |
| gate_message=gate_message, | |
| ) | |
| def _execute_scan( | |
| *, | |
| target: Path, | |
| level: int, | |
| fmt: str, | |
| out_dir: str, | |
| explain: bool, | |
| advisory_mode: str, | |
| advisory_response: Path | None, | |
| policy_name: str, | |
| tier_gate: str | None, | |
| summary_mode: str, | |
| workflow: str, | |
| ) -> int: | |
| mode, pages = _LEVEL_MAP[level] | |
| result = audit_repository( | |
| target, | |
| policy_name=policy_name, | |
| advisory=advisory_mode, | |
| advisory_response_path=advisory_response, | |
| ) | |
| output_dir = _resolve_output_dir(out_dir, target) | |
| created = write_outputs(result, output_dir, mode, pages, fmt, explain=explain) | |
| gate_passed, gate_message = _evaluate_tier_gate(result, tier_gate) | |
| _print_summary( | |
| result, | |
| workflow=workflow, | |
| level=level, | |
| mode=mode, | |
| pages=pages, | |
| output_dir=output_dir, | |
| created=created, | |
| advisory_mode=advisory_mode, | |
| advisory_response=advisory_response, | |
| summary_mode=summary_mode, | |
| gate_message=gate_message, | |
| ) | |
| return 0 if gate_passed else 1 | |
| def _format_tier_boundaries(profile: dict) -> str: | |
| boundaries = profile["tier_policy"]["tier_boundaries"] | |
| return ( | |
| f"T0 < {boundaries[0]}, " | |
| f"T1 {boundaries[0]}-{boundaries[1] - 1}, " | |
| f"T2 {boundaries[1]}-{boundaries[2] - 1}, " | |
| f"T3 {boundaries[2]}-{boundaries[3] - 1}, " | |
| f"T4 >= {boundaries[3]}" | |
| ) | |
| def _print_policy_list() -> int: | |
| print("STEM BIO-AI calibration profiles") | |
| for name in _policy_choices(): | |
| profile = load_calibration_profile(name) | |
| print( | |
| f"- {profile['profile_name']}: {profile['profile_status']} | " | |
| f"{profile['profile_read_mode']} | {profile['policy_version']}" | |
| ) | |
| return 0 | |
| def _print_policy_explain(profile_name: str) -> int: | |
| profile = load_calibration_profile(profile_name) | |
| default_profile = load_calibration_profile("default") | |
| weights = profile["weights"] | |
| clinical = profile["clinical_policy"] | |
| stage_3 = profile["stage_3_policy"] | |
| stage_4 = profile["stage_4_policy"] | |
| print(f"STEM BIO-AI policy: {profile['profile_name']}") | |
| print(f"Policy Version: {profile['policy_version']}") | |
| print(f"Status: {profile['profile_status']}") | |
| print(f"Read Mode: {profile['profile_read_mode']}") | |
| print( | |
| "Scoring Effect: " | |
| "mirror-only in 1.8.0; selection is surfaced in artifacts but does not yet reweight score computation" | |
| ) | |
| print() | |
| print( | |
| "Weights: " | |
| f"S1={weights['stage_1_percent']}% | " | |
| f"S2R={weights['stage_2r_percent']}% | " | |
| f"S3={weights['stage_3_percent']}%" | |
| ) | |
| print(f"Tier Policy: {_format_tier_boundaries(profile)}") | |
| print( | |
| "Clinical Caps: " | |
| f"no_disclaimer_cap={clinical['ca_no_disclaimer_cap']} | " | |
| f"t0_hard_floor_cap={clinical['t0_hard_floor_cap']}" | |
| ) | |
| normalization = stage_3["normalization"] | |
| print( | |
| "Stage 3: " | |
| f"normalization={normalization['kind']} " | |
| f"(raw_max={normalization['raw_max']}, target_max={normalization['target_max']}, rounding={normalization['rounding']})" | |
| ) | |
| print(f"B2 Posture: {stage_3['b2_partial_credit_mode']}") | |
| print(f"Stage 4: emphasis={stage_4.get('emphasis', 'unknown')}") | |
| print( | |
| "Reasoning: " | |
| f"{profile['reasoning_policy'].get('status', 'unknown')} " | |
| f"(score integration: {profile['reasoning_policy'].get('score_integration', 'unknown')})" | |
| ) | |
| if profile_name != "default": | |
| diffs: list[str] = [] | |
| if profile["clinical_policy"]["ca_no_disclaimer_cap"] != default_profile["clinical_policy"]["ca_no_disclaimer_cap"]: | |
| diffs.append( | |
| "ca_no_disclaimer_cap " | |
| f"{default_profile['clinical_policy']['ca_no_disclaimer_cap']} -> {profile['clinical_policy']['ca_no_disclaimer_cap']}" | |
| ) | |
| if profile["clinical_policy"]["t0_hard_floor_cap"] != default_profile["clinical_policy"]["t0_hard_floor_cap"]: | |
| diffs.append( | |
| "t0_hard_floor_cap " | |
| f"{default_profile['clinical_policy']['t0_hard_floor_cap']} -> {profile['clinical_policy']['t0_hard_floor_cap']}" | |
| ) | |
| if profile["profile_status"] != default_profile["profile_status"]: | |
| diffs.append(f"profile_status {default_profile['profile_status']} -> {profile['profile_status']}") | |
| if profile["policy_version"] != default_profile["policy_version"]: | |
| diffs.append(f"policy_version {default_profile['policy_version']} -> {profile['policy_version']}") | |
| if diffs: | |
| print() | |
| print("Default Diff:") | |
| for diff in diffs: | |
| print(f"- {diff}") | |
| return 0 | |
| def _intent_answers_from_args(args: argparse.Namespace) -> dict[str, int]: | |
| return { | |
| "clinical_strictness": args.clinical_strictness, | |
| "code_integrity_priority": args.code_integrity_priority, | |
| "reproducibility_priority": args.reproducibility_priority, | |
| "structured_limitations_requirement": args.structured_limitations_requirement, | |
| } | |
| def _has_intent_answers(args: argparse.Namespace) -> bool: | |
| return all( | |
| getattr(args, name) is not None | |
| for name in ( | |
| "clinical_strictness", | |
| "code_integrity_priority", | |
| "reproducibility_priority", | |
| "structured_limitations_requirement", | |
| ) | |
| ) | |
| def _print_derived_policy_tail(derived: dict, notes: list | None = None) -> None: | |
| if derived["triggered_rules"]: | |
| print("Rules: " + "; ".join(derived["triggered_rules"])) | |
| if derived["preview_only_deltas"]: | |
| print("Preview Deltas:") | |
| for section, values in derived["preview_only_deltas"].items(): | |
| print(f"- {section}: {values}") | |
| effective_notes = notes if notes is not None else derived["notes"] | |
| if effective_notes: | |
| print("Notes:") | |
| for note in effective_notes: | |
| print(f"- {note}") | |
| def _format_optional_cap(value: int | None) -> str: | |
| return "none" if value is None else str(value) | |
| def _print_simulation_tail(simulation: dict) -> None: | |
| print(f"Outcome Type: {simulation['outcome_type']}") | |
| print( | |
| "Profile Mode: " | |
| f"{simulation['effective_profile_status']} | " | |
| f"{simulation['effective_profile_read_mode']} | " | |
| f"{simulation['effective_profile_source']}" | |
| ) | |
| if simulation.get("effective_profile_path"): | |
| print(f"Profile File: {simulation['effective_profile_path']}") | |
| print(f"Profile Hash: {simulation['effective_policy_sha256']}") | |
| print( | |
| "Replication: " | |
| f"baseline={simulation['baseline_stage_4_emphasis']} | " | |
| f"simulation={simulation['effective_stage_4_emphasis']}" | |
| ) | |
| print( | |
| "Cap Effect: " | |
| f"baseline={_format_optional_cap(simulation['baseline_score_cap'])} | " | |
| f"simulation={_format_optional_cap(simulation['score_cap'])}" | |
| ) | |
| print(f"Score Delta: {simulation['score_delta']:+d}") | |
| print(f"Raw Delta: {simulation['raw_score_delta']:+d}") | |
| if simulation["replication_posture_changed"] and not simulation["formal_score_changed"]: | |
| print("Formal Score: unchanged; Stage 4 remains a separate replication lane in 1.8.0") | |
| def _print_policy_derive(args: argparse.Namespace) -> int: | |
| derived = derive_policy_intent(_intent_answers_from_args(args), baseline_profile_name=args.baseline) | |
| print("STEM BIO-AI policy derive") | |
| print(f"Baseline: {derived['baseline_profile']}") | |
| print(f"Outcome: {derived['outcome_type']}") | |
| print(f"Recommendation:{' '}{derived['recommended_profile']}") | |
| print("Answers: " + ", ".join(f"{k}={v}" for k, v in derived["answers"].items())) | |
| _print_derived_policy_tail(derived) | |
| return 0 | |
| def _print_policy_simulate(args: argparse.Namespace) -> int: | |
| target = _validate_target(args.target) | |
| result = audit_repository(target, policy_name=args.baseline) | |
| using_profile_file = args.profile_file is not None | |
| has_answers = _has_intent_answers(args) | |
| if using_profile_file and has_answers: | |
| raise SystemExit("Use either intent answers or --profile-file for policy simulate, not both.") | |
| if not using_profile_file and not has_answers: | |
| raise SystemExit("Provide either all 1-5 intent answers or --profile-file for policy simulate.") | |
| print("STEM BIO-AI policy simulation") | |
| print(f"Target: {result['target']['name']}") | |
| print("Mode: preview only; baseline scan scoring remains authoritative") | |
| print(f"Baseline: {args.baseline} -> {result['score']['final_score']}/100 ({result['score']['formal_tier']})") | |
| if using_profile_file: | |
| profile = load_calibration_profile_file(args.profile_file) | |
| if profile["profile_read_mode"] != "mirror_only": | |
| raise SystemExit("External profile files for simulation must remain mirror_only.") | |
| simulation = simulate_policy_outcome( | |
| result, | |
| None, | |
| baseline_profile_name=args.baseline, | |
| external_profile=profile, | |
| ) | |
| print( | |
| f"Simulation: {simulation['effective_profile']} [local_file] -> " | |
| f"{simulation['final_score']}/100 ({simulation['formal_tier']})" | |
| ) | |
| _print_simulation_tail(simulation) | |
| if simulation["notes"]: | |
| print("Notes:") | |
| for note in simulation["notes"]: | |
| print(f"- {note}") | |
| return 0 | |
| derived = derive_policy_intent(_intent_answers_from_args(args), baseline_profile_name=args.baseline) | |
| simulation = simulate_policy_outcome(result, derived, baseline_profile_name=args.baseline) | |
| print( | |
| f"Simulation: {simulation['effective_profile']} -> " | |
| f"{simulation['final_score']}/100 ({simulation['formal_tier']})" | |
| ) | |
| _print_simulation_tail(simulation) | |
| _print_derived_policy_tail(derived, notes=simulation["notes"]) | |
| return 0 | |
| def _handle_scan_cmd(parsed: argparse.Namespace, summary_mode: str) -> int: | |
| target = _validate_target(parsed.target) | |
| advisory_response = _validate_optional_file(parsed.advisory_response) | |
| return _execute_scan( | |
| target=target, level=parsed.level, fmt=parsed.format, out_dir=parsed.out, | |
| explain=parsed.explain, advisory_mode=parsed.advisory, advisory_response=advisory_response, | |
| policy_name=parsed.policy, tier_gate=parsed.tier_gate, summary_mode=summary_mode, | |
| workflow="scan", | |
| ) | |
| def _handle_gate_cmd(parsed: argparse.Namespace, summary_mode: str) -> int: | |
| target = _validate_target(parsed.target) | |
| return _execute_scan( | |
| target=target, level=parsed.level, fmt=parsed.format, out_dir=parsed.out, | |
| explain=parsed.explain, advisory_mode="none", advisory_response=None, | |
| policy_name=parsed.policy, tier_gate=parsed.min_tier, summary_mode=summary_mode, | |
| workflow="gate", | |
| ) | |
| def _resolve_advisory_params(parsed: argparse.Namespace) -> tuple[str, Path | None]: | |
| cmd = parsed.advisory_command | |
| if cmd == "validate": | |
| return "validate", None | |
| if cmd == "packet": | |
| return "packet", None | |
| if cmd == "call": | |
| return "call", None | |
| if cmd == "check-response": | |
| return "none", _validate_optional_file(parsed.response) | |
| return "none", None | |
| def _handle_advisory_cmd(parsed: argparse.Namespace, summary_mode: str) -> int: | |
| target = _validate_target(parsed.target) | |
| advisory_mode, advisory_response = _resolve_advisory_params(parsed) | |
| return _execute_scan( | |
| target=target, level=parsed.level, fmt=parsed.format, out_dir=parsed.out, | |
| explain=parsed.explain, advisory_mode=advisory_mode, advisory_response=advisory_response, | |
| policy_name=parsed.policy, tier_gate=None, summary_mode=summary_mode, | |
| workflow=_workflow_label(parsed.command, parsed.advisory_command), | |
| ) | |
| def _handle_policy_cmd(parsed: argparse.Namespace) -> int: | |
| if parsed.policy_command == "list": | |
| return _print_policy_list() | |
| if parsed.policy_command == "explain": | |
| return _print_policy_explain(parsed.profile_name) | |
| if parsed.policy_command == "derive": | |
| return _print_policy_derive(parsed) | |
| if parsed.policy_command == "simulate": | |
| return _print_policy_simulate(parsed) | |
| return 2 | |
| def main(argv: list[str] | None = None) -> int: | |
| parser = _build_parser() | |
| parsed = parser.parse_args(_normalize_argv(list(sys.argv[1:] if argv is None else argv))) | |
| if parsed.command is None: | |
| parser.print_help() | |
| return 2 | |
| summary_mode = _resolve_summary_mode(parsed) | |
| if parsed.command in {"scan", "audit"}: | |
| return _handle_scan_cmd(parsed, summary_mode) | |
| if parsed.command == "gate": | |
| return _handle_gate_cmd(parsed, summary_mode) | |
| if parsed.command == "advisory": | |
| return _handle_advisory_cmd(parsed, summary_mode) | |
| if parsed.command == "policy": | |
| return _handle_policy_cmd(parsed) | |
| parser.print_help() | |
| return 2 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |