From 1cca2311da6918fd8bd884b0a3743ec88bc699a5 Mon Sep 17 00:00:00 2001 From: gbouras13 Date: Fri, 7 Aug 2026 10:12:10 +0930 Subject: [PATCH] Fix: agtools clean doesn't fill '*' placeholder sequences on real myloasm output transform_segment in clean.py only filled a segment's sequence when the existing GFA field was a literal empty string (parts[2] == ""). Real myloasm output uses the standard GFA1 "*" placeholder for an unspecified sequence instead -- confirmed against actual myloasm assembly output, not assumed. tests/data/myloasm/final_contig_graph.gfa happens to use the empty-field convention (it appears to be from an older myloasm build, judging by its embedded dna_r10.4.1_e8.2_sup@v3.5.1 model tag), which is why the existing test suite didn't catch this -- it only exercised the convention that already worked. Net effect: `agtools clean -a myloasm` ran without error and correctly dropped segments/links/paths with no FASTA match, but silently left every surviving segment's sequence as "*" -- the GFA looked cleaned but was still missing the one thing most downstream tools need. Also treats "*" as needing a fill, matching real-world myloasm output. Added test_write_filtered_graph_fills_star_placeholder_sequences alongside the existing empty-field test; both pass, full suite (250 tests) passes with no regressions. --- src/agtools/commands/clean.py | 2 +- tests/test_clean_unit.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/agtools/commands/clean.py b/src/agtools/commands/clean.py index 3655dc9..091064a 100644 --- a/src/agtools/commands/clean.py +++ b/src/agtools/commands/clean.py @@ -52,7 +52,7 @@ def keep_segment(seg_id: str) -> bool: return seg_id not in segments_to_remove def transform_segment(parts: list[str]) -> list[str]: - if len(parts) > 2 and parts[2] == "": + if len(parts) > 2 and parts[2] in ("", "*"): updated_parts = parts.copy() updated_parts[2] = str(parser.get_sequence(parts[1])) return updated_parts diff --git a/tests/test_clean_unit.py b/tests/test_clean_unit.py index 6e7825c..d5ddc92 100644 --- a/tests/test_clean_unit.py +++ b/tests/test_clean_unit.py @@ -48,6 +48,32 @@ def test_write_filtered_graph_fills_missing_sequences_and_prunes_edges(tmp_path) assert "W\tw2\t*\t*\t*\t>s1" in content +def test_write_filtered_graph_fills_star_placeholder_sequences(tmp_path): + """Real myloasm output uses the standard GFA1 "*" placeholder for an + unspecified sequence (confirmed against real myloasm assembly output), not + the empty-field convention the other test above covers. Both need to be + handled -- this was previously only handled for the empty-field case, so + real myloasm output silently kept "*" instead of getting a sequence filled + in from the FASTA. + """ + gfa_file = tmp_path / "graph.gfa" + gfa_file.write_text( + "S\ts1\t*\tLN:i:4\n" + "S\ts2\tTT\n" + "L\ts1\t+\ts2\t-\t1M\n" + ) + + parser = DummyParser(index={"s1": 0}, sequences={"s1": "ACGT"}) + + target = tmp_path / "cleaned_graph.gfa" + _write_filtered_graph(set(), parser, str(gfa_file), str(target)) + content = target.read_text() + + assert "S\ts1\tACGT\tLN:i:4" in content + assert "S\ts1\t*\tLN:i:4" not in content + assert "S\ts2\tTT" in content + + def test_clean_removes_segments_missing_from_fasta_index(tmp_path, monkeypatch): class DummyUG: segment_names = ["s1", "s2"]