factorforge-cds 3.2.8__tar.gz → 3.3.0__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.2.8/src/factorforge_cds.egg-info → factorforge_cds-3.3.0}/PKG-INFO +4 -4
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/README.md +2 -2
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/pyproject.toml +2 -2
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/__init__.py +1 -1
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/analysis/feasibility.py +8 -11
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/analysis/metrics.py +1 -1
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/cli/main.py +2 -2
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/__init__.py +1 -1
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/__init__.py +1 -1
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/optimizer.py +18 -2
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/reverse_translator.py +7 -5
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/rule_engine.py +5 -5
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/scoring.py +18 -27
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/utils.py +7 -7
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0/src/factorforge_cds.egg-info}/PKG-INFO +4 -4
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_benchmark_codon_table_metadata.py +3 -6
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_registry_production_sync.py +13 -16
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_worked_example.py +2 -3
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/LICENSE +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/setup.cfg +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/__main__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/analysis/__init__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/cli/__init__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/cli/legacy_cli.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/__init__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/exporter.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/optimizer.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/validator.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/data/nbenthamiana_codons.json +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/data/nbenthamiana_golden_set.json +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/data/ntabacum_codons.json +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/data/templates/high_expression.json +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/data/templates/standard_expression.json +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/data/wolffia_globosa_codons.json +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/database.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/codon_table_builder.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/construct_builder.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/exporter.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/pipeline.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/__init__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/domesticator.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/scoring_ml.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/validator.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/registry.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/io/__init__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/io/fasta.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/io/validation.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/__init__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/annotate.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/kd_scale.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/risk_classifier.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/sp_predict.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/tm_predict.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/registry/__init__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/registry/registry_loader.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/schemas/__init__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/schemas/design_package.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/schemas/design_package.schema.json +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/utils/__init__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/utils/construct_id.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/utils/exceptions.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/utils/restriction_sites.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/utils/sequence_validator.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/utils/validation.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/validation/__init__.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/validation/cli.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/validation/package_generator.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/validation_registry.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/validation_report.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/SOURCES.txt +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/dependency_links.txt +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/entry_points.txt +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/requires.txt +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/top_level.txt +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_baselines.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_benchmark_regression.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_benchmark_scoring.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_benchmark_smoke.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_cai.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_codon_table_manifest.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_database.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_design_package_schema.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_design_package_semantics.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_design_package_serialization.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_docs_consistency.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_fasta_io.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_gc_content.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_host_profile_metadata.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_iupac_validation.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_legacy_cli.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_no_raw_sequence_logging.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_openbio_missing_metric_contract.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_parameter_registry.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_protein_risk.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_restriction_sites.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_sequence_validator.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_translation_integrity.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_validation_contract_compat.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_validation_registry.py +0 -0
- {factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_validation_report.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: factorforge-cds
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: FactorForge - open-source CDS design and pre-synthesis sequence review
|
|
3
|
+
Version: 3.3.0
|
|
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
|
|
7
7
|
Project-URL: Homepage, https://factorforge.eijex.com
|
|
@@ -33,7 +33,7 @@ Dynamic: license-file
|
|
|
33
33
|
|
|
34
34
|
# FactorForge
|
|
35
35
|
|
|
36
|
-
**Open-source constraint-based CDS design and pre-synthesis sequence review
|
|
36
|
+
**Open-source constraint-based CDS design and pre-synthesis sequence review for plant CDS workflows, with primary support for *Nicotiana benthamiana* (Tobacco BY-2: experimental).**
|
|
37
37
|
|
|
38
38
|
[](LICENSE)
|
|
39
39
|
[](https://www.python.org/)
|
|
@@ -93,7 +93,7 @@ FactorForge outputs are **in-silico only** and have not been experimentally vali
|
|
|
93
93
|
## Citing
|
|
94
94
|
|
|
95
95
|
```
|
|
96
|
-
FactorForge v3.
|
|
96
|
+
FactorForge v3.3.0 (2026). Open-source constraint-based CDS design and sequence review.
|
|
97
97
|
Eijex. https://github.com/eijex/factorforge-cds
|
|
98
98
|
```
|
|
99
99
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# FactorForge
|
|
2
2
|
|
|
3
|
-
**Open-source constraint-based CDS design and pre-synthesis sequence review
|
|
3
|
+
**Open-source constraint-based CDS design and pre-synthesis sequence review for plant CDS workflows, with primary support for *Nicotiana benthamiana* (Tobacco BY-2: experimental).**
|
|
4
4
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.python.org/)
|
|
@@ -60,7 +60,7 @@ FactorForge outputs are **in-silico only** and have not been experimentally vali
|
|
|
60
60
|
## Citing
|
|
61
61
|
|
|
62
62
|
```
|
|
63
|
-
FactorForge v3.
|
|
63
|
+
FactorForge v3.3.0 (2026). Open-source constraint-based CDS design and sequence review.
|
|
64
64
|
Eijex. https://github.com/eijex/factorforge-cds
|
|
65
65
|
```
|
|
66
66
|
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "factorforge-cds"
|
|
7
|
-
version = "3.
|
|
8
|
-
description = "FactorForge - open-source CDS design and pre-synthesis sequence review
|
|
7
|
+
version = "3.3.0"
|
|
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"
|
|
11
11
|
license-files = ["LICENSE"]
|
|
@@ -14,19 +14,16 @@ from factorforge.analysis.metrics import (
|
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
# Defaults
|
|
18
|
-
#
|
|
19
|
-
#
|
|
17
|
+
# Defaults for the current N. benthamiana software-default codon reference.
|
|
18
|
+
# DEFAULT_CAI_TARGET=0.82 remains a soft in-silico target aligned with
|
|
19
|
+
# industry practice (>0.8). DEFAULT_GC_LOW/HIGH track the NbeV1.1
|
|
20
|
+
# high-confidence CDS-derived native composition band. These are not wet-lab
|
|
21
|
+
# validation, expression/yield prediction, or biological-superiority claims.
|
|
20
22
|
# Exported as named constants so tests/test_registry_production_sync.py can
|
|
21
23
|
# strictly compare them against the registry (single source of truth).
|
|
22
|
-
#
|
|
23
|
-
# DEFAULT_GC_LOW/HIGH provisionally reverted from the Job 168/v3.3.0
|
|
24
|
-
# native-genome-composition anchor (40-47%, released as part of v3.2.7) back
|
|
25
|
-
# to the legacy engine-output-calibrated band, pending an MFE re-sensitivity +
|
|
26
|
-
# 2x2 factorial recheck. See scoring.py's GC_OPT_MIN/MAX comment.
|
|
27
24
|
DEFAULT_CAI_TARGET: float = 0.82
|
|
28
|
-
DEFAULT_GC_LOW: float =
|
|
29
|
-
DEFAULT_GC_HIGH: float =
|
|
25
|
+
DEFAULT_GC_LOW: float = 40.0
|
|
26
|
+
DEFAULT_GC_HIGH: float = 47.0
|
|
30
27
|
|
|
31
28
|
|
|
32
29
|
AA_TO_CODONS: dict[str, list[str]] = {}
|
|
@@ -121,7 +118,7 @@ def analyze_feasibility(
|
|
|
121
118
|
if not protein:
|
|
122
119
|
raise ValueError("protein_sequence must not be empty")
|
|
123
120
|
|
|
124
|
-
ranges = gc_ranges or [(
|
|
121
|
+
ranges = gc_ranges or [(40.0, 47.0), (40.0, 55.0), (40.0, 65.0)]
|
|
125
122
|
normalized_ranges = [
|
|
126
123
|
(_normalize_gc_bound(low), _normalize_gc_bound(high)) for low, high in ranges
|
|
127
124
|
]
|
|
@@ -98,7 +98,7 @@ class CodonUsageTable:
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
def _default_codon_table_path() -> Path:
|
|
101
|
-
#
|
|
101
|
+
# v3.3.0 reference-policy update: production default switched from the
|
|
102
102
|
# legacy Kazusa/SGN-derived table to the NbeV1.1 LAB-strain high-confidence
|
|
103
103
|
# derived table. See data/reference/active_codon_reference.json.
|
|
104
104
|
return get_data_path() / "profiles" / "nbev11_cds_hc_derived_codons.json"
|
|
@@ -164,8 +164,8 @@ def list_engines():
|
|
|
164
164
|
type=click.Choice(["feasibility_best"], case_sensitive=False),
|
|
165
165
|
help="DP objective",
|
|
166
166
|
)
|
|
167
|
-
@click.option("--gc-min", type=float, default=
|
|
168
|
-
@click.option("--gc-max", type=float, default=
|
|
167
|
+
@click.option("--gc-min", type=float, default=40.0, help="Minimum target GC percentage")
|
|
168
|
+
@click.option("--gc-max", type=float, default=47.0, help="Maximum target GC percentage")
|
|
169
169
|
@click.option("--template", "construct_template", help="Construct template name")
|
|
170
170
|
@click.option("--output", "-o", help="Output file")
|
|
171
171
|
@click.option("--format", "output_format", default="fasta", help="Output format (fasta, genbank)")
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/optimizer.py
RENAMED
|
@@ -10,7 +10,7 @@ from factorforge.core.interfaces import OptimizationResult, OptimizerEngine
|
|
|
10
10
|
from .exporter import SequenceExporter
|
|
11
11
|
from .rules.reverse_translator import OptimizationProfile, ReverseTranslator
|
|
12
12
|
from .rules.rule_engine import RuleEngine
|
|
13
|
-
from .scoring import calculate_composite_score, compute_mfe_evidence
|
|
13
|
+
from .scoring import calculate_composite_score, compute_mfe_evidence, resolve_host_gc_range
|
|
14
14
|
from .validator import InputValidator
|
|
15
15
|
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
@@ -20,7 +20,7 @@ class RuleBasedOptimizer(OptimizerEngine):
|
|
|
20
20
|
"""Profile-based rule optimization engine."""
|
|
21
21
|
|
|
22
22
|
name = "Profile-based"
|
|
23
|
-
version = "3.
|
|
23
|
+
version = "3.3.0"
|
|
24
24
|
|
|
25
25
|
def __init__(self, codon_table_path: str | None = None) -> None:
|
|
26
26
|
"""
|
|
@@ -157,6 +157,22 @@ class RuleBasedOptimizer(OptimizerEngine):
|
|
|
157
157
|
"score": candidates[0]["score"],
|
|
158
158
|
"violations": sum(len(v) for v in scan_results.values()),
|
|
159
159
|
}
|
|
160
|
+
if profile_value == "balanced":
|
|
161
|
+
host_gc_min, host_gc_max = resolve_host_gc_range(host)
|
|
162
|
+
requested_gc_min_percent = float(kwargs.get("target_gc_min", host_gc_min))
|
|
163
|
+
requested_gc_max_percent = float(kwargs.get("target_gc_max", host_gc_max))
|
|
164
|
+
achieved_gc_percent = metrics["gc_percent"]
|
|
165
|
+
metrics.update(
|
|
166
|
+
{
|
|
167
|
+
"gc_target_reached": (
|
|
168
|
+
requested_gc_min_percent
|
|
169
|
+
<= achieved_gc_percent
|
|
170
|
+
<= requested_gc_max_percent
|
|
171
|
+
),
|
|
172
|
+
"requested_gc_min_percent": requested_gc_min_percent,
|
|
173
|
+
"requested_gc_max_percent": requested_gc_max_percent,
|
|
174
|
+
}
|
|
175
|
+
)
|
|
160
176
|
# MFE provenance: expose whether MFE was actually computed so downstream
|
|
161
177
|
# artifacts (API response, Design Package) never report an uncomputed
|
|
162
178
|
# MFE as a misleading 0.0 (016 audit). Score value is unchanged.
|
|
@@ -376,10 +376,12 @@ class ReverseTranslator:
|
|
|
376
376
|
- Preferred codon ratio: 70%
|
|
377
377
|
- Target GC: host composition band, resolved via
|
|
378
378
|
resolve_host_gc_range() (engines/profile/scoring.py
|
|
379
|
-
GC_OPT_MIN/MAX).
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
379
|
+
GC_OPT_MIN/MAX). N. benthamiana currently uses the NbeV1.1
|
|
380
|
+
high-confidence CDS-derived native-composition band (40-47%);
|
|
381
|
+
other hosts retain their configured defaults.
|
|
382
|
+
- GC target requests are best-effort sampling targets, not guarantees:
|
|
383
|
+
achieved GC can be dominated by the codon table's frequency-weighted
|
|
384
|
+
sampling distribution.
|
|
383
385
|
"""
|
|
384
386
|
_host_gc_min, _host_gc_max = resolve_host_gc_range(self.host)
|
|
385
387
|
target_gc_min = kwargs.get("target_gc_min", _host_gc_min)
|
|
@@ -454,7 +456,7 @@ class ReverseTranslator:
|
|
|
454
456
|
|
|
455
457
|
Targets the caller-supplied ``target_gc`` if provided, otherwise the
|
|
456
458
|
active host's composition midpoint (resolve_host_gc_range(self.host);
|
|
457
|
-
43.5% for nbenthamiana since v3.3.0 /
|
|
459
|
+
43.5% for nbenthamiana since v3.3.0 / reference-policy audit, 60.0% for other
|
|
458
460
|
hosts pending their own host-specific analysis). To target a different
|
|
459
461
|
GC (e.g. for specific vector requirements), pass target_gc explicitly.
|
|
460
462
|
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/rule_engine.py
RENAMED
|
@@ -358,11 +358,11 @@ class RuleEngine:
|
|
|
358
358
|
|
|
359
359
|
This is a LOCAL synthesis/extreme-window guard (default 25-75% over a
|
|
360
360
|
50 bp window), NOT the global GC target. Global GC is governed separately
|
|
361
|
-
by the scoring band (GC_OPT_MIN/MAX
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
361
|
+
by the scoring band (GC_OPT_MIN/MAX; N. benthamiana currently uses
|
|
362
|
+
the NbeV1.1 high-confidence CDS-derived native-composition anchor,
|
|
363
|
+
40-47%, while other hosts retain their own configured defaults) and
|
|
364
|
+
the API/DP gc_min/gc_max constraints. The wide 25-75% local band is
|
|
365
|
+
an independent synthesis/hairpin-risk guard
|
|
366
366
|
(registry `gc_extreme_local_window`), not derived from or coupled to
|
|
367
367
|
the global GC target — narrowing it is a separate decision from the
|
|
368
368
|
global band retarget and is out of scope here.
|
|
@@ -11,27 +11,20 @@ from typing import Any
|
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
|
-
# GC band for N. benthamiana
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#
|
|
23
|
-
# reverted here pending an MFE re-sensitivity + 2x2 factorial recheck
|
|
24
|
-
# (_analysis, scope TBD as of 2026-06-29) — see eijex-workspace
|
|
25
|
-
# _version/factorforge-version-sequencing-plan.md. Not a rejection of the
|
|
26
|
-
# 40-47% anchor, just not yet re-confirmed as the production default.
|
|
27
|
-
GC_OPT_MIN = 55.0
|
|
28
|
-
GC_OPT_MAX = 65.0
|
|
29
|
-
GC_OPT_MID = 60.0 # kept for gc_target point-scoring and viral_delivery centering
|
|
14
|
+
# GC band for the current N. benthamiana software-default codon reference.
|
|
15
|
+
# NbeV1.1 high-confidence CDS-derived codon usage anchors the public default
|
|
16
|
+
# to the native genome-derived composition band. This is a configured
|
|
17
|
+
# in-silico reference/GC policy, not wet-lab validation or expression/yield
|
|
18
|
+
# prediction. Sequences inside [GC_OPT_MIN, GC_OPT_MAX] receive full GC score;
|
|
19
|
+
# outside the band the score decays linearly.
|
|
20
|
+
GC_OPT_MIN = 40.0
|
|
21
|
+
GC_OPT_MAX = 47.0
|
|
22
|
+
GC_OPT_MID = 43.5 # kept for gc_target point-scoring and viral_delivery centering
|
|
30
23
|
GC_DECAY_WIDTH = 20.0 # percentage points outside band before score reaches 0.0
|
|
31
24
|
|
|
32
|
-
# Host-isolation fix
|
|
33
|
-
#
|
|
34
|
-
#
|
|
25
|
+
# Host-isolation fix: ntabacum/BY-2 must not silently inherit the NbeV1.1
|
|
26
|
+
# N. benthamiana band. Other hosts keep the pre-v3.3.0 global default until
|
|
27
|
+
# they get their own host-specific analysis.
|
|
35
28
|
GC_RANGE_DEFAULT: tuple[float, float] = (55.0, 65.0)
|
|
36
29
|
GC_RANGES_BY_HOST: dict[str, tuple[float, float]] = {
|
|
37
30
|
"nbenthamiana": (GC_OPT_MIN, GC_OPT_MAX),
|
|
@@ -41,7 +34,7 @@ GC_RANGES_BY_HOST: dict[str, tuple[float, float]] = {
|
|
|
41
34
|
def resolve_host_gc_range(host: str | None) -> tuple[float, float]:
|
|
42
35
|
"""Resolve the (gc_min, gc_max) composition band for a host.
|
|
43
36
|
|
|
44
|
-
nbenthamiana uses the
|
|
37
|
+
nbenthamiana uses the reference-policy audit native genome-composition anchor.
|
|
45
38
|
Any other host (including unknown ones) keeps the pre-v3.3.0 global
|
|
46
39
|
default band until it gets its own host-specific analysis.
|
|
47
40
|
"""
|
|
@@ -146,7 +139,7 @@ def _check_vienna_available() -> bool:
|
|
|
146
139
|
return _vienna_available
|
|
147
140
|
|
|
148
141
|
|
|
149
|
-
#
|
|
142
|
+
# ViennaRNA's RNA.fold() uses Zuker's MFE algorithm, O(n^3) time /
|
|
150
143
|
# O(n^2) memory — there was previously no length guard anywhere in the
|
|
151
144
|
# calculate_mfe() call chain, so a single request at/under the existing
|
|
152
145
|
# public API length limits (5000aa/15000bp) could pin a CPU core for minutes
|
|
@@ -179,7 +172,7 @@ def calculate_mfe(sequence: str) -> float | None:
|
|
|
179
172
|
"Sequence length (%d nt) exceeds MFE_MAX_SEQUENCE_LENGTH (%d nt); "
|
|
180
173
|
"skipping global MFE calculation to avoid an unbounded ViennaRNA "
|
|
181
174
|
"RNA.fold() runtime (O(n^3)). MFE scoring falls back to a neutral, "
|
|
182
|
-
"zero-weighted contribution for this candidate
|
|
175
|
+
"zero-weighted contribution for this candidate.",
|
|
183
176
|
len(sequence), MFE_MAX_SEQUENCE_LENGTH,
|
|
184
177
|
)
|
|
185
178
|
return None
|
|
@@ -200,8 +193,7 @@ def normalize_mfe(mfe: float, seq_length: int) -> float:
|
|
|
200
193
|
"""
|
|
201
194
|
Normalize MFE to 0-1 range where 1 = no structure (favorable).
|
|
202
195
|
|
|
203
|
-
Clamp range calibrated empirically
|
|
204
|
-
(eijex-workspace/_analysis/2026-06-26/011-mfe-clamp-calibration):
|
|
196
|
+
Clamp range calibrated empirically from internal computational audit data:
|
|
205
197
|
measured MFE/nt across 135 FactorForge outputs (N. benthamiana +
|
|
206
198
|
BY-2 hosts, 5 profiles) ranged -0.4064 to -0.1338 (combined 5th/95th
|
|
207
199
|
percentile -0.3839/-0.1760). The range below widens that empirical
|
|
@@ -215,8 +207,7 @@ def normalize_mfe(mfe: float, seq_length: int) -> float:
|
|
|
215
207
|
Returns:
|
|
216
208
|
Normalized MFE score (0-1) under this computational normalization,
|
|
217
209
|
where a higher value represents a less negative whole-CDS MFE/nt.
|
|
218
|
-
This is a Tier-0 computational heuristic only
|
|
219
|
-
eijex-validationHub/docs/CLAIM_EVIDENCE_BENCHMARK_MODEL.md §4) — no
|
|
210
|
+
This is a Tier-0 computational heuristic only — no
|
|
220
211
|
biological interpretation (translation efficiency, mRNA stability,
|
|
221
212
|
or expression outcome) is implied or validated.
|
|
222
213
|
"""
|
|
@@ -422,7 +413,7 @@ def compute_mfe_evidence(
|
|
|
422
413
|
elif not _check_vienna_available():
|
|
423
414
|
reason = "MFE was not computed because ViennaRNA is unavailable in this environment."
|
|
424
415
|
elif len(sequence) > MFE_MAX_SEQUENCE_LENGTH:
|
|
425
|
-
#
|
|
416
|
+
# Distinguish a deliberate length-based skip from an actual
|
|
426
417
|
# fold failure — the generic "computation failed" message below would
|
|
427
418
|
# otherwise mislead a caller into thinking something is broken.
|
|
428
419
|
reason = (
|
|
@@ -115,13 +115,13 @@ def calculate_dinucleotide_ratio(sequence: str, dinucleotide: str = "CG") -> flo
|
|
|
115
115
|
return observed / expected
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
118
|
+
# NbeV1.1 high-confidence CDS-derived codon usage is the production-default
|
|
119
|
+
# N. benthamiana software reference. This is a provenance-tracked in-silico
|
|
120
|
+
# default, not wet-lab validation or a biological-superiority claim; see
|
|
121
|
+
# data/reference/active_codon_reference.json.
|
|
122
|
+
_HOST_CODON_TABLE_OVERRIDES: dict[str, str] = {
|
|
123
|
+
"nbenthamiana": "profiles/nbev11_cds_hc_derived_codons.json",
|
|
124
|
+
}
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
def resolve_host_codon_table_path(host: str, codon_tables_dir: Path) -> Path:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: factorforge-cds
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: FactorForge - open-source CDS design and pre-synthesis sequence review
|
|
3
|
+
Version: 3.3.0
|
|
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
|
|
7
7
|
Project-URL: Homepage, https://factorforge.eijex.com
|
|
@@ -33,7 +33,7 @@ Dynamic: license-file
|
|
|
33
33
|
|
|
34
34
|
# FactorForge
|
|
35
35
|
|
|
36
|
-
**Open-source constraint-based CDS design and pre-synthesis sequence review
|
|
36
|
+
**Open-source constraint-based CDS design and pre-synthesis sequence review for plant CDS workflows, with primary support for *Nicotiana benthamiana* (Tobacco BY-2: experimental).**
|
|
37
37
|
|
|
38
38
|
[](LICENSE)
|
|
39
39
|
[](https://www.python.org/)
|
|
@@ -93,7 +93,7 @@ FactorForge outputs are **in-silico only** and have not been experimentally vali
|
|
|
93
93
|
## Citing
|
|
94
94
|
|
|
95
95
|
```
|
|
96
|
-
FactorForge v3.
|
|
96
|
+
FactorForge v3.3.0 (2026). Open-source constraint-based CDS design and sequence review.
|
|
97
97
|
Eijex. https://github.com/eijex/factorforge-cds
|
|
98
98
|
```
|
|
99
99
|
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_benchmark_codon_table_metadata.py
RENAMED
|
@@ -111,8 +111,8 @@ def test_smoke_summary_contains_codon_table_fields(tmp_path):
|
|
|
111
111
|
assert field in data, f"Missing codon table field in smoke summary JSON: {field}"
|
|
112
112
|
|
|
113
113
|
# No --codon-table-path override ⇒ codon_table_id must reflect whichever
|
|
114
|
-
# asset is actually the current production default (
|
|
115
|
-
#
|
|
114
|
+
# asset is actually the current production default (v3.3.0 reference-policy update,
|
|
115
|
+
# reference-policy audit — see data/reference/active_codon_reference.json), not a
|
|
116
116
|
# hardcoded legacy literal.
|
|
117
117
|
active_ref = json.loads(
|
|
118
118
|
(ROOT / "data" / "reference" / "active_codon_reference.json").read_text(encoding="utf-8")
|
|
@@ -148,9 +148,6 @@ def test_smoke_summary_contains_vienna_rna_active(tmp_path):
|
|
|
148
148
|
|
|
149
149
|
def test_no_raw_genome_fasta_committed():
|
|
150
150
|
"""Raw genome/CDS/protein FASTA files must not be committed to the repo."""
|
|
151
|
-
forbidden_patterns = ["*.fasta", "*.fa", "*.fna", "*.fastq"]
|
|
152
|
-
allowed_fixtures = ROOT / "tests" / "fixtures"
|
|
153
|
-
|
|
154
151
|
import subprocess
|
|
155
152
|
result = subprocess.run(
|
|
156
153
|
["git", "ls-files", "--", "*.fasta", "*.fa", "*.fna", "*.fastq"],
|
|
@@ -168,6 +165,6 @@ def test_no_raw_genome_fasta_committed():
|
|
|
168
165
|
if p and not any(p.startswith(pfx) for pfx in allowed_prefixes)
|
|
169
166
|
]
|
|
170
167
|
assert not committed, (
|
|
171
|
-
|
|
168
|
+
"Raw FASTA/genome files must not be committed outside allowed locations:\n"
|
|
172
169
|
+ "\n".join(committed)
|
|
173
170
|
)
|
|
@@ -76,7 +76,7 @@ def test_aa_identity_policy_sync():
|
|
|
76
76
|
assert ok["passed"] and ok["aa_identity"] == registry_val
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
# ── codon_reference source-of-truth sync (
|
|
79
|
+
# ── codon_reference source-of-truth sync (v3.3.0 reference-policy update) ────
|
|
80
80
|
|
|
81
81
|
def test_codon_reference_active_sync_with_active_reference_file():
|
|
82
82
|
"""registry's codon_reference.active block must match
|
|
@@ -103,18 +103,16 @@ def test_codon_reference_active_sync_with_active_reference_file():
|
|
|
103
103
|
assert registry_active["sha256"] == hashlib.sha256(table_path.read_bytes()).hexdigest()
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
def
|
|
106
|
+
def test_codon_reference_active_sync_with_v2_manifest():
|
|
107
107
|
"""registry's codon_reference.active block must match the schema-conformant
|
|
108
|
-
|
|
109
|
-
is the production default again as of the v3.2.7 GC-band/codon-reference
|
|
110
|
-
revert, pending an MFE re-sensitivity + 2x2 factorial recheck of v2."""
|
|
108
|
+
NbeV1.1 HC (v2) manifest file's facts now that v2 is the production default."""
|
|
111
109
|
import json
|
|
112
110
|
from pathlib import Path
|
|
113
111
|
|
|
114
112
|
registry_active = _resolve("codon_reference.active")
|
|
115
113
|
manifest = json.loads(
|
|
116
114
|
(Path(__file__).resolve().parents[1] / "data" / "reference"
|
|
117
|
-
/ "
|
|
115
|
+
/ "codon_table_manifest_nbev11_hc_v2.json").read_text(encoding="utf-8")
|
|
118
116
|
)
|
|
119
117
|
assert registry_active["id"] == manifest["codon_table_id"]
|
|
120
118
|
assert registry_active["sha256"] == manifest["sha256"]
|
|
@@ -122,22 +120,21 @@ def test_codon_reference_active_sync_with_legacy_manifest():
|
|
|
122
120
|
assert registry_active["source_status"] == manifest["source_status"]
|
|
123
121
|
|
|
124
122
|
|
|
125
|
-
def
|
|
126
|
-
"""registry's codon_reference.
|
|
127
|
-
|
|
128
|
-
while not active."""
|
|
123
|
+
def test_codon_reference_historical_comparator_sync_with_legacy_manifest():
|
|
124
|
+
"""registry's codon_reference.historical_comparator block must stay in sync
|
|
125
|
+
with the legacy v1 manifest retained for v3.2.x continuity/replay."""
|
|
129
126
|
import json
|
|
130
127
|
from pathlib import Path
|
|
131
128
|
|
|
132
|
-
|
|
129
|
+
registry_historical = _resolve("codon_reference.historical_comparator")
|
|
133
130
|
manifest = json.loads(
|
|
134
131
|
(Path(__file__).resolve().parents[1] / "data" / "reference"
|
|
135
|
-
/ "
|
|
132
|
+
/ "codon_table_manifest.json").read_text(encoding="utf-8")
|
|
136
133
|
)
|
|
137
|
-
assert
|
|
138
|
-
assert
|
|
139
|
-
assert
|
|
140
|
-
assert
|
|
134
|
+
assert registry_historical["id"] == manifest["codon_table_id"]
|
|
135
|
+
assert registry_historical["sha256"] == manifest["sha256"]
|
|
136
|
+
assert registry_historical["asset_type"] == manifest["asset_type"]
|
|
137
|
+
assert registry_historical["source_status"] == manifest["source_status"]
|
|
141
138
|
|
|
142
139
|
|
|
143
140
|
def test_codon_reference_active_table_sha256_matches_production_default():
|
|
@@ -63,7 +63,7 @@ def test_sfgfp_sequence_no_x_residues():
|
|
|
63
63
|
def test_run_example_deterministic():
|
|
64
64
|
"""run_example.py (no --freeze) must exit 0 when frozen outputs exist.
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
v3.3.0 reference-policy update: this script is explicitly pinned to the
|
|
67
67
|
legacy v1 codon reference (GC 55-65%) regardless of the engine's current
|
|
68
68
|
production default, so this reproducibility check stays valid forever.
|
|
69
69
|
See run_example_v2_smoke.py / test_run_example_v2_smoke_succeeds for the
|
|
@@ -85,8 +85,7 @@ def test_run_example_v2_smoke_succeeds():
|
|
|
85
85
|
"""run_example_v2_smoke.py (tracks whatever the current production
|
|
86
86
|
default actually is) must exit 0 and report a provenance ID that matches
|
|
87
87
|
data/reference/active_codon_reference.json — not hardcoded to v2, since
|
|
88
|
-
the
|
|
89
|
-
re-sensitivity + 2x2 factorial recheck (Job 168 / v3.3.0, _analysis/025).
|
|
88
|
+
the example is aligned to the current v3.3.0 production-default reference policy.
|
|
90
89
|
|
|
91
90
|
No frozen-output comparison — this only checks that the current-default
|
|
92
91
|
path runs end-to-end and reports correct provenance.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/optimizer.py
RENAMED
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/validator.py
RENAMED
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/data/nbenthamiana_codons.json
RENAMED
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/data/nbenthamiana_golden_set.json
RENAMED
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/data/templates/high_expression.json
RENAMED
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/data/wolffia_globosa_codons.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/construct_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/scoring_ml.py
RENAMED
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/validator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/risk_classifier.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/schemas/design_package.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge/validation/package_generator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.8 → factorforge_cds-3.3.0}/tests/test_openbio_missing_metric_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|