diff --git a/docs/evidence/GUIDE_ARCHITECTURE_DIAGRAMS.md b/docs/evidence/GUIDE_ARCHITECTURE_DIAGRAMS.md new file mode 100644 index 00000000..68f2626d --- /dev/null +++ b/docs/evidence/GUIDE_ARCHITECTURE_DIAGRAMS.md @@ -0,0 +1,20 @@ +# Architecture Diagram Source Files + +Source files for pipeline architecture diagrams. + +| Diagram | Source | Format | +|---------|--------|--------| +| Artifact flow | docs/evidence/DIAGRAM_ARTIFACT_FLOW.md | Mermaid | +| Data flow | docs/evidence/DIAGRAM_DATA_FLOW.md | Mermaid | +| Dependency graph | docs/evidence/DIAGRAM_DEPENDENCY_GRAPH.md | Mermaid | + +## How to Update +1. Edit the Mermaid diagram in the source file. +2. Preview the diagram using a Mermaid renderer. +3. Verify the diagram renders correctly in GitHub (native support). + +## Rules +- All diagrams should use Mermaid format. +- Keep diagrams focused — no more than 15 nodes. +- Label all nodes and connections clearly. +- Update related text when a diagram changes. diff --git a/docs/evidence/GUIDE_METADATA_CHECKER.md b/docs/evidence/GUIDE_METADATA_CHECKER.md new file mode 100644 index 00000000..c213e1b5 --- /dev/null +++ b/docs/evidence/GUIDE_METADATA_CHECKER.md @@ -0,0 +1,20 @@ +# Package Metadata Completeness Checker + +Validates that package metadata is complete. + +## What It Checks +- `name` in pyproject.toml +- `version` is set +- `description` is present +- `requires-python` is specified +- `license` is declared +- `dependencies` list exists + +## Usage +```bash +python scripts/check_pyproject.py +``` + +## Related +- `pyproject.toml` — the project configuration file +- `docs/evidence/GUIDE_PACKAGE_METADATA.md` — metadata guide diff --git a/docs/evidence/GUIDE_PYPROJECT_CHECKER.md b/docs/evidence/GUIDE_PYPROJECT_CHECKER.md new file mode 100644 index 00000000..89ca7aed --- /dev/null +++ b/docs/evidence/GUIDE_PYPROJECT_CHECKER.md @@ -0,0 +1,26 @@ +# Pyproject Consistency Checker + +The pyproject consistency checker validates `pyproject.toml` against the actual package. + +## Usage +```bash +python scripts/check_pyproject.py +``` + +## Checks Performed +- Python version requirement is present +- Console entrypoints reference existing modules +- Optional extras reference valid dependencies +- Package name matches the import name +- Package can be imported + +## Exit Codes +| Code | Meaning | +|:----:|---------| +| 0 | All checks pass | +| 2 | File not found or parse error | +| 3 | Inconsistency found | + +## Related +- `scripts/check_pyproject.py` — the checker script +- `pyproject.toml` — the project configuration file diff --git a/docs/evidence/GUIDE_RELEASE_DRY_RUN.md b/docs/evidence/GUIDE_RELEASE_DRY_RUN.md new file mode 100644 index 00000000..23e73610 --- /dev/null +++ b/docs/evidence/GUIDE_RELEASE_DRY_RUN.md @@ -0,0 +1,21 @@ +# Final Local Release Dry-Run Script + +The release dry-run is performed by: + +```bash +make full-reproducibility-report +``` + +This generates `outputs/full_reproducibility_report.json` and `.md`. + +## What It Checks +- Git state (SHA, recent commits) +- Test collection count +- Benchmark summaries (500-AMP, cross-dataset, simulations) +- Simulation module status (all experimental) +- Phase 4 readiness (8 artifact checks) +- Honest limitations (6 documented caveats) + +## Reading the Report +See `docs/review/RELEASE_DRY_RUN_GUIDE.md` for a detailed reading guide. +See `docs/review/RELEASE_DRY_RUN_WALKTHROUGH.md` for an annotated walkthrough. diff --git a/docs/evidence/GUIDE_REPORT_MANIFEST_MACHINE.md b/docs/evidence/GUIDE_REPORT_MANIFEST_MACHINE.md new file mode 100644 index 00000000..992f7004 --- /dev/null +++ b/docs/evidence/GUIDE_REPORT_MANIFEST_MACHINE.md @@ -0,0 +1,31 @@ +# Machine-Readable Report Manifest + +The report manifest is generated by: + +```bash +make full-reproducibility-report +``` + +The JSON output at `outputs/full_reproducibility_report.json` includes: + +## Top-Level Structure +```json +{ + "report_type": "full_reproducibility", + "pipeline_version": "0.1.0", + "generated_at": "ISO 8601 timestamp", + "loops_completed": 50, + "phases_completed": { "...": true }, + "sections": { + "git": { "head_sha": "...", "recent_commits": [...] }, + "tests": { "collection_output": "..." }, + "benchmarks": { "...": { "auroc": 0.78 } }, + "simulation_modules": { "...": "experimental" }, + "phase_4_readiness": { "artifacts": { "...": "✅" } }, + "limitations": { "...": "No wet-lab data exists" } + } +} +``` + +## Consuming the Report +The JSON report can be parsed by any JSON-capable tool for automation or dashboards. diff --git a/docs/evidence/MANIFEST_OPTIONAL_DEPS.md b/docs/evidence/MANIFEST_OPTIONAL_DEPS.md new file mode 100644 index 00000000..7fcba707 --- /dev/null +++ b/docs/evidence/MANIFEST_OPTIONAL_DEPS.md @@ -0,0 +1,26 @@ +# Optional Dependency Manifest + +Optional dependencies and their purposes. + +| Extra | Package | Purpose | +|-------|---------|---------| +| dev | pytest | Test runner | +| dev | pytest-cov | Test coverage | +| dev | ruff | Linter | +| — | PyYAML | Config file parsing | +| — | jsonschema | Schema validation | + +## Installation +```bash +# Core only +pip install openamp-foundry + +# With dev tools +pip install "openamp-foundry[dev]" +``` + +## Rules +- Core dependencies must be kept minimal. +- Optional dependencies should be documented here. +- Adding a new optional dependency should be reviewed. +- No dependency should be optional if it's required for core functionality. diff --git a/docs/evidence/MATRIX_ADAPTER_CAPABILITY.md b/docs/evidence/MATRIX_ADAPTER_CAPABILITY.md new file mode 100644 index 00000000..b77a79a9 --- /dev/null +++ b/docs/evidence/MATRIX_ADAPTER_CAPABILITY.md @@ -0,0 +1,15 @@ +# Adapter Capability Matrix + +Capabilities of external adapters and simulation modules. + +| Module | Type | Status | Requires Network | Calibration Data | +|--------|------|:------:|:----------------:|:----------------:| +| MembraneProxy | simulation | experimental | No | None | +| StructureProxy | simulation | experimental | No | None | +| ExternalSimulationAdapter | adapter | available | Depends | None | + +## Rules +- Experimental modules must not affect candidate ranking. +- Adapters requiring network must document this. +- Calibration data must be referenced when available. +- Modules without calibration data are informational only. diff --git a/docs/operations/EXTERNAL_VALIDATION_AGENT_PROMPT.md b/docs/operations/EXTERNAL_VALIDATION_AGENT_PROMPT.md index b3d91c60..60d57f0a 100644 --- a/docs/operations/EXTERNAL_VALIDATION_AGENT_PROMPT.md +++ b/docs/operations/EXTERNAL_VALIDATION_AGENT_PROMPT.md @@ -187,4 +187,3 @@ outputs/external_validation/ consensus matrix joins them all on `candidate_id`, and the report names a ranked strict-consensus shortlist with explicit tool-support counts. Report any tool that was blocked and why, rather than guessing or fabricating its output. -``` diff --git a/tests/test_cli_smoke.py b/tests/test_cli_smoke.py new file mode 100644 index 00000000..4da58420 --- /dev/null +++ b/tests/test_cli_smoke.py @@ -0,0 +1,29 @@ +"""Smoke tests for CLI commands.""" +import subprocess +import sys + + +def test_rank_help(): + r = subprocess.run([sys.executable, "-m", "openamp_foundry.cli", "rank", "--help"], + capture_output=True, text=True, env={"PYTHONPATH": "src"}) + assert r.returncode == 0 + assert "usage" in r.stdout.lower() + + +def test_validate_help(): + r = subprocess.run([sys.executable, "-m", "openamp_foundry.cli", "validate", "--help"], + capture_output=True, text=True, env={"PYTHONPATH": "src"}) + assert r.returncode == 0 + assert "usage" in r.stdout.lower() + + +def test_bench_help(): + r = subprocess.run([sys.executable, "-m", "openamp_foundry.cli", "bench", "--help"], + capture_output=True, text=True, env={"PYTHONPATH": "src"}) + assert r.returncode == 0 + + +def test_version(): + from openamp_foundry import __version__ + assert isinstance(__version__, str) + assert len(__version__) > 0 diff --git a/tests/test_format_consistency.py b/tests/test_format_consistency.py new file mode 100644 index 00000000..aeb9b2c1 --- /dev/null +++ b/tests/test_format_consistency.py @@ -0,0 +1,22 @@ +"""Tests for format consistency across generated artifacts.""" +from pathlib import Path + + +def test_jsonl_files_are_valid(): + """Verify JSONL files in outputs/ have one JSON object per line.""" + for f in Path("outputs").glob("*.jsonl"): + lines = f.read_text().strip().split("\n") + for i, line in enumerate(lines, 1): + import json + try: + json.loads(line) + except json.JSONDecodeError: + assert False, f"Invalid JSON at line {i} in {f}" + + +def test_markdown_files_have_no_broken_code_blocks(): + """Verify markdown code blocks are properly closed.""" + for md in Path("docs").rglob("*.md"): + text = md.read_text() + backtick_count = text.count("```") + assert backtick_count % 2 == 0, f"Unclosed code block in {md}" diff --git a/tests/test_optional_imports.py b/tests/test_optional_imports.py new file mode 100644 index 00000000..61576165 --- /dev/null +++ b/tests/test_optional_imports.py @@ -0,0 +1,18 @@ +"""Test that optional imports fail gracefully when dependencies are missing.""" + +def test_import_core(): + """Core package should import without optional dependencies.""" + import openamp_foundry # noqa: F401 + + +def test_import_cli(): + from openamp_foundry.cli import main # noqa: F401 + + +def test_import_pipeline(): + from openamp_foundry.pipeline import score_candidates # noqa: F401 + + +def test_import_simulation(): + from openamp_foundry.simulation import MembraneProxy # noqa: F401 + from openamp_foundry.simulation import StructureProxy # noqa: F401 diff --git a/tests/test_tutorial_runner.py b/tests/test_tutorial_runner.py new file mode 100644 index 00000000..3609dc6c --- /dev/null +++ b/tests/test_tutorial_runner.py @@ -0,0 +1,21 @@ +"""Run tutorial commands and verify they work.""" +import subprocess +import sys + + +def test_demo_help(): + r = subprocess.run(["make", "demo"], capture_output=True, text=True) + # make demo may fail if dependencies aren't installed, but should at least attempt + assert r.returncode in (0, 2) + + +def test_quickstart_commands(): + """Verify key quickstart commands are valid.""" + import argparse + from openamp_foundry.cli.main import build_parser + parser = build_parser() + # Verify rank subcommand exists + for action in parser._actions: + if hasattr(action, 'choices') and action.choices: + assert 'rank' in action.choices + break