factorforge-cds 3.2.1__tar.gz → 3.2.3__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.
Files changed (95) hide show
  1. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/PKG-INFO +4 -3
  2. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/README.md +3 -2
  3. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/pyproject.toml +1 -1
  4. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/__init__.py +1 -1
  5. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/__init__.py +1 -1
  6. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/__init__.py +1 -1
  7. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/optimizer.py +25 -4
  8. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge_cds.egg-info/PKG-INFO +4 -3
  9. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_docs_consistency.py +14 -3
  10. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/LICENSE +0 -0
  11. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/setup.cfg +0 -0
  12. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/__main__.py +0 -0
  13. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/analysis/__init__.py +0 -0
  14. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/analysis/feasibility.py +0 -0
  15. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/analysis/metrics.py +0 -0
  16. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/cli/__init__.py +0 -0
  17. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/cli/legacy_cli.py +0 -0
  18. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/cli/main.py +0 -0
  19. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/core/interfaces/__init__.py +0 -0
  20. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/core/interfaces/exporter.py +0 -0
  21. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/core/interfaces/optimizer.py +0 -0
  22. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/core/interfaces/validator.py +0 -0
  23. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/data/nbenthamiana_codons.json +0 -0
  24. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/data/nbenthamiana_golden_set.json +0 -0
  25. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/data/ntabacum_codons.json +0 -0
  26. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/data/templates/high_expression.json +0 -0
  27. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/data/templates/standard_expression.json +0 -0
  28. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/data/wolffia_globosa_codons.json +0 -0
  29. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/database.py +0 -0
  30. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/codon_table_builder.py +0 -0
  31. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/construct_builder.py +0 -0
  32. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/exporter.py +0 -0
  33. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/pipeline.py +0 -0
  34. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/rules/__init__.py +0 -0
  35. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/rules/domesticator.py +0 -0
  36. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/rules/reverse_translator.py +0 -0
  37. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/rules/rule_engine.py +0 -0
  38. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/scoring.py +0 -0
  39. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/scoring_ml.py +0 -0
  40. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/utils.py +0 -0
  41. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/profile/validator.py +0 -0
  42. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/engines/registry.py +0 -0
  43. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/io/__init__.py +0 -0
  44. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/io/fasta.py +0 -0
  45. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/io/validation.py +0 -0
  46. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/protein_risk/__init__.py +0 -0
  47. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/protein_risk/annotate.py +0 -0
  48. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/protein_risk/kd_scale.py +0 -0
  49. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/protein_risk/risk_classifier.py +0 -0
  50. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/protein_risk/sp_predict.py +0 -0
  51. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/protein_risk/tm_predict.py +0 -0
  52. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/registry/__init__.py +0 -0
  53. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/registry/registry_loader.py +0 -0
  54. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/schemas/__init__.py +0 -0
  55. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/schemas/design_package.py +0 -0
  56. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/schemas/design_package.schema.json +0 -0
  57. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/utils/__init__.py +0 -0
  58. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/utils/construct_id.py +0 -0
  59. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/utils/exceptions.py +0 -0
  60. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/utils/restriction_sites.py +0 -0
  61. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/utils/sequence_validator.py +0 -0
  62. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/utils/validation.py +0 -0
  63. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/validation/__init__.py +0 -0
  64. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/validation/cli.py +0 -0
  65. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge/validation/package_generator.py +0 -0
  66. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge_cds.egg-info/SOURCES.txt +0 -0
  67. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge_cds.egg-info/dependency_links.txt +0 -0
  68. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge_cds.egg-info/entry_points.txt +0 -0
  69. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge_cds.egg-info/requires.txt +0 -0
  70. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/src/factorforge_cds.egg-info/top_level.txt +0 -0
  71. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_baselines.py +0 -0
  72. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_benchmark_codon_table_metadata.py +0 -0
  73. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_benchmark_regression.py +0 -0
  74. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_benchmark_scoring.py +0 -0
  75. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_benchmark_smoke.py +0 -0
  76. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_cai.py +0 -0
  77. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_codon_table_manifest.py +0 -0
  78. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_database.py +0 -0
  79. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_design_package_schema.py +0 -0
  80. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_design_package_semantics.py +0 -0
  81. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_design_package_serialization.py +0 -0
  82. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_fasta_io.py +0 -0
  83. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_gc_content.py +0 -0
  84. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_host_profile_metadata.py +0 -0
  85. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_iupac_validation.py +0 -0
  86. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_legacy_cli.py +0 -0
  87. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_no_raw_sequence_logging.py +0 -0
  88. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_openbio_missing_metric_contract.py +0 -0
  89. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_parameter_registry.py +0 -0
  90. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_protein_risk.py +0 -0
  91. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_registry_production_sync.py +0 -0
  92. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_restriction_sites.py +0 -0
  93. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_sequence_validator.py +0 -0
  94. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/tests/test_translation_integrity.py +0 -0
  95. {factorforge_cds-3.2.1 → factorforge_cds-3.2.3}/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.2.1
