From 838257161ff19a7189517013b01c91c18577be7a Mon Sep 17 00:00:00 2001
From: Jude Thomas Zeng <98306986+1613358894@users.noreply.github.com>
Date: Mon, 3 Aug 2026 23:43:51 +0800
Subject: [PATCH] Refine download smoke fragmentation gate
---
docs/reference.md | 2 +-
tests/test_cli_download_smoke.py | 70 ++++++++++++++++++++++++++++++
typetreeflow/download_smoke_cli.py | 9 ++++
3 files changed, 80 insertions(+), 1 deletion(-)
diff --git a/docs/reference.md b/docs/reference.md
index 908ad1b..2526b7e 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -1561,7 +1561,7 @@ Recommended layout:
If the `datasets` executable is missing, execution records `datasets_executable_missing`, leaves the row unexecuted, and keeps `downloads_triggered=0`, `network_access=false`, and `external_tools=false`.
`--download-smoke-execution-dir
` is an explicit read-only report/package surface for these two files. Report-only mode may show `## Bounded Download Smoke Execution`; `package-results --include reports` and `--include all` may copy validated members under `download_smoke/` with `evidence_policy=download_smoke_execution_audit` and `strict_scientific_deliverable=false`. Missing input is omitted; partial or malformed input copies only valid members and emits a compact warning. Failed-handoff packages exclude the execution files.
- `bounded_download_smoke_inspection.tsv`: local inspection rows for a bounded smoke plan after a separately authorized download attempt. Fields are `record_id`, `assembly_accession`, `assembly_level`, `refseq_category`, `quality_tier`, `zip_path`, `zip_exists`, `zip_valid`, `unsafe_zip_member_count`, `genome_fasta_present`, `genome_fasta_member_count`, `genomic_named_fasta_member_count`, `genome_fasta_install_selection_status`, `fasta_record_count`, `fasta_total_bases`, `fasta_longest_record_bases`, `fasta_n50_bases`, `fasta_ambiguous_bases`, `fasta_header_wgs_keyword_count`, `fasta_header_scaffold_keyword_count`, `fasta_header_contig_keyword_count`, `empty_genome_fasta_count`, `multiple_genome_fasta_members_count`, `fasta_fragmentation_signal`, `fasta_quality_gate_blockers`, `installable_genome_fasta_ready`, `installable_genome_fasta_not_ready_reasons`, and `status`. `assembly_level`, `refseq_category`, and `quality_tier` are copied from annotated bounded smoke plans when available and otherwise use `unknown`. `unsafe_zip_member_count` is a count-only fail-closed signal for absolute, path-traversing, Windows-drive-prefixed, or symlink-like ZIP members; row status becomes `zip_unsafe_members` and blocker `unsafe_zip_member_paths` is reported before FASTA inspection. `genome_fasta_install_selection_status` is `selected`, `ambiguous`, or `not_evaluated` and mirrors the later reference-genome installer selection rule: one FASTA member is selectable, more than one member is selectable only when exactly one member is named `genomic.fna` or `*_genomic.fna`. When the source member is uniquely selectable, FASTA quality statistics describe that installable member; member-count fields still describe all FASTA members in the ZIP. `fasta_quality_gate_blockers` is empty unless explicit local quality gates are supplied, then contains semicolon-separated controlled blocker codes for that row. `installable_genome_fasta_ready` is a row-level local smoke signal derived from ZIP validity, unsafe-member checks, genome FASTA presence, install-selection status, empty-FASTA checks, and enabled quality-gate blockers; `installable_genome_fasta_not_ready_reasons` contains semicolon-separated controlled reason codes when the row is not ready. The FASTA statistics, N50 value, header keyword counts, unsafe-member count, empty-FASTA count, multiple-member count, install-selection status, fragmentation signal, row-level readiness, and row-level blocker/reason codes are counts-only audit visibility; raw sequence text and raw FASTA headers are not copied into the inspection files. Readers accept earlier inspection TSVs that lack `assembly_level`, `refseq_category`, `quality_tier`, `unsafe_zip_member_count`, `genome_fasta_install_selection_status`, `genomic_named_fasta_member_count`, `multiple_genome_fasta_members_count`, `empty_genome_fasta_count`, `fasta_quality_gate_blockers`, `installable_genome_fasta_ready`, or `installable_genome_fasta_not_ready_reasons`.
-- `bounded_download_smoke_inspection_summary.json`: read-only JSON summary for `download-smoke inspect`. Stable fields include `schema_version`, `command`, `source_download_plan_path`, `selected_row_count`, `zip_exists_count`, `zip_valid_count`, `unsafe_zip_member_count`, `genome_fasta_present_count`, `genome_fasta_member_count`, `genomic_named_fasta_member_count`, `genome_fasta_install_selection_status_counts`, `genome_fasta_install_selection_ambiguous_count`, `installable_genome_fasta_ready_count`, `installable_genome_fasta_not_ready_count`, `installable_genome_fasta_not_ready_reason_counts`, `installable_genome_fasta_ready_preview`, `installable_genome_fasta_ready_preview_truncated`, `installable_genome_fasta_not_ready_preview`, `installable_genome_fasta_not_ready_preview_truncated`, `assembly_metadata_high_quality_row_count`, `assembly_metadata_high_quality_installable_ready_count`, `assembly_metadata_high_quality_fasta_quality_blocked_count`, `assembly_metadata_high_quality_fasta_quality_blocker_counts`, `assembly_metadata_high_quality_fasta_quality_blocked_preview`, `assembly_metadata_high_quality_fasta_quality_blocked_preview_truncated`, `fasta_record_count`, `fasta_total_bases`, `fasta_longest_record_bases`, `fasta_max_n50_bases`, `fasta_ambiguous_bases`, `fasta_header_wgs_keyword_count`, `fasta_header_scaffold_keyword_count`, `fasta_header_contig_keyword_count`, `empty_genome_fasta_count`, `multiple_genome_fasta_members_count`, `fasta_fragmentation_signal_counts`, `installable_genome_fasta_fragmentation_signal_counts`, `installable_genome_fasta_header_fragment_keyword_row_count`, `min_fasta_n50_bases`, `max_fasta_record_count`, `max_fasta_ambiguous_bases`, `min_fasta_total_bases`, `min_fasta_longest_record_bases`, `quality_profile`, `block_fragmented_fasta`, `block_fasta_header_keywords`, `fasta_n50_below_minimum_count`, `fasta_record_count_above_maximum_count`, `fasta_ambiguous_bases_above_maximum_count`, `fasta_total_bases_below_minimum_count`, `fasta_longest_record_below_minimum_count`, `fragmented_fasta_signal_count`, `fasta_header_fragment_keyword_row_count`, `fasta_quality_gate_passed_row_count`, `fasta_quality_gate_blocked_row_count`, `fasta_quality_gate_blocker_counts`, `quality_gate_recommendation`, `quality_gate_recommendation_reasons`, `recommended_quality_gate_request_target`, `recommended_quality_gate_request`, `recommended_quality_gate_next_command`, `recommended_quality_gate_command`, `recommended_review_queue_request_target`, `recommended_review_queue_request`, `recommended_review_queue_next_command`, `recommended_execution_validation_request_target`, `recommended_execution_validation_request`, `recommended_execution_validation_next_command`, `recommended_execution_validation_command`, `bounded_smoke_next_action`, `bounded_smoke_next_action_reasons`, `status_counts`, `ready`, `blockers`, `execution_boundary`, `safe_for_unattended_download`, `downloads_triggered`, `providers_contacted`, `network_access`, `external_tools`, `manifest_mutated`, and `strict_scientific_deliverable`. `installable_genome_fasta_ready_count` counts rows with a uniquely selectable non-empty genome FASTA source and no enabled FASTA quality-gate blockers; `installable_genome_fasta_not_ready_count` counts the remaining selected rows, including missing, invalid, unsafe, empty, ambiguous, missing-FASTA, or quality-gate-blocked rows. `installable_genome_fasta_not_ready_reason_counts` maps those non-ready rows to controlled blocker-style reason counts such as missing ZIPs, invalid ZIPs, unsafe ZIP members, missing or empty genome FASTA, ambiguous install selection, and enabled FASTA quality-gate blockers. The ready and not-ready previews list at most five rows each using only record IDs, accessions, assembly metadata, row status, controlled not-ready reasons, controlled quality blocker codes, and fragmentation signal; they do not include ZIP paths, raw FASTA headers, or sequence text. `assembly_metadata_high_quality_*` fields separately summarize rows selected from high-quality assembly metadata, including how many remain locally FASTA-installable and how many are blocked by local FASTA quality gates; this prevents Complete Genome or Chromosome metadata from being mistaken for automatic local FASTA acceptance. The high-quality blocked preview lists at most five affected rows using only record IDs, accessions, assembly metadata, row status, and controlled blocker codes; it does not include raw FASTA headers or sequence text. The `recommended_quality_gate_*` fields are populated when the inspection observed fragmentation/header signals without the matching active gates; controllers can pass `recommended_quality_gate_request` to `commands render`, while `recommended_quality_gate_command` remains a compatibility argv list. The `recommended_review_queue_*` fields are populated only when `inspect --write` has written an isolated inspection directory with bounded rows, and point to the local `coverage-pipeline server-validation-result review-queue` handoff. The `recommended_execution_validation_*` fields are populated when ZIP outputs are missing and the sibling `bounded_download_smoke_commands.tsv` exists, and point to validation-only `download-smoke execute` without `--execute`; that handoff validates the pinned command manifest and does not download. `bounded_smoke_next_action` is a controlled AI/operator routing hint such as `rerun_with_fragmentation_quality_gates`, `review_high_quality_metadata_fasta_quality_blockers`, `review_or_rerun_bounded_download_outputs`, `prepare_bounded_download_smoke_plan`, or `review_bounded_smoke_outputs`; it does not authorize downloads, provider access, manifest mutation, installation, or strict deliverable changes. The default `quality_profile` is `fragmentation`, which enables fragmented FASTA and WGS/scaffold/contig header keyword blockers so obvious scaffold/contig/WGS-like outputs fail bounded-smoke readiness by default. `--quality-profile none` preserves compatibility/visibility-only behavior and may return `quality_gate_recommendation=rerun_with_fragmentation_quality_gates` when those signals are observed. `installable_genome_fasta_fragmentation_signal_counts` and `installable_genome_fasta_header_fragment_keyword_row_count` summarize fragmentation/header keyword signals only among rows that remain locally installable under the active quality gates. `fasta_n50_bases`, header keyword counts, and `fasta_fragmentation_signal` are descriptive fields (`single_record`, `multi_record_single_dominant`, `multi_record_fragmented`, or `not_evaluated`). Unsafe ZIP member paths are blocked by default as `unsafe_zip_member_paths`, with row status `zip_unsafe_members`; no FASTA member content is inspected for that row. A genome FASTA member with zero records or zero bases is blocked by default as `empty_genome_fasta_outputs`, with row status `genome_fasta_empty`. A bounded row with more than one genome FASTA member remains visible through `multiple_genome_fasta_members_count`; it is blocked by default only when the installer selection rule cannot uniquely choose the source member, reported as `genome_fasta_install_selection_ambiguous` with row status `genome_fasta_multiple_members`. Optional flags `--min-fasta-n50-bases`, `--max-fasta-record-count`, `--max-fasta-ambiguous-bases`, `--min-fasta-total-bases`, `--min-fasta-longest-record-bases`, `--block-fragmented-fasta`, and `--block-fasta-header-keywords` can add or repeat local bounded smoke blockers for obvious short, ambiguous-base-heavy, scaffold/contig, or WGS-style outputs, and the quality-gate passed/blocked row counts plus blocker-count map summarize those controlled row blockers. `--quality-profile fragmentation` is shorthand for enabling the fragmented FASTA and WGS/scaffold/contig header keyword blockers. These readiness blockers are local smoke acceptance criteria only; they do not change strict status, completion, or final genome usability. The inspection only checks local ZIP existence, ZIP validity, unsafe member paths, whether genome FASTA members are present, install-selection ambiguity, and bounded FASTA count statistics; it does not run `datasets`, extract ZIPs, write raw sequences, access the network, contact providers, mutate manifests, or create strict deliverables.
+- `bounded_download_smoke_inspection_summary.json`: read-only JSON summary for `download-smoke inspect`. Stable fields include `schema_version`, `command`, `source_download_plan_path`, `selected_row_count`, `zip_exists_count`, `zip_valid_count`, `unsafe_zip_member_count`, `genome_fasta_present_count`, `genome_fasta_member_count`, `genomic_named_fasta_member_count`, `genome_fasta_install_selection_status_counts`, `genome_fasta_install_selection_ambiguous_count`, `installable_genome_fasta_ready_count`, `installable_genome_fasta_not_ready_count`, `installable_genome_fasta_not_ready_reason_counts`, `installable_genome_fasta_ready_preview`, `installable_genome_fasta_ready_preview_truncated`, `installable_genome_fasta_not_ready_preview`, `installable_genome_fasta_not_ready_preview_truncated`, `assembly_metadata_high_quality_row_count`, `assembly_metadata_high_quality_installable_ready_count`, `assembly_metadata_high_quality_fasta_quality_blocked_count`, `assembly_metadata_high_quality_fasta_quality_blocker_counts`, `assembly_metadata_high_quality_fasta_quality_blocked_preview`, `assembly_metadata_high_quality_fasta_quality_blocked_preview_truncated`, `fasta_record_count`, `fasta_total_bases`, `fasta_longest_record_bases`, `fasta_max_n50_bases`, `fasta_ambiguous_bases`, `fasta_header_wgs_keyword_count`, `fasta_header_scaffold_keyword_count`, `fasta_header_contig_keyword_count`, `empty_genome_fasta_count`, `multiple_genome_fasta_members_count`, `fasta_fragmentation_signal_counts`, `installable_genome_fasta_fragmentation_signal_counts`, `installable_genome_fasta_header_fragment_keyword_row_count`, `min_fasta_n50_bases`, `max_fasta_record_count`, `max_fasta_ambiguous_bases`, `min_fasta_total_bases`, `min_fasta_longest_record_bases`, `quality_profile`, `block_fragmented_fasta`, `block_fasta_header_keywords`, `fasta_n50_below_minimum_count`, `fasta_record_count_above_maximum_count`, `fasta_ambiguous_bases_above_maximum_count`, `fasta_total_bases_below_minimum_count`, `fasta_longest_record_below_minimum_count`, `fragmented_fasta_signal_count`, `fasta_header_fragment_keyword_row_count`, `fasta_quality_gate_passed_row_count`, `fasta_quality_gate_blocked_row_count`, `fasta_quality_gate_blocker_counts`, `quality_gate_recommendation`, `quality_gate_recommendation_reasons`, `recommended_quality_gate_request_target`, `recommended_quality_gate_request`, `recommended_quality_gate_next_command`, `recommended_quality_gate_command`, `recommended_review_queue_request_target`, `recommended_review_queue_request`, `recommended_review_queue_next_command`, `recommended_execution_validation_request_target`, `recommended_execution_validation_request`, `recommended_execution_validation_next_command`, `recommended_execution_validation_command`, `bounded_smoke_next_action`, `bounded_smoke_next_action_reasons`, `status_counts`, `ready`, `blockers`, `execution_boundary`, `safe_for_unattended_download`, `downloads_triggered`, `providers_contacted`, `network_access`, `external_tools`, `manifest_mutated`, and `strict_scientific_deliverable`. `installable_genome_fasta_ready_count` counts rows with a uniquely selectable non-empty genome FASTA source and no enabled FASTA quality-gate blockers; `installable_genome_fasta_not_ready_count` counts the remaining selected rows, including missing, invalid, unsafe, empty, ambiguous, missing-FASTA, or quality-gate-blocked rows. `installable_genome_fasta_not_ready_reason_counts` maps those non-ready rows to controlled blocker-style reason counts such as missing ZIPs, invalid ZIPs, unsafe ZIP members, missing or empty genome FASTA, ambiguous install selection, and enabled FASTA quality-gate blockers. The ready and not-ready previews list at most five rows each using only record IDs, accessions, assembly metadata, row status, controlled not-ready reasons, controlled quality blocker codes, and fragmentation signal; they do not include ZIP paths, raw FASTA headers, or sequence text. `assembly_metadata_high_quality_*` fields separately summarize rows selected from high-quality assembly metadata, including how many remain locally FASTA-installable and how many are blocked by local FASTA quality gates; this prevents Complete Genome or Chromosome metadata from being mistaken for automatic local FASTA acceptance. The high-quality blocked preview lists at most five affected rows using only record IDs, accessions, assembly metadata, row status, and controlled blocker codes; it does not include raw FASTA headers or sequence text. The `recommended_quality_gate_*` fields are populated when the inspection observed fragmentation/header signals without the matching active gates; controllers can pass `recommended_quality_gate_request` to `commands render`, while `recommended_quality_gate_command` remains a compatibility argv list. The `recommended_review_queue_*` fields are populated only when `inspect --write` has written an isolated inspection directory with bounded rows, and point to the local `coverage-pipeline server-validation-result review-queue` handoff. The `recommended_execution_validation_*` fields are populated when ZIP outputs are missing and the sibling `bounded_download_smoke_commands.tsv` exists, and point to validation-only `download-smoke execute` without `--execute`; that handoff validates the pinned command manifest and does not download. `bounded_smoke_next_action` is a controlled AI/operator routing hint such as `rerun_with_fragmentation_quality_gates`, `review_high_quality_metadata_fasta_quality_blockers`, `review_or_rerun_bounded_download_outputs`, `prepare_bounded_download_smoke_plan`, or `review_bounded_smoke_outputs`; it does not authorize downloads, provider access, manifest mutation, installation, or strict deliverable changes. The default `quality_profile` is `fragmentation`, which enables fragmented FASTA and WGS/scaffold/contig header keyword blockers so obvious scaffold/contig/WGS-like outputs fail bounded-smoke readiness by default. It does not treat a small number of records with one dominant chromosome-scale record and no WGS/scaffold/contig header keywords as automatically fragmented; those rows are reported as `multi_record_single_dominant` for review visibility. `--quality-profile none` preserves compatibility/visibility-only behavior and may return `quality_gate_recommendation=rerun_with_fragmentation_quality_gates` when those signals are observed. `installable_genome_fasta_fragmentation_signal_counts` and `installable_genome_fasta_header_fragment_keyword_row_count` summarize fragmentation/header keyword signals only among rows that remain locally installable under the active quality gates. `fasta_n50_bases`, header keyword counts, and `fasta_fragmentation_signal` are descriptive fields (`single_record`, `multi_record_single_dominant`, `multi_record_fragmented`, or `not_evaluated`). Unsafe ZIP member paths are blocked by default as `unsafe_zip_member_paths`, with row status `zip_unsafe_members`; no FASTA member content is inspected for that row. A genome FASTA member with zero records or zero bases is blocked by default as `empty_genome_fasta_outputs`, with row status `genome_fasta_empty`. A bounded row with more than one genome FASTA member remains visible through `multiple_genome_fasta_members_count`; it is blocked by default only when the installer selection rule cannot uniquely choose the source member, reported as `genome_fasta_install_selection_ambiguous` with row status `genome_fasta_multiple_members`. Optional flags `--min-fasta-n50-bases`, `--max-fasta-record-count`, `--max-fasta-ambiguous-bases`, `--min-fasta-total-bases`, `--min-fasta-longest-record-bases`, `--block-fragmented-fasta`, and `--block-fasta-header-keywords` can add or repeat local bounded smoke blockers for obvious short, ambiguous-base-heavy, scaffold/contig, or WGS-style outputs, and the quality-gate passed/blocked row counts plus blocker-count map summarize those controlled row blockers. `--quality-profile fragmentation` is shorthand for enabling the fragmented FASTA and WGS/scaffold/contig header keyword blockers. These readiness blockers are local smoke acceptance criteria only; they do not change strict status, completion, or final genome usability. The inspection only checks local ZIP existence, ZIP validity, unsafe member paths, whether genome FASTA members are present, install-selection ambiguity, and bounded FASTA count statistics; it does not run `datasets`, extract ZIPs, write raw sequences, access the network, contact providers, mutate manifests, or create strict deliverables.
`--download-smoke-inspection-dir ` is an explicit read-only report/package surface for these two files. Report-only mode may show `## Bounded Download Smoke Inspection`; `package-results --include reports` and `--include all` may copy validated members under `download_smoke/` with `evidence_policy=download_smoke_inspection_audit` and `strict_scientific_deliverable=false`. Missing input is omitted; partial or malformed input copies only valid members and emits a compact warning. Failed-handoff packages exclude the inspection files.
- `download_smoke_quality_review_decisions.tsv`: optional local decision template written by `coverage-pipeline server-validation-result quality-review-template --write --out `. It copies `record_id` and `assembly_accession` from `download_smoke_review_queue_triage.tsv` and leaves `quality_review_decision`, `decision_reason_code`, `reviewer_id`, and `reviewed_at` empty. The template is not a completed review and is rejected by `quality-review` until every row is filled with controlled decision/reason, reviewer, and date values.
- `download_smoke_quality_review.tsv`, `download_smoke_quality_review_summary.json`, and `download_smoke_quality_review_diagnostics.tsv`: isolated audit triplet written by `coverage-pipeline server-validation-result quality-review --write --outdir `. The TSV records exact triage-to-decision linkage, controlled `quality_review_decision` and `decision_reason_code`, reviewer id, review date, and fixed false boundaries for final-use acceptance, strict upgrade, and manifest mutation. The summary repeats controlled decision/reason counts and fixed boundaries: `audit_only=true`, `accepted_for_final_use=false`, `strict_upgrade_applied=false`, `manifest_mutated=false`, `downloads_triggered=0`, `providers_contacted=0`, `network_access=false`, and `external_tools=false`. The diagnostics TSV uses controlled component/code/severity/accession fields only. `--download-smoke-quality-review-dir ` is the explicit report/package surface for this triplet. Report-only mode may show `## Bounded Download Smoke Quality Review`; `package-results --include reports` and `--include all` may copy validated members under `download_smoke/` with `evidence_policy=download_smoke_quality_review_audit` and `strict_scientific_deliverable=false`. Missing input is omitted; partial or malformed input copies only valid members and emits a compact warning. Failed-handoff packages exclude the quality-review files. A bounded-smoke quality acceptance is not final genome acceptance, not download authorization, and not strict deliverable promotion.
diff --git a/tests/test_cli_download_smoke.py b/tests/test_cli_download_smoke.py
index 50a0716..3659398 100644
--- a/tests/test_cli_download_smoke.py
+++ b/tests/test_cli_download_smoke.py
@@ -1986,6 +1986,76 @@ def test_download_smoke_inspect_optional_quality_gates_block_fragmented_fasta(
assert summary["providers_contacted"] == 0
+def test_download_smoke_inspect_fragmentation_profile_allows_complete_multireplicon(
+ capsys,
+ tmp_path,
+):
+ zip_path = tmp_path / "cache" / "ncbi" / "rec-1.zip"
+ plan = tmp_path / "bounded_download_smoke_plan.tsv"
+ _write_zip(
+ zip_path,
+ content=(
+ ">chromosome\n"
+ + "A" * 2897536
+ + "\n>plasmid_a\n"
+ + "C" * 800000
+ + "\n>plasmid_b\n"
+ + "G" * 576182
+ + "\n"
+ ),
+ )
+ _write_bounded_download_plan(
+ plan,
+ [
+ {
+ **_bounded_row(
+ "rec-1",
+ "GCF_000011805.1",
+ assembly_level="Complete Genome",
+ refseq_category="reference genome",
+ quality_tier="high",
+ ),
+ "datasets_zip_path": str(zip_path),
+ }
+ ],
+ )
+
+ assert (
+ main(
+ [
+ "download-smoke",
+ "inspect",
+ "--download-plan",
+ str(plan),
+ "--quality-profile",
+ "fragmentation",
+ ]
+ )
+ == 0
+ )
+
+ payload = json.loads(capsys.readouterr().out)
+ summary = payload["bounded_download_smoke_inspection_summary"]
+ assert payload["status"] == "pass"
+ assert summary["ready"] is True
+ assert summary["blockers"] == []
+ assert summary["installable_genome_fasta_ready_count"] == 1
+ assert summary["installable_genome_fasta_not_ready_reason_counts"] == {}
+ assert summary["fasta_record_count"] == 3
+ assert summary["fasta_total_bases"] == 4273718
+ assert summary["fasta_longest_record_bases"] == 2897536
+ assert summary["fasta_max_n50_bases"] == 2897536
+ assert summary["fasta_header_wgs_keyword_count"] == 0
+ assert summary["fasta_header_scaffold_keyword_count"] == 0
+ assert summary["fasta_header_contig_keyword_count"] == 0
+ assert summary["fasta_fragmentation_signal_counts"] == {
+ "multi_record_single_dominant": 1
+ }
+ assert summary["fasta_quality_gate_blocker_counts"] == {}
+ assert summary["assembly_metadata_high_quality_installable_ready_count"] == 1
+ assert summary["assembly_metadata_high_quality_fasta_quality_blocked_count"] == 0
+
+
def test_download_smoke_inspect_write_outputs_row_quality_gate_blockers(
capsys,
tmp_path,
diff --git a/typetreeflow/download_smoke_cli.py b/typetreeflow/download_smoke_cli.py
index 0865640..d545e49 100644
--- a/typetreeflow/download_smoke_cli.py
+++ b/typetreeflow/download_smoke_cli.py
@@ -1747,10 +1747,19 @@ def _classify_fasta_fragmentation(fasta_stats: dict[str, int]) -> str:
record_count = int(fasta_stats.get("fasta_record_count", 0))
total_bases = int(fasta_stats.get("fasta_total_bases", 0))
longest_record = int(fasta_stats.get("fasta_longest_record_bases", 0))
+ header_fragment_keyword_count = (
+ int(fasta_stats.get("fasta_header_wgs_keyword_count", 0))
+ + int(fasta_stats.get("fasta_header_scaffold_keyword_count", 0))
+ + int(fasta_stats.get("fasta_header_contig_keyword_count", 0))
+ )
if record_count <= 0 or total_bases <= 0:
return FASTA_FRAGMENTATION_SIGNAL_NOT_EVALUATED
if record_count == 1:
return FASTA_FRAGMENTATION_SIGNAL_SINGLE_RECORD
+ if header_fragment_keyword_count > 0:
+ return FASTA_FRAGMENTATION_SIGNAL_FRAGMENTED
+ if record_count <= 10 and longest_record * 2 >= total_bases:
+ return FASTA_FRAGMENTATION_SIGNAL_SINGLE_DOMINANT
if longest_record * 10 >= total_bases * 9:
return FASTA_FRAGMENTATION_SIGNAL_SINGLE_DOMINANT
return FASTA_FRAGMENTATION_SIGNAL_FRAGMENTED