Skip to content

Latest commit

 

History

History
702 lines (541 loc) · 28.4 KB

File metadata and controls

702 lines (541 loc) · 28.4 KB

FoldForge

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:

  • FoldSpec is 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.

Roadmap Strategy

FoldForge is being built from the inside out:

  1. Deterministic core: FoldSpec, validation, backend exports, runners, parsers, and reports.
  2. CLI backbone: commands that expose every workflow in a scriptable, testable form.
  3. Visualization/report layer: static reports and confidence plots generated from parsed results.
  4. GUI application: a thin Swift/SwiftUI workflow over the same core modules, with no backend logic hidden in UI code.
  5. 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.

Current Scope

Implemented in this scaffold:

  • Python 3.12 package named foldfoundry
  • Public app/CLI command named foldforge, with foldfoundry kept 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 FoldSpec files
  • 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

Run The App

For the human GUI in a source checkout:

./run-app

With 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/foldforge

Running ./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.json

For 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.yaml

For 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.yaml

FoldForge 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.

FoldSpec YAML

Minimal example:

name: simple_protein
backend: alphafold3
seeds: [1]
entities:
  - id: A
    type: protein
    sequence: "MKT"
run:
  mode: export_only

This 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_only

Dry-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:test

Validation rules:

  • backend must be alphafold3
  • run.mode must be export_only or dry_run
  • dry_run mode requires model_dir and database_dir
  • seeds must be a non-empty list of unique strict integers greater than or equal to 1
  • entities must be non-empty
  • entity IDs must be unique strings
  • unknown fields are rejected

CLI

Validate a job:

foldfoundry validate examples/simple_protein.yaml
foldfoundry --version

Export AlphaFold 3 JSON:

foldfoundry export examples/simple_protein.yaml --backend alphafold3 --out runs/simple_protein/input.json

Build 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.json

To 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.json

The 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.json

If 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 --json

Benchmark 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 foldforge

The 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-only

The 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.

Optional live resolver smoke tests

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 live

The 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 --live

Optional live provider smoke tests

Real 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 -rs

Manual 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 \
  --json

Short 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.json

Prepare 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_protein

Summarize an AlphaFold 3 output directory:

foldfoundry summarize tests/fixtures/af3_outputs/hello_fold

The 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/login

This 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 --open

This 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/browser

If 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/browser

This 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-run

Carbonyl 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 --force

For 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.yaml

submit 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-run

Typical manual handoff:

  1. Run foldfoundry submit ....
  2. Open AlphaFold Server in your normal browser.
  3. Sign in with Google if needed.
  4. Choose the AlphaFold Server Upload JSON action.
  5. Select the printed .afserver.json file in Downloads, or paste its absolute path.
  6. Review the draft and submit manually.

For local development and packaged runs, run-app is the only repo-root launch shortcut:

./run-app

With 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-run

Useful submit options:

  • --out-dir PATH chooses a directory for alphafold_server.json and events.jsonl. Without it, FoldForge writes <job>.afserver.json and events.jsonl in ~/Downloads.
  • --force overwrites existing generated submit artifacts and ZIP conflicts.
  • --dry-run is accepted for compatibility; submit already prepares files only.
  • Browser automation flags such as --browser, --chrome, --port, --profile, --connect-only, and --timeout-hours are 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 \
  --force

Open 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.

G2 HPC Submission

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.json

Profiles 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.edu

G2 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.

AlphaFold Server Mini-Browser Direction

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
  • --out must be a file path, not a directory
  • existing files fail unless --force is passed
  • foldfoundry run writes input.json into the run directory and creates an output/ directory for later AF3 results

AlphaFold 3 Export

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": ["..."]}}

AlphaFold 3 Summaries

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.

Development

Install dependencies with uv when available:

uv sync --dev

When 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 --help

This 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.yaml

On 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 doctor

If 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 --fix

doctor --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 src

GitHub 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.