fix(protspace): derive missing sequence length from fasta - #401
fix(protspace): derive missing sequence length from fasta#401FlorinSenoner wants to merge 6 commits into
Conversation
FlorinSenoner
left a comment
There was a problem hiding this comment.
One cache-path correctness issue found.
| if self.sources_to_fetch["uniprot"] | ||
| else cached_uniprot | ||
| ) | ||
| uniprot_annotations = self._fill_missing_fasta_lengths(uniprot_annotations) |
There was a problem hiding this comment.
[P2] Apply the fallback before the complete-cache early return
This helper only runs once ProteinAnnotationManager.to_pd() is reached, but ReductionPipeline._fetch_annotations() returns cached_df directly when all_annotations.parquet already contains the requested columns (and protspace prepare keeps that cache by default). I reproduced this at this head with cached custom_protein,length='' plus FASTA MPEPTIDE: the pipeline logged Using cached annotations and returned '', not '8'. Users rerunning an existing output after upgrading therefore continue to see N/A. Route the complete-cache branch through the same enrichment (or invalidate/migrate cached empty lengths) and add a warm-cache pipeline regression test.
There was a problem hiding this comment.
Fixed in 0e000cb. I independently reproduced the complete-cache branch returning an empty length for cached custom_protein plus FASTA MPEPTIDE (cached_length='', expected 8). The cache-hit branch now enriches a copy of the selected DataFrame through the same missing-only scalar resolver used by ProteinAnnotationManager; non-empty cached lengths remain authoritative and all_annotations.parquet is not rewritten. The new warm-cache pipeline regression failed as ['', '110'] != ['8', '110'] before the fix and passes afterward. Fresh evidence: focused affected tests 160 passed; full Python suite 753 passed, 2 skipped; Ruff clean/143 formatted; strict OpenSpec valid; pnpm precommit passed; bundle contract 11 passed.
Automated reviewDoes it solve #336? Partially — the FASTA length fallback is correctly implemented in Found 2 issues:
protspace/apps/protspace/src/protspace/cli/annotate.py Lines 98 to 104 in 0e000cb
protspace/apps/protspace/docs/annotations.md Lines 21 to 27 in 0e000cb 🤖 Generated with Claude Code |
|
Independent triage at
No fix was applied in this triage, and no thread was resolved. |
|
Implemented the two actionable findings in
Verification: affected tests |
- Merge the function-local UniProtRetriever import in test_annotate_fasta_derives_missing_length_from_normalized_sequence into the existing module-level import from the same module; the deferred import was not load-bearing since monkeypatch targets a class attribute. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016qoU16kDQxz6U3H2UWbbm2
Adversarial reviewReviewed in an isolated worktree by three independent lenses (code quality, adversarial correctness, issue-resolution audit), with every finding then put through a refuter whose default position was that it is a false positive. 8 raised, 4 survived refutation. Applied and pushed (
|
|
Addressed the adversarial review at #401 (comment) in
Verification:
No review thread was resolved and no PR metadata was changed. |
Summary
Root cause
ReductionPipelinealready parsed FASTA files and passed an identifier-to-sequence map intoProteinAnnotationManager. The manager used those local sequences only for InterPro and Biocentral requests, while the exportedlengthfield remained sourced exclusively from UniProt. An unmapped identifier therefore kept an empty UniProt length even though its FASTA sequence was available.Fix
Normalize the primary annotation rows immediately after UniProt retrieval. When a row has an empty
lengthand a matching non-empty local sequence, copy the row and filllengthwith the sequence's residue count. Existing UniProt lengths remain authoritative.Reproduction
Against
main(fbbaefa1):custom_proteinwith FASTA-derived sequenceMPEPTIDE(8 residues) to the annotation manager.length.actual_length=''.After this change, the same reproduction returns
actual_length='8'.Tests
assert '' == '8'2 passed)81 passed789 passed, 6 deselectedpnpm precommit: passed before commit and pushCloses #336