FoldForge is a local-first structure-prediction workflow app. The long-term product is a GUI-centered application for preparing, validating, running, and interpreting folding jobs, but the backbone is being built through CLI-first slices so the core models, backend adapters, runners, parsers, and reports are fast to develop and easy to test.
The current slices turn a small, validated FoldSpec YAML file into deterministic AlphaFold 3 input JSON, a dry-run command preview, and a normalized summary of AF3 output directories.
The product center is the deterministic core:
FoldSpecis the source of truth.- Backend JSON is generated output.
- The CLI is the automation, agent, and testing interface for the core.
- The GUI is the primary human-facing layer and must stay thin over the same workflows.
- LLM patching remains an optional assistant layer, not the source of truth.
FoldForge is being built from the inside out:
- Deterministic core:
FoldSpec, validation, backend exports, runners, parsers, and reports. - CLI backbone: commands that expose every workflow in a scriptable, testable form.
- Visualization/report layer: static reports and confidence plots generated from parsed results.
- GUI application: a thin Swift/SwiftUI workflow over the same core modules, with no backend logic hidden in UI code.
- Optional assistant layer: natural-language patch generation that proposes inspectable changes to
FoldSpec.
The CLI is not the final human experience. It remains first-class for agents, bots, scripts, tests, and CI; every GUI operation must have an equivalent CLI workflow.
Implemented in this scaffold:
- Python 3.12 package named
foldfoundry - Public app/CLI command named
foldforge, withfoldfoundrykept as the Python package and backward-compatible CLI alias - Human GUI launcher named
foldforge - Thin Swift/SwiftUI macOS GUI shell over CLI-equivalent workflows
- PyInstaller packaging target for a macOS terminal executable at
dist/foldforge - YAML validation for inline
FoldSpecfiles - AlphaFold 3 export-only JSON
- AlphaFold 3 local dry-run command preparation
- AlphaFold 3 result parsing and
summarize - AlphaFold Server upload JSON and manual browser submission instructions
- Plain-language structure-builder inputs with FASTA ingestion, preview, fusion linkers, segment/reference FoldSpec normalization, and AlphaFold Server export
- DeepSeek/GPT-Nano parser benchmarking through a shared adapter and scorer
- Protein, DNA, RNA, ligand SMILES, and ligand CCD inputs
- Golden fixture tests for deterministic export
- G2 HPC bundle preparation, profile doctor checks, reviewed SLURM submission, status, fetch, and cancel helpers
Explicitly out of scope unless a command says otherwise:
- job execution, Docker invocation, subprocesses, or network calls
- backend logic in GUI code
- full Textual-style terminal apps
- report rendering
- SDF, MSA, template, modification, userCCD, and path-based non-FASTA inputs
- AF3 multi-copy entity IDs
- compatibility with AF3 JSON versions older than version 4
For the human GUI in a source checkout:
./run-appWith no arguments, run-app builds and opens the native Swift FoldForge GUI.
The GUI can search sequence sources from a plain-English description, build a
FoldSpec, validate a FoldSpec, export AlphaFold Server Upload JSON, prepare the
manual AlphaFold Server upload handoff, export AlphaFold 3 JSON, prepare an AF3
dry-run command, and summarize AF3 output directories. In the build workflow,
choosing AlphaFold Server writes a sibling .afserver.json Upload JSON before
opening Chrome; the .foldspec.json file is FoldForge's local source file, not
the server upload file. The detail pane always shows the matching
foldforge ... command so the GUI stays paired to the scriptable surface.
For a packaged terminal executable on macOS:
uv sync --group packaging
uv run pyinstaller packaging/foldforge.spec
./dist/foldforgeRunning ./dist/foldforge with no arguments opens the guided terminal workflow.
Advanced users can pass CLI arguments through the same executable:
./dist/foldforge validate examples/simple_protein.yaml
./dist/foldforge export examples/simple_protein.yaml --backend alphafold3 --out input.jsonFor development CLI work, pass commands through run-app or use foldforge
directly:
./run-app validate examples/simple_protein.yaml
uv run foldforge validate examples/simple_protein.yamlFor scriptable and advanced workflows, foldforge is the public spelling. The
older foldfoundry entry point remains available for compatibility:
uv run foldforge --help
uv run foldforge validate examples/simple_protein.yaml
uv run foldfoundry validate examples/simple_protein.yamlFoldForge keeps biological sequences local unless you explicitly choose a live provider, public resolver lookup, manual AlphaFold Server handoff, or other command documented as remote-facing.
Minimal example:
name: simple_protein
backend: alphafold3
seeds: [1]
entities:
- id: A
type: protein
sequence: "MKT"
run:
mode: export_onlyThis tiny MKT job is the golden fixture for deterministic local tests. For live
AlphaFold Server upload checks, use examples/server_toy_protein.yaml; the
server rejects very short toy sequences before it will enable draft submission.
Protein, DNA, and RNA entities use inline sequence. Ligands use exactly one of smiles or ccd_code.
name: protein_ligand
backend: alphafold3
seeds: [1, 2]
entities:
- id: A
type: protein
sequence: "MKT"
- id: L
type: ligand
ccd_code: ATP
run:
mode: export_onlyDry-run local AF3 command preview:
name: dry_run_protein
backend: alphafold3
seeds: [1]
entities:
- id: A
type: protein
sequence: "MKT"
run:
mode: dry_run
model_dir: /models/af3
database_dir: /databases/af3
container_image: alphafold3:testValidation rules:
backendmust bealphafold3run.modemust beexport_onlyordry_rundry_runmode requiresmodel_diranddatabase_dirseedsmust be a non-empty list of unique strict integers greater than or equal to 1entitiesmust be non-empty- entity IDs must be unique strings
- unknown fields are rejected
Validate a job:
foldfoundry validate examples/simple_protein.yaml
foldfoundry --versionExport AlphaFold 3 JSON:
foldfoundry export examples/simple_protein.yaml --backend alphafold3 --out runs/simple_protein/input.jsonBuild structure prediction jobs from plain language:
foldfoundry build \
"Model HER2 extracellular domain fused to this affibody sequence with GGGGSx3" \
--fasta AFFIBODY=affibody.fasta \
--assembly fusion \
--linker GGGGSx3 \
--out her2_affibody.foldspec.json
foldfoundry preview her2_affibody.foldspec.json
foldfoundry export her2_affibody.foldspec.json --backend alphafoldserver --out her2_affibody.afserver.json
foldfoundry submit her2_affibody.afserver.jsonTo search the molecules inferred from a description before building:
foldfoundry resolve "qbeta coat protein fused to an anti-HER2 affibody" --infer-terms
foldfoundry build "qbeta coat protein fused to an anti-HER2 affibody" --resolve --allow-defaults --out qbeta_zher2.foldspec.jsonThe builder layer sits above the existing submission system. Existing direct
sequence FoldSpecs continue to work; richer builder specs use a sequence
registry plus chain segments, then normalize deterministically into export-ready
FoldSpec entities. Submission remains the already-automated AlphaFold Server
flow; the builder only prepares validated inputs for it.
Repeated FASTA inputs are supported with optional labels:
foldfoundry build "Model these as separate chains" \
--fasta AFFIBODY=affibody.fasta \
--fasta CARGO=cargo.fasta \
--out job.foldspec.jsonIf the request uses ambiguous language such as "connect" or "attach", build
defaults to separate_chains, marks the spec ambiguous, and emits a warning
instead of silently connecting sequences. Use --assembly fusion, --linker,
and --allow-defaults to make non-interactive choices explicit.
Benchmark DeepSeek and GPT-Nano request parsing without live providers:
foldfoundry benchmark request.txt --fasta affibody.fasta --mock --jsonBenchmark behavior is intentionally conservative:
| Command | Behavior |
|---|---|
foldfoundry benchmark request.txt |
mock/default, no network |
foldfoundry benchmark request.txt --mock |
mock, no network |
foldfoundry benchmark request.txt --providers deepseek,gpt_nano |
error requiring --live |
foldfoundry benchmark request.txt --providers deepseek,gpt_nano --live |
real provider calls after credential preflight |
foldfoundry benchmark request.txt --mock --live |
error |
Real provider calls are optional and require --live. API keys are read from
environment variables or local .env only; .env is ignored by Git and must not
be committed. Supported key names are DeepSeek_API / DEEPSEEK_API_KEY and
OpenAI_API / OPENAI_API_KEY. Environment variables take precedence over
.env. DEEPSEEK_MODEL and OPENAI_MODEL configure model IDs; the GPT-Nano
default is gpt-5.4-nano. Benchmark JSONL records are stored under
.foldfoundry/benchmarks/ by default, or at --benchmark-log PATH; raw model
output is omitted unless --include-raw-model-output is explicitly supplied, and
private FASTA sequences are redacted from those debug logs.
Use --live only for sequences you are comfortable sending to the selected
provider.
LLMs propose assembly plans only. Deterministic code resolves, validates,
assembles, previews, and exports. Auto-found sequences are risky candidate
sequences, stored as source="auto" and verified=false; they are shown in
preview and require explicit confirmation with --allow-auto-sequence before
export.
Interactive terminal menus are available as a thin InquirerPy layer:
./run-app
uv run foldforgeThe TUI menu covers build, resolve, benchmark, preview, and export flows without
making you remember the CLI flags. The prompts include short inline guidance and
contextual help choices for assembly mode, fusion linkers, export backends, and
browser/submission boundaries. The interactive prompts call the same services as
the non-interactive CLI; tests target the core modules and scriptable commands.
After a guided build writes a FoldSpec, the TUI stays in the job workflow and
asks for the next action: prepare AlphaFold Server upload JSON, export AlphaFold
Server JSON, export AlphaFold 3 JSON, preview again, return to the main menu, or
exit. A FoldSpec named qbeta_zher2.foldspec.json defaults to
~/Downloads/qbeta_zher2.afserver.json for AlphaFold Server and
qbeta_zher2.af3.json next to the FoldSpec for AlphaFold 3.
If the standalone sequence resolver is given a full structure description, it
offers to resolve the inferred molecules separately or jump into the guided build
with the same description.
Sequence lookup is federated. When a TUI build has no FASTA input, FoldFoundry asks before searching public databases, then searches the local/private registry, UniProtKB, NCBI Protein, and RCSB/PDB. Candidates are source-labeled and can be opened in Chrome before selection. Public resolvers try the exact search phrase first, then bounded contiguous phrase variants when the exact phrase misses; this keeps search from stopping at the first brittle full-text miss without adding domain-specific keyword rules.
foldfoundry registry add "HER2 nanobody 27A05" --fasta nanobody.fasta --alias "anti HER2 binder"
foldfoundry registry search "anti HER2 binder"
foldfoundry resolve "anti HER2 nanobody" --sources local,uniprot,ncbi,rcsb --limit-per-source 5
foldfoundry resolve "qbeta coat protein fused to an anti HER2 affibody" --infer-terms
foldfoundry resolve "private binder" --local-onlyThe default private registry is ~/.foldfoundry/sequences.jsonl. Project-local
.foldfoundry/sequences.jsonl is ignored by Git if it is created. NCBI Protein
lookup requires NCBI_EMAIL; NCBI_TOOL and NCBI_API_KEY are optional.
Selected public candidates become resolved FoldSpec sequences only after user
selection and deterministic validation, with accession, URL, query, and source
provenance retained.
Normal tests are mock-only and do not call UniProt, NCBI, RCSB, AlphaFold Server, or any model provider. To verify public resolver integration on a machine with network access, opt in explicitly:
FOLDFOUNDRY_RUN_LIVE_TESTS=1 uv run pytest -m liveThe live smoke tests cover UniProtKB, NCBI Protein, and RCSB/PDB. NCBI's live
test also requires NCBI_EMAIL; NCBI_TOOL and NCBI_API_KEY remain optional.
The same live resolver path can be exercised manually through the CLI:
uv run foldfoundry resolve "P04626" --sources uniprot --limit-per-source 3 --live
uv run foldfoundry resolve "NP_004439.2" --sources ncbi --limit-per-source 3 --live
uv run foldfoundry resolve "HER2 extracellular domain" --sources rcsb --limit-per-source 3 --liveReal DeepSeek/OpenAI parser benchmarking is also opt-in. Normal tests and normal
foldfoundry benchmark runs are mock-only. To run tiny provider smoke tests on a
machine with configured keys:
FOLDFOUNDRY_RUN_PROVIDER_TESTS=1 uv run pytest -m provider_live -rsManual live benchmark smoke:
uv run foldfoundry benchmark request.txt \
--providers deepseek,gpt_nano \
--live \
--fasta AFFIBODY=tests/fixtures/affibody_toy.fasta \
--fasta CARGO=tests/fixtures/cargo_toy.fasta \
--benchmark-log .foldfoundry/benchmarks/provider-smoke.jsonl \
--jsonShort compatibility flow:
foldfoundry build "HER2 extracellular domain fused to this sequence" --fasta affibody.fasta --allow-defaults --out job.foldspec.json
foldfoundry preview job.foldspec.json
foldfoundry export job.foldspec.json --backend alphafoldserver --out exported_job.json
foldfoundry submit exported_job.jsonPrepare an AlphaFold 3 dry-run directory and print the Docker command:
foldfoundry run examples/af3_dry_run.yaml --backend alphafold3-local --dry-run --out-dir runs/dry_run_proteinSummarize an AlphaFold 3 output directory:
foldfoundry summarize tests/fixtures/af3_outputs/hello_foldThe summary is deterministic tab-separated text with one row per ranked prediction. It includes job name, backend, rank, seed, sample, ranking score, mean atom pLDDT when atom_plddts is available, pTM, ipTM, clash status, and relevant result paths.
Inspect login-form detection on a local dummy page:
foldfoundry vault inspect tests/fixtures/dummy_login_site.html --url https://dummy.test/loginThis is the first local vault-helper test harness. It detects username/password field candidates from ordinary HTML and prints deterministic selector output. It does not require Carbonyl, Chrome, Google, AlphaFold Server, or network access.
Prepare an AlphaFold Server upload JSON file for manual web submission:
foldfoundry server examples/server_toy_protein.yaml --openThis writes dialect: "alphafoldserver" JSON for AlphaFold Server's Upload JSON flow and optionally opens AlphaFold Server in your system browser. FoldForge does not log in, click through the site, scrape results, or submit jobs automatically from the export command. AlphaFold Server upload JSON supports the server's allowed CCD ligands; arbitrary SMILES ligands are rejected for this workflow.
The old foldfoundry browser command remains as explicit experimental Carbonyl
debug plumbing for preparing upload JSON plus a local debug bundle. It is not
the recommended AlphaFold Server workflow; guided submission and the default
submit command use Chrome.
foldfoundry browser examples/server_toy_protein.yaml --out-dir runs/toy_protein/browserIf foldfoundry is not on your shell PATH, run it through the local project environment:
python3 -m uv run --no-editable --reinstall-package foldfoundry foldfoundry browser examples/server_toy_protein.yaml --out-dir runs/toy_protein/browserThis writes the same AlphaFold Server upload JSON plus a local debug bundle under the session directory, then launches Carbonyl at AlphaFold Server. The browser command is experimental and remains completely user-operated. Use --dry-run to prepare the files and print the Carbonyl command without launching a browser:
foldfoundry browser examples/server_toy_protein.yaml --out-dir runs/toy_protein/browser --dry-runCarbonyl may render AlphaFold Server's upload button without exposing a usable native file picker in the terminal. The browser command prints both the relative and absolute upload JSON paths; use the absolute path if the browser asks for a file path or if a user-directed path-injection workflow is needed.
Carbonyl is not bundled; install it separately or pass --carbonyl /path/to/carbonyl.
Google Sign-In may reject plain Carbonyl with a "browser or app may not be secure" warning. That is an OAuth policy limitation for nonstandard, embedded, headless, automation-capable, or text-based browsers, not a TLS problem. If you have carbonyl-stealth, pass it explicitly:
python3 -m uv run --no-editable --reinstall-package foldfoundry foldfoundry browser examples/server_toy_protein.yaml --out-dir runs/toy_protein/browser --carbonyl carbonyl-stealth --forceFor a live server smoke test, stop after AlphaFold Server shows a green check for
toy_protein and enables Submit job as draft. That verifies the generated JSON
is accepted without consuming quota or submitting a job.
Prepare an AlphaFold Server upload JSON file with manual submission steps:
foldfoundry submit examples/server_toy_protein.yamlsubmit writes the final server-bound JSON to ~/Downloads/<job>.afserver.json
by default, prints the absolute path, and gives the exact manual AlphaFold
Server steps. It does not open Chrome, automate Google login, upload files,
click submit, poll results, download files, interact with hidden AlphaFold
Server APIs, or bypass quotas.
foldfoundry submit examples/simple_protein.yaml --out-dir /tmp/test --dry-runTypical manual handoff:
- Run
foldfoundry submit .... - Open AlphaFold Server in your normal browser.
- Sign in with Google if needed.
- Choose the AlphaFold Server Upload JSON action.
- Select the printed
.afserver.jsonfile in Downloads, or paste its absolute path. - Review the draft and submit manually.
For local development and packaged runs, run-app is the only repo-root launch
shortcut:
./run-appWith no arguments it opens the Swift GUI. If uv is available, argument-bearing
commands run against the current source checkout so local fixes are not hidden
behind a stale packaged executable. To test the packaged executable explicitly,
run FOLDFORGE_USE_DIST=1 ./run-app validate examples/simple_protein.yaml. Pass
any advanced CLI command after ./run-app:
./run-app --help
./run-app submit examples/server_toy_protein.yaml --force
./run-app submit-json /path/to/alphafold_server.json --dry-runUseful submit options:
--out-dir PATHchooses a directory foralphafold_server.jsonandevents.jsonl. Without it, FoldForge writes<job>.afserver.jsonandevents.jsonlin~/Downloads.--forceoverwrites existing generated submit artifacts and ZIP conflicts.--dry-runis accepted for compatibility; submit already prepares files only.- Browser automation flags such as
--browser,--chrome,--port,--profile,--connect-only, and--timeout-hoursare accepted for old scripts but ignored in manual submit mode.
Manual acceptance test requiring a Google account and network:
PYTHONPATH=src uv run --no-sync python -m foldfoundry.cli submit \
examples/server_toy_protein.yaml \
--forceOpen AlphaFold Server yourself, upload the printed file from ~/Downloads, and
stop after AlphaFold Server accepts the JSON and shows a draft preview if you do
not want to consume quota. If examples/simple_protein.yaml is rejected by the
live server for being too small, use examples/server_toy_protein.yaml.
For foldforge export, the CLI --backend value must match the YAML backend. It validates intent but never overrides FoldSpec.
For foldforge run, the YAML backend remains alphafold3; --backend alphafold3-local selects the local runner preview. The command requires --dry-run and never executes Docker or AlphaFold.
For foldforge summarize, the input is an existing local AF3 output directory. It only reads files and never runs AlphaFold.
For foldforge server, the input is a validated local FoldSpec; the command writes a JSON draft for manual upload and never transmits biological data itself.
For foldforge browser, the input is a validated local FoldSpec; the legacy command writes a browser session directory and launches Carbonyl only after an explicit user command. FoldForge does not upload or submit anything automatically in this mode.
For foldforge submit, the input is a validated local FoldSpec or existing AlphaFold Server JSON; the command writes a submit directory and prints manual upload instructions. It never launches a browser or transmits biological data itself.
For foldforge hpc prepare, the input is a validated local FoldSpec; the
command writes a hashed review bundle and does not SSH, rsync, run subprocesses,
contact G2, or execute AlphaFold. foldforge hpc submit accepts only the
prepared slurm_plan.json, verifies hashes, transfers that exact bundle, and
runs sbatch --parsable.
FoldForge's G2 workflow is explicit: prepare locally, run doctor against the cluster, review the generated bundle, then submit that exact bundle.
foldforge hpc list-profiles
foldforge hpc inspect coskunuzer-h100
foldforge hpc prepare examples/simple_protein.yaml --backend alphafold3 --profile coskunuzer-h100 --out-dir runs/simple_protein/g2
foldforge hpc doctor --profile coskunuzer-h100
foldforge hpc submit runs/simple_protein/g2/slurm_plan.json
foldforge hpc status runs/simple_protein/g2/slurm_submission.json
foldforge hpc fetch runs/simple_protein/g2/slurm_submission.json --out-dir runs/simple_protein/g2/results
foldforge hpc cancel runs/simple_protein/g2/slurm_submission.jsonProfiles load from ~/.foldforge/hpc-profiles.yaml; set
FOLDFORGE_HPC_PROFILE_PATH to use another file. Example profiles live in
examples/hpc/profiles.yaml and cover coskunuzer-h100, rotea-h100, and
gpu-preempt-h100. Replace /groups/<pi-name> placeholders before preparing
or running doctor.
Recommended AF3 resources on G2 are one task on one H100 NVL:
nodes: 1, ntasks: 1, cpus_per_task: 8, memory: 64G,
time: "08:00:00", gres: gpu:nvidia_h100_nvl:1, and requeue: false.
FoldForge rejects cpus_per_gpu in this first HPC slice; AF3 uses
cpus_per_task for the one-process-per-GPU shape.
Persistent files should live under /groups/<pi-name>/foldforge/runs.
Per-job scratch I/O uses ~/scratch; AF3 model weights, databases, and the
container image stay under /groups/<pi-name>/... and are bind-mounted
read-only into Apptainer. The generated job.sbatch copies only per-job input,
outputs, and logs, and never rsyncs model or database directories.
Configure SSH through ~/.ssh/config and keep the target name configurable:
Host ganymede2
HostName ganymede2.utdallas.edu
User <NetID>
# Some CIRC docs show this hostname instead:
# HostName ganymede2.circ.utdallas.eduG2 access requires the UTD campus network, CometNet, wired campus, or UTD VPN.
Users who prefer a browser-based cluster entry point can use Open OnDemand at
https://g2-ood.circ.utdallas.edu/; FoldForge does not need to implement that
path to prepare reviewed bundles.
References: UTD Ganymede2, CIRC Ganymede2, Slurm sbatch, Slurm squeue, and Apptainer GPU support.
The CLI now supports a Carbonyl-powered mini-browser session, and the GUI should eventually include the same idea as an embedded Chromium-based mini-browser for AlphaFold Server work. The browser command is user-operated: users sign in, upload JSON, review drafts, and submit jobs themselves. FoldForge's role is to keep the submission context inspectable and debuggable:
- generate the official AlphaFold Server upload JSON from validated
FoldSpec - show the current local spec and generated JSON next to the browser
- expose the absolute upload JSON path because terminal browsers may not support a usable file picker
- capture browser console and network errors for local debugging when the selected browser backend exposes them
- capture optional screenshots and downloaded result zips when the user asks
- associate downloads with the originating
FoldSpec
The submit command is a manual handoff helper: it prepares the Upload JSON,
prints the exact local file path, and stops. No FoldForge command may automate
Google login, bypass quotas, scrape hidden APIs, or upload biological data.
Output behavior:
- missing parent directories are created
--outmust be a file path, not a directory- existing files fail unless
--forceis passed foldfoundry runwritesinput.jsoninto the run directory and creates anoutput/directory for later AF3 results
FoldForge emits one AF3 job per JSON file as a top-level object:
{
"name": "simple_protein",
"modelSeeds": [1],
"sequences": [
{
"protein": {
"id": "A",
"sequence": "MKT"
}
}
],
"dialect": "alphafold3",
"version": 4
}Entity mappings:
type: protein->{"protein": {"id": "...", "sequence": "..."}}type: dna->{"dna": {"id": "...", "sequence": "..."}}type: rna->{"rna": {"id": "...", "sequence": "..."}}- ligand
smiles->{"ligand": {"id": "...", "smiles": "..."}} - ligand
ccd_code->{"ligand": {"id": "...", "ccdCodes": ["..."]}}
The AF3 parser expects the output layout documented by Google DeepMind:
<job_name>_ranking_scores.csv- top-ranked root files:
<job_name>_model.cif,<job_name>_summary_confidences.json, and optionally<job_name>_confidences.json - per-sample directories named
seed-<seed>_sample-<sample>/
The parser reads summary confidence metrics such as ranking_score, ptm, iptm, fraction_disordered, and has_clash. It computes mean atom pLDDT only when atom_plddts is present in a confidence JSON.
Install dependencies with uv when available:
uv sync --devWhen testing the installed console script from a source checkout, uv's non-editable install mode can force the local package to rebuild:
uv run --no-editable --reinstall-package foldfoundry foldfoundry --helpThis path has been tested with uv 0.11.7. If an older uv does not support
--no-editable or --reinstall-package, upgrade uv before using this exact
workaround.
For local development, use the normal console command after syncing the project:
uv sync
uv run foldforge
uv run foldfoundry --help
uv run foldfoundry validate examples/simple_protein.yamlOn macOS, editable installs can fail if .pth files in .venv are marked with
the Finder hidden flag. Python skips hidden .pth files, so the generated
foldfoundry console script may exist while import foldfoundry still fails.
Check for this with:
PYTHONPATH=src uv run --no-sync python -m foldfoundry.cli doctorIf doctor reports a hidden FoldFoundry editable .pth file, repair only that
project-related file:
PYTHONPATH=src uv run --no-sync python -m foldfoundry.cli doctor --fixdoctor --fix reports unrelated hidden .pth files but does not modify them.
Required checks:
uv run pytest
uv run pytest --cov=foldfoundry --cov-report=term-missing --cov-fail-under=85
uv run ruff check .
uv run ruff format --check .
uv run mypy srcGitHub Actions runs the same checks on Linux and macOS for Python 3.12, 3.13, and 3.14, including the coverage gate and CLI smoke tests. Ruff and mypy still target Python 3.12 because it is the declared minimum supported runtime.
No unit test should require network access, Docker, GPUs, model weights, databases, or live AlphaFold installation.