Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ OpenAMP Foundry is a verification-first, safety-constrained antimicrobial peptid
## Fast orientation

- [docs/PROJECT_INDEX.md](docs/PROJECT_INDEX.md) — navigation hub for humans and agents.
- [docs/TRUST_CENTER.md](docs/TRUST_CENTER.md) — trust architecture.
- [docs/COMMAND_SURFACE.md](docs/COMMAND_SURFACE.md) — command workflows and claim boundaries.
- [docs/HUMAN_AGENT_COLLABORATION.md](docs/HUMAN_AGENT_COLLABORATION.md) — human-agent division of labor.
- [docs/REVIEWER_ONBOARDING.md](docs/REVIEWER_ONBOARDING.md) — review expectations.
- [docs/NEXT_100_PR_MAP.md](docs/NEXT_100_PR_MAP.md) — PR-sized backlog.
- [docs/PROOF_LADDER.md](docs/PROOF_LADDER.md) — claim ladder and evidence levels.
- [docs/trust/TRUST_CENTER.md](docs/trust/TRUST_CENTER.md) — trust architecture.
- [docs/getting-started/COMMAND_SURFACE.md](docs/getting-started/COMMAND_SURFACE.md) — command workflows and claim boundaries.
- [docs/operations/HUMAN_AGENT_COLLABORATION.md](docs/operations/HUMAN_AGENT_COLLABORATION.md) — human-agent division of labor.
- [docs/getting-started/REVIEWER_ONBOARDING.md](docs/getting-started/REVIEWER_ONBOARDING.md) — review expectations.
- [docs/research/NEXT_100_PR_MAP.md](docs/research/NEXT_100_PR_MAP.md) — PR-sized backlog.
- [docs/evidence/PROOF_LADDER.md](docs/evidence/PROOF_LADDER.md) — claim ladder and evidence levels.

## Key docs

Expand All @@ -39,13 +39,13 @@ OpenAMP Foundry is a verification-first, safety-constrained antimicrobial peptid
- [RESPONSIBLE_USE.md](RESPONSIBLE_USE.md)
- [MODEL_RELEASE_POLICY.md](MODEL_RELEASE_POLICY.md)
- [DATA_LICENSE_NOTICE.md](DATA_LICENSE_NOTICE.md)
- [docs/ARTIFACT_VERSIONING.md](docs/ARTIFACT_VERSIONING.md)
- [docs/BENCHMARK_GOVERNANCE.md](docs/BENCHMARK_GOVERNANCE.md)
- [docs/CI_AND_QUALITY_GATES.md](docs/CI_AND_QUALITY_GATES.md)
- [docs/CLAIM_REVIEW_CHECKLIST.md](docs/CLAIM_REVIEW_CHECKLIST.md)
- [docs/DATA_GOVERNANCE.md](docs/DATA_GOVERNANCE.md)
- [docs/MODEL_CARD_TEMPLATE.md](docs/MODEL_CARD_TEMPLATE.md)
- [docs/RELEASE_CHECKLIST.md](docs/RELEASE_CHECKLIST.md)
- [docs/engineering/ARTIFACT_VERSIONING.md](docs/engineering/ARTIFACT_VERSIONING.md)
- [docs/evidence/BENCHMARK_GOVERNANCE.md](docs/evidence/BENCHMARK_GOVERNANCE.md)
- [docs/engineering/CI_AND_QUALITY_GATES.md](docs/engineering/CI_AND_QUALITY_GATES.md)
- [docs/evidence/CLAIM_REVIEW_CHECKLIST.md](docs/evidence/CLAIM_REVIEW_CHECKLIST.md)
- [docs/trust/DATA_GOVERNANCE.md](docs/trust/DATA_GOVERNANCE.md)
- [docs/trust/MODEL_CARD_TEMPLATE.md](docs/trust/MODEL_CARD_TEMPLATE.md)
- [docs/trust/RELEASE_CHECKLIST.md](docs/trust/RELEASE_CHECKLIST.md)

## Default work loop

Expand Down
19 changes: 19 additions & 0 deletions docs/evidence/GUIDE_ARTIFACT_IDENTITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Artifact Identity Helper Based on Hashes

Helper for identifying artifacts by their content hash.

## Usage
```python
from scripts.verify_lab_peptides import _sha256

# Get artifact identity
file_hash = _sha256(open("outputs/demo_ranked.jsonl").read())
print(f"Artifact identity: {file_hash}")
```

## Rules
- Artifact identity is the SHA-256 hash of the file contents.
- Two artifacts with the same hash are identical.
- Hash-based identity works across machines and time.
- If the artifact changes, the identity changes.
- Use artifact identity to verify that expected outputs match received outputs.
26 changes: 26 additions & 0 deletions docs/evidence/GUIDE_FEATURE_PROVENANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Feature Provenance Records to Scoring Outputs

Tracks which features contributed to which scores.

