Fix #108: refuse to fold when chain IDs are exhausted - #112
Merged
Conversation
EntityList.add assigned chain IDs with ``_CHAIN_IDS[len(self.entities) % len(_CHAIN_IDS)]``. Past 26 entities the modulo wrapped: entity 27 silently got chain ID 'A' again. Duplicate chain IDs are structurally invalid but are accepted verbatim by the downstream input formats (Boltz YAML `id:`, Chai FASTA headers), so the job folded the wrong complex instead of failing. Reachable today because the 26-record limit is enforced per FASTA file in parse_fasta_entities, not across inputs: `--protein a.fasta --dna b.fasta` with 20 records each builds 40 entities and passes every check. The cap now lives in ``add`` as a data-model invariant, next to the CCD validation and for the same reason — it covers the CLI, the batch job-file path, adapters, and direct library use. The modulo is gone, so the index can no longer wrap silently. --force deliberately does not lift it: it lifts the per-file sequence and residue limits, which are soft guards, whereas there simply is no 27th single-letter chain ID. The message says so, names the shared budget across all inputs, and suggests splitting the complex into separate jobs. Both CLI entity-collection paths now funnel every add through a small wrapper that reports these invariants as click.UsageError, so the user gets `Error: <message>` (exit 2) at submit time rather than a traceback — previously only the ligand CCD error was translated. Tests: boundary (26 build with distinct chain IDs A-Z, 27th raises), ligands/SMILES share the budget, the rejected entity is not appended, the CCD error is not shadowed below the cap, message content, multi-file combination via _build_entity_list, and --force not bypassing it. Suite: 598 passed, 10 skipped (was 589 passed, 10 skipped). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DEWo4xvL59PHC1QUvtTV9F
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #108.
EntityList.addassigned chain IDs with_CHAIN_IDS[len(self.entities) % 26], so entity 27 silently got chain IDAagain — duplicate chains that Boltz YAML and Chai FASTA headers both accept and then fold as the wrong complex. Reachable today because the 26-record limit is per FASTA file, so--protein a.fasta --dna b.fastawith 20 records each passes validation.addnow raises before appending, and the modulo is gone so the index cannot wrap even if the guard moved. Enforced inaddas a data-model invariant, the same rationale as the CCD validation already there — it covers the CLI, the batch job-file path, adapters, and library use.--forcedeliberately does not lift it.--forcerelaxes the per-file sequence and total-residue limits, which are soft guards; there is no 27th single-letter chain ID to hand out, so this one is structural. The message says so, and a test pins it.cli.pygained a small wrapper so every entity flag reports these invariants asError: <message>with exit 2 rather than a traceback — previously only ligand errors were translated. 598 passed, 10 skipped.