factorforge-cds 3.5.4__tar.gz → 3.5.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {factorforge_cds-3.5.4/src/factorforge_cds.egg-info → factorforge_cds-3.5.10}/PKG-INFO +51 -4
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/README.md +49 -3
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/pyproject.toml +2 -1
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/cli/main.py +26 -11
- factorforge_cds-3.5.10/src/factorforge/closed_loop/__init__.py +27 -0
- factorforge_cds-3.5.10/src/factorforge/closed_loop/contracts.py +103 -0
- factorforge_cds-3.5.10/src/factorforge/closed_loop/ledger.py +222 -0
- factorforge_cds-3.5.10/src/factorforge/closed_loop/readiness.py +172 -0
- factorforge_cds-3.5.10/src/factorforge/codon/__init__.py +25 -0
- factorforge_cds-3.5.10/src/factorforge/codon/allocator.py +87 -0
- factorforge_cds-3.5.10/src/factorforge/codon/policy.py +212 -0
- factorforge_cds-3.5.10/src/factorforge/codon/qa.py +166 -0
- factorforge_cds-3.5.10/src/factorforge/codon/twin_generator.py +338 -0
- factorforge_cds-3.5.10/src/factorforge/core/__init__.py +35 -0
- factorforge_cds-3.5.10/src/factorforge/core/experiment.py +282 -0
- factorforge_cds-3.5.10/src/factorforge/core/policy.py +403 -0
- factorforge_cds-3.5.10/src/factorforge/data/human_stemcell_codons.json +329 -0
- factorforge_cds-3.5.10/src/factorforge/data/profiles/human_stemcell_derived_codons.json +329 -0
- factorforge_cds-3.5.10/src/factorforge/data/profiles/human_stemcell_policy.json +76 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/__init__.py +26 -0
- factorforge_cds-3.5.10/src/factorforge/engines/dp_v2_2.py +617 -0
- factorforge_cds-3.5.10/src/factorforge/engines/dp_v2_3.py +770 -0
- factorforge_cds-3.5.10/src/factorforge/engines/local_rewriter.py +1575 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/utils.py +1 -0
- factorforge_cds-3.5.10/src/factorforge/engines/sllm/adapters.py +241 -0
- factorforge_cds-3.5.10/src/factorforge/engines/stemcell_3dp.py +395 -0
- factorforge_cds-3.5.10/src/factorforge/engines/structural_pause.py +219 -0
- factorforge_cds-3.5.10/src/factorforge/engines/structure_web.py +111 -0
- factorforge_cds-3.5.10/src/factorforge/guards/__init__.py +15 -0
- factorforge_cds-3.5.10/src/factorforge/guards/initiation_mfe.py +141 -0
- factorforge_cds-3.5.10/src/factorforge/guards/plant_rna_guards.py +303 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/io/__init__.py +2 -2
- factorforge_cds-3.5.10/src/factorforge/io/package_compiler.py +467 -0
- factorforge_cds-3.5.10/src/factorforge/nbfold/__init__.py +94 -0
- factorforge_cds-3.5.10/src/factorforge/nbfold/annotations.py +1743 -0
- factorforge_cds-3.5.10/src/factorforge/nbfold/bridge.py +1200 -0
- factorforge_cds-3.5.10/src/factorforge/nbfold/cli.py +228 -0
- factorforge_cds-3.5.10/src/factorforge/nbfold/contracts.py +303 -0
- factorforge_cds-3.5.10/src/factorforge/nbfold/pipeline.py +1580 -0
- factorforge_cds-3.5.10/src/factorforge/pipeline/__init__.py +5 -0
- factorforge_cds-3.5.10/src/factorforge/pipeline/design_pipeline.py +631 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/registry/version_manifest.json +30 -3
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/rules/registry.py +80 -5
- factorforge_cds-3.5.10/src/factorforge/structure/__init__.py +11 -0
- factorforge_cds-3.5.10/src/factorforge/structure/auto_annotator.py +296 -0
- factorforge_cds-3.5.10/src/factorforge/structure/auto_resolver.py +174 -0
- factorforge_cds-3.5.10/src/factorforge/telemetry/__init__.py +19 -0
- factorforge_cds-3.5.10/src/factorforge/telemetry/gcs_reconciliation.py +266 -0
- factorforge_cds-3.5.10/src/factorforge/telemetry/training_logger.py +600 -0
- factorforge_cds-3.5.10/src/factorforge/validation/tradeoff_suite.py +591 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10/src/factorforge_cds.egg-info}/PKG-INFO +51 -4
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge_cds.egg-info/SOURCES.txt +65 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge_cds.egg-info/requires.txt +1 -0
- factorforge_cds-3.5.10/tests/test_3dp_review_regressions.py +33 -0
- factorforge_cds-3.5.10/tests/test_3dp_validation_suite.py +149 -0
- factorforge_cds-3.5.10/tests/test_auto_annotator.py +111 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_benchmark_regression.py +2 -1
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_benchmark_smoke.py +2 -2
- factorforge_cds-3.5.10/tests/test_candidate_review_safety.py +98 -0
- factorforge_cds-3.5.10/tests/test_dp_v2_2_0.py +153 -0
- factorforge_cds-3.5.10/tests/test_dp_v2_3_0.py +184 -0
- factorforge_cds-3.5.10/tests/test_durable_reconciliation.py +225 -0
- factorforge_cds-3.5.10/tests/test_enterprise_integration_396.py +142 -0
- factorforge_cds-3.5.10/tests/test_hpx_viewer.py +125 -0
- factorforge_cds-3.5.10/tests/test_initiation_mfe.py +54 -0
- factorforge_cds-3.5.10/tests/test_job350_codon_twins.py +159 -0
- factorforge_cds-3.5.10/tests/test_job351_closed_loop_foundation.py +253 -0
- factorforge_cds-3.5.10/tests/test_job355_design_experiment.py +523 -0
- factorforge_cds-3.5.10/tests/test_local_rewriter.py +353 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_multi_engine_governance.py +3 -3
- factorforge_cds-3.5.10/tests/test_nbfold_phase_c.py +767 -0
- factorforge_cds-3.5.10/tests/test_nbfold_phase_c_benchmark.py +817 -0
- factorforge_cds-3.5.10/tests/test_nbfold_pipeline.py +897 -0
- factorforge_cds-3.5.10/tests/test_oct23_review.py +73 -0
- factorforge_cds-3.5.10/tests/test_onnx_adapter_contract.py +126 -0
- factorforge_cds-3.5.10/tests/test_plant_rna_guards.py +139 -0
- factorforge_cds-3.5.10/tests/test_policy_type_iis_targets.py +30 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_rule_registry.py +32 -0
- factorforge_cds-3.5.10/tests/test_scientific_web_397.py +168 -0
- factorforge_cds-3.5.10/tests/test_stemcell_3dp.py +255 -0
- factorforge_cds-3.5.10/tests/test_structural_pause.py +82 -0
- factorforge_cds-3.5.10/tests/test_training_logger.py +266 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_version_manifest.py +5 -2
- factorforge_cds-3.5.4/src/factorforge/engines/sllm/adapters.py +0 -193
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/LICENSE +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/setup.cfg +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/__main__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/analysis/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/analysis/feasibility.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/analysis/metrics.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/analysis/sequence_risks.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/benchmark/models.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/benchmark/runner.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/cli/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/cli/benchmark_cli.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/cli/legacy_cli.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/constraints/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/constraints/type_iis.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/core/host_model.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/core/interfaces/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/core/interfaces/exporter.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/core/interfaces/optimizer.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/core/interfaces/validator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/core/reranker.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/core/slate_engine.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/core/slate_generator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/nbenthamiana_codons.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/nbenthamiana_golden_set.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/ntabacum_codons.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/profiles/nbev11_cds_all_derived_codons.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/profiles/nbev11_cds_all_derived_filtered_stats.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/profiles/nbev11_cds_all_derived_manifest.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/profiles/nbev11_cds_hc_derived_codons.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/profiles/nbev11_cds_hc_derived_filtered_stats.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/profiles/nbev11_cds_hc_derived_manifest.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/profiles/qld183_v103_derived_codons.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/profiles/qld183_v103_derived_manifest.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/profiles/qld183_v103_filtered_stats.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/reference/reference_policy_manifest.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/templates/high_expression.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/templates/standard_expression.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/data/wolffia_globosa_codons.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/database.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/db/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/db/connector.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/design_review.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/discovery/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/discovery/acquisition.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/discovery/acquisition_logger.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/discovery/filter.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/discovery/generator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/discovery/panel_builder.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/discovery/schemas.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/discovery/slate.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/discovery/traits.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/balanced_optimizer.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/dp_adapter.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/dp_v2.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/dp_v2_1.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/dp_v2_1_1.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/lm/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/lm/adapter.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/lm/diagnostics.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/lm/inference.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/lm/models/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/lm/models/mbart_codon.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/lm/tokenizer/control_tokenizer.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/codon_table_builder.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/construct_builder.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/exporter.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/optimizer.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/pipeline.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/rules/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/rules/domesticator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/rules/reverse_translator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/rules/rule_engine.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/scoring.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/scoring_ml.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/profile/validator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/registry.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/automaton.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/compiler.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/data/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/data/corpus_builder.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/data/tokenizer.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/decoder.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/interfaces.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/model/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/model/arch.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/model/baselines.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/sllm/model/export_onnx.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/engines/targeted_remediator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/evaluation/evaluator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/evaluation/evidence_logger.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/evaluation/metrics.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/evaluation/models.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/io/fasta.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/io/validation.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/learning/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/learning/ranker.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/profiles/profile_snapshot.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/protein_risk/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/protein_risk/annotate.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/protein_risk/kd_scale.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/protein_risk/risk_classifier.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/protein_risk/sp_predict.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/protein_risk/tm_predict.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/registry/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/registry/method_registry.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/registry/model_registry.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/registry/registry_loader.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/registry/versioning.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/review/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/review/evaluation_integrity.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/rules/models.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/schemas/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/schemas/design_package.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/schemas/design_package.schema.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/schemas/evidence.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/schemas/run_config.schema.json +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/scoring/quantization.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/sops/config.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/utils/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/utils/construct_id.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/utils/exceptions.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/utils/restriction_sites.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/utils/sequence_identity.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/utils/sequence_validator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/utils/validation.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/validation/__init__.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/validation/cli.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/validation/hub.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/validation/package_generator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/validation_registry.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge/validation_report.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge_cds.egg-info/dependency_links.txt +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge_cds.egg-info/entry_points.txt +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/src/factorforge_cds.egg-info/top_level.txt +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_baselines.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_benchmark_cli_output_guard.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_benchmark_codon_table_metadata.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_benchmark_harness.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_benchmark_scoring.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_cai.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_codon_table_manifest.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_cross_engine_evaluation.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_database.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_database_configuration.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_db_connector.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_design_package_schema.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_design_package_semantics.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_design_package_serialization.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_design_review.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_docs_consistency.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_dp_v2_1_1.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_dp_v2_1_initiation.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_fasta_io.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_gc_content.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_host_profile_metadata.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_iupac_validation.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_learning_workflow_boundary.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_legacy_cli.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_lm_alpha_contract.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_math_v2_dp_optimality.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_no_raw_sequence_logging.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_openbio_missing_metric_contract.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_optional_postgres.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_parameter_registry.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_partial_dp_rescue.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_prepare_lm_dataset.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_protein_risk.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_public_surface_scanner.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_quantized_scoring.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_registry_production_sync.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_restriction_sites.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_sequence_validator.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_sllm_biological_pipeline.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_slm_decoder.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_slm_interface.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_sop_profile.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_suite_capability_compat.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_translation_integrity.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_validation_contract_compat.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_validation_registry.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_validation_report.py +0 -0
- {factorforge_cds-3.5.4 → factorforge_cds-3.5.10}/tests/test_worked_example.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: factorforge-cds
|
|
3
|
-
Version: 3.5.
|
|
3
|
+
Version: 3.5.10
|
|
4
4
|
Summary: FactorForge - open-source CDS design and pre-synthesis sequence review by Eijex.
|
|
5
5
|
Author-email: Eijex <eijex.lab@gmail.com>
|
|
6
6
|
License-Expression: AGPL-3.0-only
|
|
@@ -22,6 +22,7 @@ Requires-Dist: pydantic>=2.0
|
|
|
22
22
|
Provides-Extra: postgres
|
|
23
23
|
Requires-Dist: psycopg2-binary>=2.9; extra == "postgres"
|
|
24
24
|
Requires-Dist: sqlalchemy>=2.0; extra == "postgres"
|
|
25
|
+
Requires-Dist: eijex-db-core==0.1.2; extra == "postgres"
|
|
25
26
|
Provides-Extra: dev
|
|
26
27
|
Requires-Dist: jsonschema>=4.0; extra == "dev"
|
|
27
28
|
Requires-Dist: pandas>=2.0; extra == "dev"
|
|
@@ -51,7 +52,7 @@ Dynamic: license-file
|
|
|
51
52
|
|
|
52
53
|
FactorForge performs deterministic CDS design with CAI/GC metrics, PolyA-signal screening, and Golden Gate/MoClo-aware checks. It is positioned as a pre-synthesis review harness: it helps teams generate reproducible CDS candidates, inspect assembly-relevant sequence constraints, and package design metadata before downstream synthesis, cloning, or experimental review. Primary support: *N. benthamiana* (agroinfiltration). Experimental host context: Tobacco BY-2 (`--host by2`).
|
|
53
54
|
|
|
54
|
-
FactorForge v3.5.
|
|
55
|
+
FactorForge v3.5.10 uses independently versioned engines:
|
|
55
56
|
|
|
56
57
|
| Generation | Engine | Version | Availability |
|
|
57
58
|
|---|---|---:|---|
|
|
@@ -89,6 +90,12 @@ Or use the **[web app](https://factorforge.eijex.com)** — no installation requ
|
|
|
89
90
|
|
|
90
91
|
## Repository Structure
|
|
91
92
|
|
|
93
|
+
Experimental codon-distribution and evidence-ledger modules in the development
|
|
94
|
+
checkout are scaffolds, not a validated laboratory policy or active learned
|
|
95
|
+
recommendation service. Computational checks do not establish synthesis readiness
|
|
96
|
+
or biological performance. Private inputs and collaborator packages must remain
|
|
97
|
+
outside public repositories and dashboards.
|
|
98
|
+
|
|
92
99
|
The supported deterministic engines are the profile engine, stable DP v2, and
|
|
93
100
|
the explicit DP v2.1.1 development candidate under:
|
|
94
101
|
|
|
@@ -127,7 +134,7 @@ FactorForge outputs are **in-silico only** and have not been experimentally vali
|
|
|
127
134
|
## Citing
|
|
128
135
|
|
|
129
136
|
```
|
|
130
|
-
FactorForge v3.5.
|
|
137
|
+
FactorForge v3.5.10 (2026). Open-source constraint-based CDS design and sequence review.
|
|
131
138
|
Eijex. https://github.com/eijex/factorforge-cds
|
|
132
139
|
```
|
|
133
140
|
|
|
@@ -143,7 +150,10 @@ FactorForge design, CLI, Python API, and file export do not require a database.
|
|
|
143
150
|
PostgreSQL persistence is an explicit Eijex integration path for retaining shared
|
|
144
151
|
campaign and candidate identities; it is not enabled by default.
|
|
145
152
|
|
|
146
|
-
|
|
153
|
+
The extra pins `eijex-db-core==0.1.2`. Supply its approved wheel bundle using
|
|
154
|
+
`pip install --find-links PATH_TO_BUNDLE "factorforge-cds[postgres]"`; the shared
|
|
155
|
+
package is not assumed to have been published on PyPI. Standalone installation
|
|
156
|
+
does not require it.
|
|
147
157
|
The deployment must also provide a compatible `eijex-db-core` package and set
|
|
148
158
|
`FACTORFORGE_DATABASE_URL`. FactorForge contains no default database credentials and
|
|
149
159
|
does not silently fall back to another backend. See [Persistence](docs/persistence.md).
|
|
@@ -176,3 +186,40 @@ GNU Affero General Public License v3.0 — see [LICENSE](LICENSE).
|
|
|
176
186
|
- **Eijex MCP** — [mcp.eijex.com](https://mcp.eijex.com)
|
|
177
187
|
- **Lab** — [www.eijex.com](https://www.eijex.com)
|
|
178
188
|
|
|
189
|
+
|
|
190
|
+
## Testing status
|
|
191
|
+
|
|
192
|
+
The web UI now labels watermark as disabled and distinguishes the sequence-only baseline from experimental structure-conditioned testing. The local approved workflow supports explicit 1DP-only or paired 3DP mode. 3DP availability is local research testing; it is not a new public optimization endpoint or a biological-performance claim. See [web UI boundaries](web/README.md).
|
|
193
|
+
|
|
194
|
+
## Direct 3DP browser testing (unreleased)
|
|
195
|
+
|
|
196
|
+
The FactorForge form supports explicit 1DP/3DP selection. 3DP uploads a PDB/mmCIF file, selects chain and one-based file model order, and requires reviewed one-based protein positions. The selected observed chain must exactly match the complete protein; partial or homologous structures require the retained local reviewed mapping workflow. Direct requests use `/api/optimize` with `design_mode=paired_3dp` and `structure_context={content,format,chain,model,positions,positions_reviewed,target_adaptiveness,penalty_weight}`.
|
|
197
|
+
|
|
198
|
+
Limits: 3–300 amino acids, structure content 250 KB, HTTP JSON body 1 MB. Both candidates use the configured codon reference, requested GC and canonical motif exclusions plus custom additions. Invalid inputs and infeasible candidates fail without a baseline fallback. Existing sequence-only settings and API requests remain supported. 3DP uses its explicit DP settings and does not apply Kozak, TpA, template or seed transformations. SOP checks are reported for both candidates; regeneration is manual and never silently changes mode. Watermark stays disabled.
|
|
199
|
+
|
|
200
|
+
This is stateless computational testing, with no external service dependencies or remote database recording. Those existing integrations remain available for later operational use. The structure file is not retained in the response or browser history; normal design results may be retained in local browser history. Submit only public-safe inputs. Structural conformity metrics are codon-weight proxies, not folding/expression measurements. These source changes are not deployed or a new package release.
|
|
201
|
+
|
|
202
|
+
## Current public policy UI (2026-10-12)
|
|
203
|
+
|
|
204
|
+
Custom SOP management is paused. The built-in FactorForge default policy is applied; upload/export/preset/editing controls are hidden and disabled. Their code and previous browser configuration remain available for later reactivation through ENABLE_CUSTOM_SOP. Saved custom SOPs are ignored without deletion. 1DP/3DP mode selection and explicit structure inputs remain active. This supersedes the earlier public SOP upload/export instructions. Public API contracts are retained.
|
|
205
|
+
|
|
206
|
+
### Scientific web observations
|
|
207
|
+
|
|
208
|
+
Direct 1DP/3DP browser testing reports sense-CDS RNA motif scans and available initiation-window MFE in results, downloadable reports and local history. Structure confidence observations can suggest positions for explicit acceptance and review; unsupported inputs do not invent positions. These checks do not establish expression, yield or folding benefits. The default policy remains active and custom SOP management remains paused. See `docs/experimental-structure.md` for the API and measurement boundary.
|
|
209
|
+
|
|
210
|
+
### Local reviewed structure file workflow
|
|
211
|
+
|
|
212
|
+
`factorforge nbfold annotate` and `factorforge nbfold optimize` provide the experimental local Phase A/B workflow for exact-match structure files and an explicit user review manifest. See [NB-Fold documentation](docs/experimental-structure.md).
|
|
213
|
+
|
|
214
|
+
Web structure handling now requires exact-match uploaded PDB/mmCIF coordinates and explicit reviewed positions. Automatic external folding, artificial structure/confidence fallback, and auto-approval are disabled. Structure metrics are synthetic reconstruction checks; independent public-structure/DSSP validation remains open.
|
|
215
|
+
|
|
216
|
+
### Experimental human-reference prototype
|
|
217
|
+
|
|
218
|
+
`factorforge.engines.stemcell_3dp.StemCell3DPOptimizer` is a local experimental interface using a generic human-frequency reference and provisional Eijex review criteria. It is not an MSC/iPSC-calibrated model or a laboratory-approved SOP. See [prototype boundaries](docs/model_cards/human-reference-prototype.md). It is not a new production web host.
|
|
219
|
+
# Local research candidate: DP v2.2
|
|
220
|
+
|
|
221
|
+
The opt-in Python `DPV220Optimizer` shares the v2.1.1 DP core and adds reviewed
|
|
222
|
+
position penalties. The experimental generic-human adapter uses this core;
|
|
223
|
+
existing plant web/NB-Fold routes retain their prior objective. See the
|
|
224
|
+
[model card](docs/model_cards/dp_v2_2.md) for usage, relaxation reporting and
|
|
225
|
+
engineering validation boundaries. This is not an expression or folding claim.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
FactorForge performs deterministic CDS design with CAI/GC metrics, PolyA-signal screening, and Golden Gate/MoClo-aware checks. It is positioned as a pre-synthesis review harness: it helps teams generate reproducible CDS candidates, inspect assembly-relevant sequence constraints, and package design metadata before downstream synthesis, cloning, or experimental review. Primary support: *N. benthamiana* (agroinfiltration). Experimental host context: Tobacco BY-2 (`--host by2`).
|
|
16
16
|
|
|
17
|
-
FactorForge v3.5.
|
|
17
|
+
FactorForge v3.5.10 uses independently versioned engines:
|
|
18
18
|
|
|
19
19
|
| Generation | Engine | Version | Availability |
|
|
20
20
|
|---|---|---:|---|
|
|
@@ -52,6 +52,12 @@ Or use the **[web app](https://factorforge.eijex.com)** — no installation requ
|
|
|
52
52
|
|
|
53
53
|
## Repository Structure
|
|
54
54
|
|
|
55
|
+
Experimental codon-distribution and evidence-ledger modules in the development
|
|
56
|
+
checkout are scaffolds, not a validated laboratory policy or active learned
|
|
57
|
+
recommendation service. Computational checks do not establish synthesis readiness
|
|
58
|
+
or biological performance. Private inputs and collaborator packages must remain
|
|
59
|
+
outside public repositories and dashboards.
|
|
60
|
+
|
|
55
61
|
The supported deterministic engines are the profile engine, stable DP v2, and
|
|
56
62
|
the explicit DP v2.1.1 development candidate under:
|
|
57
63
|
|
|
@@ -90,7 +96,7 @@ FactorForge outputs are **in-silico only** and have not been experimentally vali
|
|
|
90
96
|
## Citing
|
|
91
97
|
|
|
92
98
|
```
|
|
93
|
-
FactorForge v3.5.
|
|
99
|
+
FactorForge v3.5.10 (2026). Open-source constraint-based CDS design and sequence review.
|
|
94
100
|
Eijex. https://github.com/eijex/factorforge-cds
|
|
95
101
|
```
|
|
96
102
|
|
|
@@ -106,7 +112,10 @@ FactorForge design, CLI, Python API, and file export do not require a database.
|
|
|
106
112
|
PostgreSQL persistence is an explicit Eijex integration path for retaining shared
|
|
107
113
|
campaign and candidate identities; it is not enabled by default.
|
|
108
114
|
|
|
109
|
-
|
|
115
|
+
The extra pins `eijex-db-core==0.1.2`. Supply its approved wheel bundle using
|
|
116
|
+
`pip install --find-links PATH_TO_BUNDLE "factorforge-cds[postgres]"`; the shared
|
|
117
|
+
package is not assumed to have been published on PyPI. Standalone installation
|
|
118
|
+
does not require it.
|
|
110
119
|
The deployment must also provide a compatible `eijex-db-core` package and set
|
|
111
120
|
`FACTORFORGE_DATABASE_URL`. FactorForge contains no default database credentials and
|
|
112
121
|
does not silently fall back to another backend. See [Persistence](docs/persistence.md).
|
|
@@ -139,3 +148,40 @@ GNU Affero General Public License v3.0 — see [LICENSE](LICENSE).
|
|
|
139
148
|
- **Eijex MCP** — [mcp.eijex.com](https://mcp.eijex.com)
|
|
140
149
|
- **Lab** — [www.eijex.com](https://www.eijex.com)
|
|
141
150
|
|
|
151
|
+
|
|
152
|
+
## Testing status
|
|
153
|
+
|
|
154
|
+
The web UI now labels watermark as disabled and distinguishes the sequence-only baseline from experimental structure-conditioned testing. The local approved workflow supports explicit 1DP-only or paired 3DP mode. 3DP availability is local research testing; it is not a new public optimization endpoint or a biological-performance claim. See [web UI boundaries](web/README.md).
|
|
155
|
+
|
|
156
|
+
## Direct 3DP browser testing (unreleased)
|
|
157
|
+
|
|
158
|
+
The FactorForge form supports explicit 1DP/3DP selection. 3DP uploads a PDB/mmCIF file, selects chain and one-based file model order, and requires reviewed one-based protein positions. The selected observed chain must exactly match the complete protein; partial or homologous structures require the retained local reviewed mapping workflow. Direct requests use `/api/optimize` with `design_mode=paired_3dp` and `structure_context={content,format,chain,model,positions,positions_reviewed,target_adaptiveness,penalty_weight}`.
|
|
159
|
+
|
|
160
|
+
Limits: 3–300 amino acids, structure content 250 KB, HTTP JSON body 1 MB. Both candidates use the configured codon reference, requested GC and canonical motif exclusions plus custom additions. Invalid inputs and infeasible candidates fail without a baseline fallback. Existing sequence-only settings and API requests remain supported. 3DP uses its explicit DP settings and does not apply Kozak, TpA, template or seed transformations. SOP checks are reported for both candidates; regeneration is manual and never silently changes mode. Watermark stays disabled.
|
|
161
|
+
|
|
162
|
+
This is stateless computational testing, with no external service dependencies or remote database recording. Those existing integrations remain available for later operational use. The structure file is not retained in the response or browser history; normal design results may be retained in local browser history. Submit only public-safe inputs. Structural conformity metrics are codon-weight proxies, not folding/expression measurements. These source changes are not deployed or a new package release.
|
|
163
|
+
|
|
164
|
+
## Current public policy UI (2026-10-12)
|
|
165
|
+
|
|
166
|
+
Custom SOP management is paused. The built-in FactorForge default policy is applied; upload/export/preset/editing controls are hidden and disabled. Their code and previous browser configuration remain available for later reactivation through ENABLE_CUSTOM_SOP. Saved custom SOPs are ignored without deletion. 1DP/3DP mode selection and explicit structure inputs remain active. This supersedes the earlier public SOP upload/export instructions. Public API contracts are retained.
|
|
167
|
+
|
|
168
|
+
### Scientific web observations
|
|
169
|
+
|
|
170
|
+
Direct 1DP/3DP browser testing reports sense-CDS RNA motif scans and available initiation-window MFE in results, downloadable reports and local history. Structure confidence observations can suggest positions for explicit acceptance and review; unsupported inputs do not invent positions. These checks do not establish expression, yield or folding benefits. The default policy remains active and custom SOP management remains paused. See `docs/experimental-structure.md` for the API and measurement boundary.
|
|
171
|
+
|
|
172
|
+
### Local reviewed structure file workflow
|
|
173
|
+
|
|
174
|
+
`factorforge nbfold annotate` and `factorforge nbfold optimize` provide the experimental local Phase A/B workflow for exact-match structure files and an explicit user review manifest. See [NB-Fold documentation](docs/experimental-structure.md).
|
|
175
|
+
|
|
176
|
+
Web structure handling now requires exact-match uploaded PDB/mmCIF coordinates and explicit reviewed positions. Automatic external folding, artificial structure/confidence fallback, and auto-approval are disabled. Structure metrics are synthetic reconstruction checks; independent public-structure/DSSP validation remains open.
|
|
177
|
+
|
|
178
|
+
### Experimental human-reference prototype
|
|
179
|
+
|
|
180
|
+
`factorforge.engines.stemcell_3dp.StemCell3DPOptimizer` is a local experimental interface using a generic human-frequency reference and provisional Eijex review criteria. It is not an MSC/iPSC-calibrated model or a laboratory-approved SOP. See [prototype boundaries](docs/model_cards/human-reference-prototype.md). It is not a new production web host.
|
|
181
|
+
# Local research candidate: DP v2.2
|
|
182
|
+
|
|
183
|
+
The opt-in Python `DPV220Optimizer` shares the v2.1.1 DP core and adds reviewed
|
|
184
|
+
position penalties. The experimental generic-human adapter uses this core;
|
|
185
|
+
existing plant web/NB-Fold routes retain their prior objective. See the
|
|
186
|
+
[model card](docs/model_cards/dp_v2_2.md) for usage, relaxation reporting and
|
|
187
|
+
engineering validation boundaries. This is not an expression or folding claim.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "factorforge-cds"
|
|
7
|
-
version = "3.5.
|
|
7
|
+
version = "3.5.10"
|
|
8
8
|
description = "FactorForge - open-source CDS design and pre-synthesis sequence review by Eijex."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "AGPL-3.0-only"
|
|
@@ -30,6 +30,7 @@ dependencies = [
|
|
|
30
30
|
postgres = [
|
|
31
31
|
"psycopg2-binary>=2.9",
|
|
32
32
|
"sqlalchemy>=2.0",
|
|
33
|
+
"eijex-db-core==0.1.2",
|
|
33
34
|
]
|
|
34
35
|
dev = [
|
|
35
36
|
"jsonschema>=4.0",
|
|
@@ -7,18 +7,18 @@ Usage:
|
|
|
7
7
|
factorforge list-engines
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from pathlib import Path
|
|
11
10
|
import hashlib
|
|
12
11
|
import json
|
|
13
12
|
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
14
|
|
|
15
15
|
import click
|
|
16
16
|
|
|
17
17
|
from factorforge import __version__
|
|
18
18
|
from factorforge.analysis.feasibility import DEFAULT_CAI_TARGET
|
|
19
19
|
from factorforge.engines import register_builtin_engines
|
|
20
|
-
from factorforge.engines.registry import EngineRegistry
|
|
21
20
|
from factorforge.engines.profile.utils import parse_fasta_records
|
|
21
|
+
from factorforge.engines.registry import EngineRegistry
|
|
22
22
|
|
|
23
23
|
register_builtin_engines()
|
|
24
24
|
|
|
@@ -710,7 +710,10 @@ def evaluate_model(model_id, train_snapshot, eval_snapshot, input_sequence):
|
|
|
710
710
|
mock_train_seqs = ["ATGGCTAAATGGTAA", "ATGCGTACGTGCTAG"]
|
|
711
711
|
mock_eval_seqs = ["ATGCCCGGGAAATTT"]
|
|
712
712
|
|
|
713
|
-
from factorforge.review.evaluation_integrity import
|
|
713
|
+
from factorforge.review.evaluation_integrity import (
|
|
714
|
+
DatasetLeakageAuditor,
|
|
715
|
+
GenerationMemorizationAuditor,
|
|
716
|
+
)
|
|
714
717
|
|
|
715
718
|
# 2. Dataset Leakage Audit
|
|
716
719
|
ds_auditor = DatasetLeakageAuditor(mock_train_seqs, mock_eval_seqs)
|
|
@@ -914,17 +917,22 @@ def audit(sequence_fasta, reference_codon_profile, exclude_region, output):
|
|
|
914
917
|
Run generic sequence audit to report codon usage, CAI, GC, and AA distribution.
|
|
915
918
|
"""
|
|
916
919
|
_configure_stdio()
|
|
917
|
-
from factorforge.engines.profile.utils import parse_fasta_records
|
|
918
|
-
from factorforge.analysis.metrics import (
|
|
919
|
-
load_codon_usage_table, calculate_cai, calculate_gc, codon_usage_profile,
|
|
920
|
-
translate_dna, calculate_synonymous_entropy, calculate_max_synonymous_share,
|
|
921
|
-
calculate_reference_distance
|
|
922
|
-
)
|
|
923
|
-
from factorforge.engines.balanced_optimizer import STANDARD_GENETIC_CODE
|
|
924
|
-
|
|
925
920
|
import hashlib
|
|
926
921
|
import json
|
|
927
922
|
from pathlib import Path
|
|
923
|
+
|
|
924
|
+
from factorforge.analysis.metrics import (
|
|
925
|
+
calculate_cai,
|
|
926
|
+
calculate_gc,
|
|
927
|
+
calculate_max_synonymous_share,
|
|
928
|
+
calculate_reference_distance,
|
|
929
|
+
calculate_synonymous_entropy,
|
|
930
|
+
codon_usage_profile,
|
|
931
|
+
load_codon_usage_table,
|
|
932
|
+
translate_dna,
|
|
933
|
+
)
|
|
934
|
+
from factorforge.engines.balanced_optimizer import STANDARD_GENETIC_CODE
|
|
935
|
+
from factorforge.engines.profile.utils import parse_fasta_records
|
|
928
936
|
|
|
929
937
|
fasta_records = parse_fasta_records(Path(sequence_fasta).read_text(encoding='utf-8'))
|
|
930
938
|
if not fasta_records:
|
|
@@ -988,6 +996,13 @@ def audit(sequence_fasta, reference_codon_profile, exclude_region, output):
|
|
|
988
996
|
else:
|
|
989
997
|
click.echo(json.dumps(results, indent=2))
|
|
990
998
|
|
|
999
|
+
|
|
1000
|
+
from factorforge.nbfold.cli import nbfold_cli
|
|
1001
|
+
|
|
1002
|
+
cli.add_command(nbfold_cli)
|
|
1003
|
+
|
|
1004
|
+
|
|
991
1005
|
if __name__ == "__main__":
|
|
992
1006
|
cli()
|
|
993
1007
|
|
|
1008
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""FactorForge Closed-Loop Codon Evidence Foundation Package."""
|
|
2
|
+
|
|
3
|
+
from factorforge.closed_loop.contracts import (
|
|
4
|
+
ConstructRecord,
|
|
5
|
+
ConstructSetRecord,
|
|
6
|
+
ExperimentRecord,
|
|
7
|
+
MeasurementRecord,
|
|
8
|
+
PolicyRecord,
|
|
9
|
+
)
|
|
10
|
+
from factorforge.closed_loop.ledger import EvidenceLedger
|
|
11
|
+
from factorforge.closed_loop.readiness import (
|
|
12
|
+
ModelRecommendationGate,
|
|
13
|
+
ReadinessEvaluator,
|
|
14
|
+
ReadinessStatus,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"ConstructRecord",
|
|
19
|
+
"ConstructSetRecord",
|
|
20
|
+
"EvidenceLedger",
|
|
21
|
+
"ExperimentRecord",
|
|
22
|
+
"MeasurementRecord",
|
|
23
|
+
"ModelRecommendationGate",
|
|
24
|
+
"PolicyRecord",
|
|
25
|
+
"ReadinessEvaluator",
|
|
26
|
+
"ReadinessStatus",
|
|
27
|
+
]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Data contracts and schemas for Closed-Loop DBTL Lineage."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import math
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field, model_validator
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PolicyRecord(BaseModel):
|
|
14
|
+
"""Metadata record of an approved codon distribution policy."""
|
|
15
|
+
|
|
16
|
+
policy_id: str
|
|
17
|
+
canonical_sha256: str
|
|
18
|
+
target_distribution: dict[str, dict[str, float]]
|
|
19
|
+
region_definition: str = "mature_chain"
|
|
20
|
+
feedback_source_id: str = "unspecified"
|
|
21
|
+
created_at: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ConstructRecord(BaseModel):
|
|
25
|
+
"""Immutable sequence identity record for a designed construct."""
|
|
26
|
+
|
|
27
|
+
construct_id: str
|
|
28
|
+
cds_sha256: str
|
|
29
|
+
protein_sha256: str
|
|
30
|
+
chain: str # "LC" or "HC"
|
|
31
|
+
watermark_pair_id: str
|
|
32
|
+
watermark_applied: bool
|
|
33
|
+
achieved_distribution: dict[str, dict[str, float]]
|
|
34
|
+
policy_id: str
|
|
35
|
+
engine_version: str = "FactorForge-v3.3.0"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ConstructSetRecord(BaseModel):
|
|
39
|
+
"""Co-expressed pair of Light Chain and Heavy Chain constructs."""
|
|
40
|
+
|
|
41
|
+
construct_set_id: str
|
|
42
|
+
light_construct_id: str
|
|
43
|
+
heavy_construct_id: str
|
|
44
|
+
watermark_applied: bool
|
|
45
|
+
description: str = ""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ExperimentRecord(BaseModel):
|
|
49
|
+
"""Wet-lab or benchmark experiment metadata."""
|
|
50
|
+
|
|
51
|
+
experiment_id: str
|
|
52
|
+
construct_set_id: str
|
|
53
|
+
host: str = "Nicotiana benthamiana"
|
|
54
|
+
expression_system: str = "Agroinfiltration (Transient Leaf Expression)"
|
|
55
|
+
batch_id: str
|
|
56
|
+
experiment_date: str
|
|
57
|
+
protocol_version: str = "unspecified"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class MeasurementRecord(BaseModel):
|
|
61
|
+
"""Single empirical or synthetic readout record."""
|
|
62
|
+
|
|
63
|
+
measurement_id: str
|
|
64
|
+
experiment_id: str
|
|
65
|
+
construct_set_id: str
|
|
66
|
+
biological_replicate_id: str
|
|
67
|
+
technical_replicate_id: str
|
|
68
|
+
measurement_type: str # "protein_yield", "mrna_abundance", "functional_binding"
|
|
69
|
+
value: float | None
|
|
70
|
+
unit: str # "mg/L", "relative_fold", "%"
|
|
71
|
+
qc_status: str = "PASS" # "PASS", "FAIL", "FLAGGED"
|
|
72
|
+
is_synthetic: bool = (
|
|
73
|
+
False # CRITICAL: If True, flagged as test_only and excluded from real training
|
|
74
|
+
)
|
|
75
|
+
tag: str = "empirical"
|
|
76
|
+
notes: str = ""
|
|
77
|
+
recorded_at: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
|
|
78
|
+
|
|
79
|
+
@model_validator(mode="after")
|
|
80
|
+
def validate_measurement(self):
|
|
81
|
+
if self.value is not None and not math.isfinite(self.value):
|
|
82
|
+
raise ValueError("Measurement value must be finite or null")
|
|
83
|
+
if self.is_synthetic:
|
|
84
|
+
self.tag = "test_only"
|
|
85
|
+
if self.qc_status not in {"PASS", "FAIL", "FLAGGED"}:
|
|
86
|
+
raise ValueError("Unknown QC status")
|
|
87
|
+
return self
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def record_hash(self) -> str:
|
|
91
|
+
payload = {
|
|
92
|
+
"measurement_id": self.measurement_id,
|
|
93
|
+
"experiment_id": self.experiment_id,
|
|
94
|
+
"construct_set_id": self.construct_set_id,
|
|
95
|
+
"biological_replicate_id": self.biological_replicate_id,
|
|
96
|
+
"technical_replicate_id": self.technical_replicate_id,
|
|
97
|
+
"measurement_type": self.measurement_type,
|
|
98
|
+
"value": self.value,
|
|
99
|
+
"unit": self.unit,
|
|
100
|
+
"is_synthetic": self.is_synthetic,
|
|
101
|
+
}
|
|
102
|
+
encoded = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
103
|
+
return hashlib.sha256(encoded).hexdigest()
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""Append-only Evidence Ledger for ValidationHub / FactorForge closed loop."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from factorforge.closed_loop.contracts import (
|
|
12
|
+
ConstructRecord,
|
|
13
|
+
ConstructSetRecord,
|
|
14
|
+
ExperimentRecord,
|
|
15
|
+
MeasurementRecord,
|
|
16
|
+
PolicyRecord,
|
|
17
|
+
)
|
|
18
|
+
from factorforge.closed_loop.readiness import (
|
|
19
|
+
ReadinessEvaluator,
|
|
20
|
+
ReadinessReport,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class EvidenceLedger:
|
|
25
|
+
"""Local file-based append-only registry storing closed-loop DBTL evidence."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, ledger_dir: Path | None = None):
|
|
28
|
+
if ledger_dir is None:
|
|
29
|
+
ledger_dir = Path(__file__).resolve().parents[3] / "data" / "evidence_ledger"
|
|
30
|
+
self.ledger_dir = Path(ledger_dir)
|
|
31
|
+
self.ledger_dir.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
|
|
33
|
+
self.policies_file = self.ledger_dir / "policies.jsonl"
|
|
34
|
+
self.constructs_file = self.ledger_dir / "constructs.jsonl"
|
|
35
|
+
self.construct_sets_file = self.ledger_dir / "construct_sets.jsonl"
|
|
36
|
+
self.experiments_file = self.ledger_dir / "experiments.jsonl"
|
|
37
|
+
self.measurements_file = self.ledger_dir / "measurements.jsonl"
|
|
38
|
+
|
|
39
|
+
def _append_jsonl(self, file_path: Path, record: dict):
|
|
40
|
+
identity_field = {
|
|
41
|
+
"policies.jsonl": "policy_id",
|
|
42
|
+
"constructs.jsonl": "construct_id",
|
|
43
|
+
"construct_sets.jsonl": "construct_set_id",
|
|
44
|
+
"experiments.jsonl": "experiment_id",
|
|
45
|
+
"measurements.jsonl": "measurement_id",
|
|
46
|
+
}[file_path.name]
|
|
47
|
+
for existing in self._read_jsonl(file_path):
|
|
48
|
+
if existing.get(identity_field) == record[identity_field]:
|
|
49
|
+
if existing == record:
|
|
50
|
+
return # Same record is idempotent, not another observation.
|
|
51
|
+
raise ValueError(
|
|
52
|
+
"Conflicting identity; preserve the original and use an explicit new revision"
|
|
53
|
+
)
|
|
54
|
+
with open(file_path, "a", encoding="utf-8") as f:
|
|
55
|
+
f.write(json.dumps(record, ensure_ascii=False) + "\n")
|
|
56
|
+
|
|
57
|
+
def _read_jsonl(self, file_path: Path) -> list[dict]:
|
|
58
|
+
if not file_path.exists():
|
|
59
|
+
return []
|
|
60
|
+
records = []
|
|
61
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
62
|
+
for line in f:
|
|
63
|
+
line = line.strip()
|
|
64
|
+
if line:
|
|
65
|
+
records.append(json.loads(line))
|
|
66
|
+
return records
|
|
67
|
+
|
|
68
|
+
def register_policy(self, record: PolicyRecord):
|
|
69
|
+
self._append_jsonl(self.policies_file, record.model_dump())
|
|
70
|
+
|
|
71
|
+
def register_construct(self, record: ConstructRecord):
|
|
72
|
+
self._append_jsonl(self.constructs_file, record.model_dump())
|
|
73
|
+
|
|
74
|
+
def register_construct_set(self, record: ConstructSetRecord):
|
|
75
|
+
self._append_jsonl(self.construct_sets_file, record.model_dump())
|
|
76
|
+
|
|
77
|
+
def register_experiment(self, record: ExperimentRecord):
|
|
78
|
+
self._append_jsonl(self.experiments_file, record.model_dump())
|
|
79
|
+
|
|
80
|
+
def record_measurement(self, record: MeasurementRecord):
|
|
81
|
+
self._append_jsonl(self.measurements_file, record.model_dump())
|
|
82
|
+
|
|
83
|
+
def list_measurements(self, include_synthetic: bool = True) -> list[MeasurementRecord]:
|
|
84
|
+
raw = self._read_jsonl(self.measurements_file)
|
|
85
|
+
measurements = [MeasurementRecord(**r) for r in raw]
|
|
86
|
+
if not include_synthetic:
|
|
87
|
+
return [m for m in measurements if not m.is_synthetic]
|
|
88
|
+
return measurements
|
|
89
|
+
|
|
90
|
+
def get_readiness(self) -> ReadinessReport:
|
|
91
|
+
all_measurements = self.list_measurements(include_synthetic=True)
|
|
92
|
+
raw_experiments = self._read_jsonl(self.experiments_file)
|
|
93
|
+
experiments = {e["experiment_id"]: ExperimentRecord(**e) for e in raw_experiments}
|
|
94
|
+
all_measurements = [
|
|
95
|
+
m
|
|
96
|
+
if m.is_synthetic or self.verify_lineage(m.measurement_id)["lineage_intact"]
|
|
97
|
+
else m.model_copy(update={"qc_status": "FLAGGED"})
|
|
98
|
+
for m in all_measurements
|
|
99
|
+
]
|
|
100
|
+
return ReadinessEvaluator.evaluate(all_measurements, experiments=experiments)
|
|
101
|
+
|
|
102
|
+
def verify_lineage(self, measurement_id: str) -> dict[str, object]:
|
|
103
|
+
"""Verify unbroken provenance & referential consistency:
|
|
104
|
+
Measurement -> Experiment -> ConstructSet -> Construct -> Policy."""
|
|
105
|
+
measurements = {m["measurement_id"]: m for m in self._read_jsonl(self.measurements_file)}
|
|
106
|
+
experiments = {e["experiment_id"]: e for e in self._read_jsonl(self.experiments_file)}
|
|
107
|
+
construct_sets = {
|
|
108
|
+
s["construct_set_id"]: s for s in self._read_jsonl(self.construct_sets_file)
|
|
109
|
+
}
|
|
110
|
+
constructs = {c["construct_id"]: c for c in self._read_jsonl(self.constructs_file)}
|
|
111
|
+
policies = {p["policy_id"]: p for p in self._read_jsonl(self.policies_file)}
|
|
112
|
+
|
|
113
|
+
if measurement_id not in measurements:
|
|
114
|
+
raise KeyError(f"Measurement '{measurement_id}' not found in ledger")
|
|
115
|
+
|
|
116
|
+
m = measurements[measurement_id]
|
|
117
|
+
exp_id = m.get("experiment_id")
|
|
118
|
+
set_id = m.get("construct_set_id")
|
|
119
|
+
|
|
120
|
+
exp = experiments.get(exp_id)
|
|
121
|
+
cset = construct_sets.get(set_id)
|
|
122
|
+
|
|
123
|
+
lc_construct = constructs.get(cset.get("light_construct_id")) if cset else None
|
|
124
|
+
hc_construct = constructs.get(cset.get("heavy_construct_id")) if cset else None
|
|
125
|
+
|
|
126
|
+
policy_id = lc_construct.get("policy_id") if lc_construct else None
|
|
127
|
+
pol = policies.get(policy_id) if policy_id else None
|
|
128
|
+
|
|
129
|
+
violations: list[str] = []
|
|
130
|
+
|
|
131
|
+
# 1. Existence validations
|
|
132
|
+
if exp is None:
|
|
133
|
+
violations.append(f"Referenced experiment '{exp_id}' does not exist in ledger.")
|
|
134
|
+
if cset is None:
|
|
135
|
+
violations.append(f"Referenced construct_set '{set_id}' does not exist in ledger.")
|
|
136
|
+
if lc_construct is None:
|
|
137
|
+
violations.append("Referenced light chain construct does not exist in ledger.")
|
|
138
|
+
if hc_construct is None:
|
|
139
|
+
violations.append("Referenced heavy chain construct does not exist in ledger.")
|
|
140
|
+
if pol is None:
|
|
141
|
+
violations.append(f"Referenced policy '{policy_id}' does not exist in ledger.")
|
|
142
|
+
|
|
143
|
+
# 2. Referential integrity validations
|
|
144
|
+
if exp and cset and exp.get("construct_set_id") != set_id:
|
|
145
|
+
violations.append(
|
|
146
|
+
f"Referential mismatch: Measurement points to construct_set '{set_id}' "
|
|
147
|
+
f"but Experiment '{exp_id}' points to '{exp.get('construct_set_id')}'"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if lc_construct and hc_construct:
|
|
151
|
+
lc_pol = lc_construct.get("policy_id")
|
|
152
|
+
hc_pol = hc_construct.get("policy_id")
|
|
153
|
+
if lc_pol != hc_pol:
|
|
154
|
+
violations.append(
|
|
155
|
+
f"Policy mismatch between chains: LC is linked to '{lc_pol}' but HC is linked to '{hc_pol}'"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
lineage_intact = len(violations) == 0
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
"measurement_id": measurement_id,
|
|
162
|
+
"lineage_intact": lineage_intact,
|
|
163
|
+
"violations": violations,
|
|
164
|
+
"is_synthetic": m.get("is_synthetic", False),
|
|
165
|
+
"experiment": exp,
|
|
166
|
+
"construct_set": cset,
|
|
167
|
+
"light_construct": lc_construct,
|
|
168
|
+
"heavy_construct": hc_construct,
|
|
169
|
+
"policy": pol,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
def sync_to_gcs(
|
|
173
|
+
self,
|
|
174
|
+
bucket_name: str,
|
|
175
|
+
destination_folder: str = "validationhub/ledger_v0",
|
|
176
|
+
gcp_key_path: str | None = None,
|
|
177
|
+
) -> dict[str, str]:
|
|
178
|
+
"""Sync append-only ledger snapshot with atomic manifest to Google Cloud Storage."""
|
|
179
|
+
from google.cloud import storage
|
|
180
|
+
|
|
181
|
+
if gcp_key_path:
|
|
182
|
+
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = gcp_key_path
|
|
183
|
+
|
|
184
|
+
client = storage.Client()
|
|
185
|
+
bucket = client.bucket(bucket_name)
|
|
186
|
+
|
|
187
|
+
# Generate atomic ledger manifest with SHA256 checksums
|
|
188
|
+
manifest_files = {}
|
|
189
|
+
target_files = [
|
|
190
|
+
self.policies_file,
|
|
191
|
+
self.constructs_file,
|
|
192
|
+
self.construct_sets_file,
|
|
193
|
+
self.experiments_file,
|
|
194
|
+
self.measurements_file,
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
for file_path in target_files:
|
|
198
|
+
if file_path.exists():
|
|
199
|
+
content = file_path.read_bytes()
|
|
200
|
+
manifest_files[file_path.name] = {
|
|
201
|
+
"sha256": hashlib.sha256(content).hexdigest(),
|
|
202
|
+
"size_bytes": len(content),
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
manifest = {
|
|
206
|
+
"manifest_version": "0.2",
|
|
207
|
+
"snapshot_timestamp": datetime.now(timezone.utc).isoformat(),
|
|
208
|
+
"factorforge_version": "3.5.4",
|
|
209
|
+
"files": manifest_files,
|
|
210
|
+
}
|
|
211
|
+
manifest_file = self.ledger_dir / "ledger_manifest.json"
|
|
212
|
+
manifest_file.write_text(json.dumps(manifest, indent=2), encoding="utf-8")
|
|
213
|
+
|
|
214
|
+
uploaded = {}
|
|
215
|
+
for file_path in target_files + [manifest_file]:
|
|
216
|
+
if file_path.exists():
|
|
217
|
+
blob_name = f"{destination_folder}/{file_path.name}"
|
|
218
|
+
blob = bucket.blob(blob_name)
|
|
219
|
+
blob.upload_from_filename(str(file_path))
|
|
220
|
+
uploaded[file_path.name] = f"gs://{bucket_name}/{blob_name}"
|
|
221
|
+
|
|
222
|
+
return uploaded
|