## Format
Each scored candidate includes feature values used in scoring:
```json
{
"candidate_id": "AMPF-000001",
"features": {
"charge_density_ph74": 0.4545,
"hydrophobic_fraction": 0.3636,
"hydrophobic_moment": 0.5741
},
"scores": {
"activity": 0.8387,
"charge_bias": 0.2345
}
}
```

## Rules
- Feature values are included in the ranked JSONL output.
- Feature provenance enables debugging and auditing.
- If a feature is derived from other features, document the derivation.
- Feature provenance should be preserved through the scoring pipeline.
17 changes: 17 additions & 0 deletions docs/evidence/GUIDE_FEATURE_RANGE_CHECKS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Feature Range Sanity Checks

Sanity checks for feature values in the pipeline.

| Feature | Min | Max | Unit | Notes |
|---------|:---:|:---:|:----:|-------|
| length | 8 | 35 | AA | Filter range |
| charge_density | -1.0 | 2.0 | e/AA | Physically possible range |
| hydrophobic_fraction | 0.0 | 1.0 | ratio | Fraction of hydrophobic AAs |
| aromatic_fraction | 0.0 | 0.5 | ratio | Fraction of F, W, Y |
| helix_propensity | 0.5 | 1.5 | Pα | Chou-Fasman scale |

## Rules
- Features outside these ranges should be flagged as warnings.
- The pipeline should not crash on out-of-range features.
- Out-of-range features should be investigated.
- If a feature is consistently out of range, check the source data.
23 changes: 23 additions & 0 deletions docs/evidence/GUIDE_SEQUENCE_VALIDATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Sequence Field Validation Report

Validates sequence fields in pipeline inputs.

## Checks Performed
- All characters are valid amino acids (A, C, D, E, F, G, H, I, K, L, M, N, P, Q, R, S, T, V, W, Y).
- Sequence length is within configured range (8-35 AA).
- Sequence is not empty.
- Sequence is uppercase.

## Example
```bash
python -c "
from openamp_foundry.data.loaders import is_valid_sequence
print(is_valid_sequence('ACDEFGHIKL')) # True
print(is_valid_sequence('ACB')) # False (B is invalid)
"
```

## Rules
- Invalid sequences should be rejected at the loading stage.
- Invalid sequences should be logged with the candidate ID.
- The pipeline should continue processing valid sequences after rejecting invalid ones.
18 changes: 18 additions & 0 deletions docs/evidence/POLICY_MISSING_VALUES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Per-Feature Missing-Value Policy

How missing feature values are handled.

## Default Values
| Feature | Default When Missing | Reason |
|---------|:-------------------:|--------|
| charge_density | 0.0 | Neutral charge assumption |
| hydrophobic_moment | 0.0 | No amphipathicity |
| helix_propensity | 1.0 | Indifferent helix propensity |
| boman_index | 0.0 | No binding potential |

## Rules
- Features should not be missing under normal pipeline operation.
- If a feature is missing, log a warning with the feature name.
- Use the default value that makes the least assumption.
- Missing features should be investigated and fixed at the source.
- The pipeline should not crash on missing features.
21 changes: 21 additions & 0 deletions docs/evidence/REGISTRY_SCORER_COMPONENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Scorer Component Registry

Registry of all scoring components in the pipeline.

| Scorer | Module | Purpose | Weight |
|--------|--------|---------|:------:|
| activity | scoring/activity.py | Antimicrobial activity-likeness | 0.40 |
| safety | scoring/safety.py | Hemolysis/toxicity risk | 0.25 |
| synthesis | scoring/synthesis.py | Synthesis feasibility | 0.15 |
| novelty | scoring/novelty.py | Novelty vs known references | 0.20 |
| ensemble | scoring/ensemble.py | Weighted combination | — |
| boman | scoring/boman.py | Boman index (binding potential) | — |
| serum_stability | scoring/stability.py | Protease resistance | — |
| hemolysis_risk | scoring/hemolysis.py | Dedicated hemolysis risk | — |
| rich_selectivity | scoring/selectivity_rich.py | Evidence-based selectivity | — |
| expert | scoring/expert.py | Safety-aware composite | — |

## Rules
- New scorers should be registered here.
- The registry should be updated when weights change.
- Each scorer should have a module in `src/openamp_foundry/scoring/`.
32 changes: 32 additions & 0 deletions docs/evidence/SCHEMA_RANK_OUTPUT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Rank Output Schema for JSONL Records

Schema for ranked candidate JSONL output.

## Record Structure
```json
{
"candidate_id": "AMPF-000001",
"sequence": "RRWQWRMKKLG",
"source": "demo",
"valid": true,
"selected": true,
"scores": {
"activity": 0.8387,
"safety": 0.9638,
"ensemble": 0.8889,
"charge_bias": 0.2345
},
"features": {
"length": 11,
"charge_density": 0.4545,
"hydrophobic_fraction": 0.3636
},
"selection_reason": ["High transparent activity-likeness score"]
}
```