3
+ Version: 3.2.3
4
4
  Summary: FactorForge - open-source constraint-based CDS design engine by Eijex.
5
5
  Author-email: Eijex <eijex.lab@gmail.com>
6
6
  License-Expression: AGPL-3.0-only
@@ -40,7 +40,7 @@ Dynamic: license-file
40
40
  [![PyPI](https://img.shields.io/pypi/v/factorforge-cds.svg)](https://pypi.org/project/factorforge-cds/)
41
41
  [![CI](https://github.com/eijex/factorforge-cds/actions/workflows/ci.yml/badge.svg)](https://github.com/eijex/factorforge-cds/actions/workflows/ci.yml)
42
42
  [![codecov](https://codecov.io/gh/eijex/factorforge-cds/branch/main/graph/badge.svg)](https://codecov.io/gh/eijex/factorforge-cds)
43
- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20640931.svg)](https://doi.org/10.5281/zenodo.20640931)
43
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20407330.svg)](https://doi.org/10.5281/zenodo.20407330)
44
44
  [![Web App](https://img.shields.io/badge/web-factorforge.eijex.com-brightgreen.svg)](https://factorforge.eijex.com)
45
45
 
46
46
  FactorForge performs profile-guided CDS design with CAI/GC metrics, PolyA-signal screening, and Golden Gate/MoClo-aware checks. Primary support: *N. benthamiana* (agroinfiltration). Experimental host context: Tobacco BY-2 (`--host by2`).
@@ -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.2.0 (2026). Open-source constraint-based CDS design engine.
96
+ FactorForge v3.2.3 (2026). Open-source constraint-based CDS design engine.
97
97
  Eijex. https://github.com/eijex/factorforge-cds
98
98
  ```
99
99
 
@@ -118,4 +118,5 @@ GNU Affero General Public License v3.0 — see [LICENSE](LICENSE).
118
118
  - **GitHub Issues** — bugs, features: [github.com/eijex/factorforge-cds/issues](https://github.com/eijex/factorforge-cds/issues)
119
119
  - **Email** — eijex.lab@gmail.com
120
120
  - **FactorForge** — [factorforge.eijex.com](https://factorforge.eijex.com)
121
+ - **Eijex MCP** — [mcp.eijex.com](https://mcp.eijex.com)
121
122
  - **Lab** — [www.eijex.com](https://www.eijex.com)
@@ -7,7 +7,7 @@
7
7
  [![PyPI](https://img.shields.io/pypi/v/factorforge-cds.svg)](https://pypi.org/project/factorforge-cds/)
8
8
  [![CI](https://github.com/eijex/factorforge-cds/actions/workflows/ci.yml/badge.svg)](https://github.com/eijex/factorforge-cds/actions/workflows/ci.yml)
9
9
  [![codecov](https://codecov.io/gh/eijex/factorforge-cds/branch/main/graph/badge.svg)](https://codecov.io/gh/eijex/factorforge-cds)
10
- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20640931.svg)](https://doi.org/10.5281/zenodo.20640931)
10
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20407330.svg)](https://doi.org/10.5281/zenodo.20407330)
11
11
  [![Web App](https://img.shields.io/badge/web-factorforge.eijex.com-brightgreen.svg)](https://factorforge.eijex.com)
12
12
 
13
13
  FactorForge performs profile-guided CDS design with CAI/GC metrics, PolyA-signal screening, and Golden Gate/MoClo-aware checks. Primary support: *N. benthamiana* (agroinfiltration). Experimental host context: Tobacco BY-2 (`--host by2`).
@@ -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.2.0 (2026). Open-source constraint-based CDS design engine.
63
+ FactorForge v3.2.3 (2026). Open-source constraint-based CDS design engine.
64
64
  Eijex. https://github.com/eijex/factorforge-cds
65
65
  ```
66
66
 
@@ -85,4 +85,5 @@ GNU Affero General Public License v3.0 — see [LICENSE](LICENSE).
85
85
  - **GitHub Issues** — bugs, features: [github.com/eijex/factorforge-cds/issues](https://github.com/eijex/factorforge-cds/issues)
86
86
  - **Email** — eijex.lab@gmail.com
87
87
  - **FactorForge** — [factorforge.eijex.com](https://factorforge.eijex.com)
88
+ - **Eijex MCP** — [mcp.eijex.com](https://mcp.eijex.com)
88
89
  - **Lab** — [www.eijex.com](https://www.eijex.com)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "factorforge-cds"
7
- version = "3.2.1"
7
+ version = "3.2.3"
8
8
  description = "FactorForge - open-source constraint-based CDS design engine by Eijex."
9
9
  readme = "README.md"
10
10
  license = "AGPL-3.0-only"
@@ -4,7 +4,7 @@ FactorForge - Codon Optimization Platform
4
4
  profile: constraint-aware rule/profile engine
5
5
  """
6
6
 
7
- __version__ = "3.2.1"
7
+ __version__ = "3.2.3"
8
8
  __author__ = "Eijex"
9
9
 
10
10
  # Auto-register engines (safe when running from source tree)
@@ -13,7 +13,7 @@ def register_builtin_engines() -> None:
13
13
  "profile",
14
14
  RuleBasedOptimizer,
15
15
  metadata={
16
- "version": "3.2.0",
16
+ "version": "3.2.3",
17
17
  "engine_type": "profile_rule_based",
18
18
  "role": "stable_profile_engine",
19
19
  "stable": True,
@@ -5,7 +5,7 @@ Production system (2026)
5
5
  Plant-specific rule-based optimization
6
6
  """
7
7
 
8
- __version__ = "3.2.0"
8
+ __version__ = "3.2.3"
9
9
 
10
10
  from .optimizer import RuleBasedOptimizer
11
11
  from .pipeline import OptimizationPipeline
@@ -17,11 +17,30 @@ class RuleBasedOptimizer(OptimizerEngine):
17
17
  """Profile-based rule optimization engine."""
18
18
 
19
19
  name = "Profile-based"
20
- version = "3.2.0"
20
+ version = "3.2.3"
21
21
 
22
- def __init__(self) -> None:
22
+ def __init__(self, codon_table_path: str | None = None) -> None:
23
+ """
24
+ Args:
25
+ codon_table_path: Optional path to a codon usage table JSON used for
26
+ design-time reverse translation. When omitted (the default), the
27
+ bundled host reference table is used — this is the only path the
28
+ product `optimize` CLI and the registered engine ever take, so
29
+ their behavior is unchanged. Injection is used by the benchmark
30
+ harness to drive design with alternative source-profile tables.
31
+ """
23
32
  self.validator = InputValidator()
24
- self.translator = ReverseTranslator() # Data files use default path
33
+ self._codon_table_path = codon_table_path
34
+ # When a source-profile table is injected, drive every CAI-dependent
35
+ # profile from it: it is both the design weight table and the golden set
36
+ # that high_cai maximizes against. Otherwise high_cai would keep chasing
37
+ # the bundled golden set and stay invariant to the source profile,
38
+ # making the injection incomplete. gc_target is GC-driven and remains
39
+ # legitimately invariant either way.
40
+ self.translator = ReverseTranslator(
41
+ codon_table_path=codon_table_path,
42
+ golden_set_path=codon_table_path,
43
+ )
25
44
  self.rule_engine = RuleEngine()
26
45
  self.exporter = SequenceExporter()
27
46
 
@@ -74,7 +93,9 @@ class RuleBasedOptimizer(OptimizerEngine):
74
93
  f"Unknown profile: {profile_value}. Supported profiles: {supported}"
75
94
  ) from exc
76
95
 
77
- if host == "nbenthamiana":
96
+ if self._codon_table_path is not None or host == "nbenthamiana":
97
+ # An injected design table (benchmark source-profile runs) is
98
+ # authoritative: it already encodes the host it was derived from.
78
99
  translator = self.translator
79
100
  rule_engine = self.rule_engine
80
101
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: factorforge-cds
3
- Version: 3.2.1
3
+ Version: 3.2.3
4
4
  Summary: FactorForge - open-source constraint-based CDS design engine by Eijex.
5
5
  Author-email: Eijex <eijex.lab@gmail.com>
6
6
  License-Expression: AGPL-3.0-only
@@ -40,7 +40,7 @@ Dynamic: license-file
40
40
  [![PyPI](https://img.shields.io/pypi/v/factorforge-cds.svg)](https://pypi.org/project/factorforge-cds/)
41
41
  [![CI](https://github.com/eijex/factorforge-cds/actions/workflows/ci.yml/badge.svg)](https://github.com/eijex/factorforge-cds/actions/workflows/ci.yml)
42
42
  [![codecov](https://codecov.io/gh/eijex/factorforge-cds/branch/main/graph/badge.svg)](https://codecov.io/gh/eijex/factorforge-cds)
43
- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20640931.svg)](https://doi.org/10.5281/zenodo.20640931)
43
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20407330.svg)](https://doi.org/10.5281/zenodo.20407330)
44
44
  [![Web App](https://img.shields.io/badge/web-factorforge.eijex.com-brightgreen.svg)](https://factorforge.eijex.com)
45
45
 
46
46
  FactorForge performs profile-guided CDS design with CAI/GC metrics, PolyA-signal screening, and Golden Gate/MoClo-aware checks. Primary support: *N. benthamiana* (agroinfiltration). Experimental host context: Tobacco BY-2 (`--host by2`).
@@ -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.2.0 (2026). Open-source constraint-based CDS design engine.
96
+ FactorForge v3.2.3 (2026). Open-source constraint-based CDS design engine.
97
97
  Eijex. https://github.com/eijex/factorforge-cds
98
98
  ```
99
99
 
@@ -118,4 +118,5 @@ GNU Affero General Public License v3.0 — see [LICENSE](LICENSE).
118
118
  - **GitHub Issues** — bugs, features: [github.com/eijex/factorforge-cds/issues](https://github.com/eijex/factorforge-cds/issues)
119
119
  - **Email** — eijex.lab@gmail.com
120
120
  - **FactorForge** — [factorforge.eijex.com](https://factorforge.eijex.com)
121
+ - **Eijex MCP** — [mcp.eijex.com](https://mcp.eijex.com)
121
122
  - **Lab** — [www.eijex.com](https://www.eijex.com)
@@ -7,6 +7,7 @@ reproducibility anchors drift.
7
7
  from __future__ import annotations
8
8
  import hashlib
9
9
  import json
10
+ import subprocess
10
11
  from pathlib import Path
11
12
 
12
13
  import pytest
@@ -52,11 +53,21 @@ def test_manifest_inputs_sha256_non_empty():
52
53
 
53
54
 
54
55
  def test_manifest_input_sha256_matches_files():
56
+ # Hash the committed git blob, not local working-tree bytes: on Windows,
57
+ # core.autocrlf=true plus this repo's .gitattributes (eol=lf for
58
+ # json/yaml/yml) means the working tree can hold CRLF while git stores
59
+ # LF, so path.read_bytes() can produce a value that mismatches what is
60
+ # actually committed (the root cause of the Job 129 / Job 136 incidents).
55
61
  data = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
56
62
  for name, entry in data["inputs"].items():
57
- path = ROOT / entry["path"]
58
- actual = hashlib.sha256(path.read_bytes()).hexdigest()
59
- assert actual == entry["sha256"], f"MANIFEST.json hash drift for {name}: {path}"
63
+ blob = subprocess.run(
64
+ ["git", "show", f"HEAD:{entry['path']}"],
65
+ cwd=ROOT,
66
+ capture_output=True,
67
+ check=True,
68
+ ).stdout
69
+ actual = hashlib.sha256(blob).hexdigest()
70
+ assert actual == entry["sha256"], f"MANIFEST.json hash drift for {name}: {entry['path']}"
60
71
 
61
72
 
62
73
  def test_manifest_commands_use_reproducible_entrypoints():
File without changes