## Rules
- Every record must have `candidate_id`, `sequence`, `scores`.
- `scores` must include at least `ensemble`.
- `selected` is a boolean indicating if the candidate passed all gates.
- `selection_reason` explains why a candidate was selected or rejected.
4 changes: 2 additions & 2 deletions docs/getting-started/CONTRIBUTOR_CURRICULUM.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ A staged path from first reading to a useful first pull request.

- Python 3.11+
- Basic familiarity with command line and git
- Read [CONTRIBUTING.md](../CONTRIBUTING.md)
- Read [CONTRIBUTING.md](../../CONTRIBUTING.md)

## Stage 1: Orientation (30 min)

Read these in order:

1. `README.md` — project overview and quickstart
2. `docs/evidence/LIMITATIONS_OVERVIEW.md` — honest limitations
2. [`docs/evidence/LIMITATIONS_OVERVIEW.md`](../evidence/LIMITATIONS_OVERVIEW.md) — honest limitations
3. `docs/getting-started/QUICKSTART.md` — run the demo
4. `AGENTS.md` — operating principles (useful even for humans)
5. `AGENT_TASKS.json` — safe task categories
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/HUMAN_ONBOARDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Read the docs that match your role.
### Engineer path

- [`docs/engineering/ARCHITECTURE.md`](../engineering/ARCHITECTURE.md)
- [`CONTRIBUTING.md`](../)
- [`CONTRIBUTING.md`](../../CONTRIBUTING.md)
- [`docs/evidence/BENCHMARKING.md`](../evidence/BENCHMARKING.md)
- [`docs/evidence/DECISION_RULES.md`](../evidence/DECISION_RULES.md)

Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started/QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@ python3 -m pytest -q --tb=short

## Next Steps

- Read [`docs/evidence/LIMITATIONS_OVERVIEW.md`](docs/evidence/LIMITATIONS_OVERVIEW.md) for honest limitations
- Read [`CONTRIBUTING.md`](../CONTRIBUTING.md) for contribution guidelines
- Read [`docs/evidence/LIMITATIONS_OVERVIEW.md`](../evidence/LIMITATIONS_OVERVIEW.md) for honest limitations
- Read [`CONTRIBUTING.md`](../../CONTRIBUTING.md) for contribution guidelines
- Read [`AGENTS.md`](../AGENTS.md) for the agent operating contract
24 changes: 24 additions & 0 deletions tests/test_csv_dialect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Test CSV dialect and encoding."""
from pathlib import Path
import csv


def test_demo_csv_is_readable():
with open("examples/sequences/demo_candidates.csv", newline="") as f:
reader = csv.DictReader(f)
rows = list(reader)
assert len(rows) >= 2, "Should have at least 2 demo candidates"
assert "id" in reader.fieldnames or "candidate_id" in reader.fieldnames


def test_demo_csv_utf8():
text = Path("examples/sequences/demo_candidates.csv").read_bytes()
text.decode("utf-8") # Should not raise


def test_reference_csv_is_readable():
with open("examples/known_reference/demo_known_amps.csv", newline="") as f:
reader = csv.DictReader(f)
rows = list(reader)
assert len(rows) >= 2
assert "id" in reader.fieldnames or "candidate_id" in reader.fieldnames
29 changes: 29 additions & 0 deletions tests/test_duplicate_records.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Test duplicate record handling."""
from pathlib import Path


def test_demo_candidates_no_duplicates():
"""Demo candidates should not have duplicate IDs."""
ids = []
with open("examples/sequences/demo_candidates.csv") as f:
for line in f.readlines()[1:]:
cid = line.strip().split(",")[0]
ids.append(cid)
assert len(ids) == len(set(ids)), "Duplicate candidate IDs found"


def test_demo_references_no_duplicates():
ids = []
with open("examples/known_reference/demo_known_amps.csv") as f:
for line in f.readlines()[1:]:
cid = line.strip().split(",")[0]
ids.append(cid)
assert len(ids) == len(set(ids))


def test_evidence_certificates_unique():
ev_dir = Path("outputs/evidence")
if ev_dir.exists():
files = list(ev_dir.glob("*.json"))
ids = [f.stem for f in files]
assert len(ids) == len(set(ids))
19 changes: 19 additions & 0 deletions tests/test_scorer_registry_coverage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Test scorer registry coverage."""
from pathlib import Path


def test_scoring_modules_exist():
scoring_dir = Path("src/openamp_foundry/scoring")
assert scoring_dir.exists()
modules = [f.stem for f in scoring_dir.glob("*.py") if f.stem != "__init__"]
assert len(modules) >= 10, f"Only {len(modules)} scoring modules found"


def test_ensemble_scorer_importable():
from openamp_foundry.scoring.ensemble import ensemble_score
assert callable(ensemble_score)


def test_activity_scorer_importable():
from openamp_foundry.scoring.activity import activity_likeness_score
assert callable(activity_likeness_score)
Loading